Merge remote-tracking branch 'origin/tracking' into custom

This commit is contained in:
roytam1 2023-03-06 16:29:00 +08:00
commit eb8748c537
119 changed files with 3277 additions and 1496 deletions

View file

@ -25,10 +25,8 @@
<script>
"use strict";
var host = document.querySelector("#shadow");
if (host.createShadowRoot) {
var root = host.createShadowRoot();
root.innerHTML = "<h3>Shadow DOM</h3><select multiple></select>";
}
var root = host.attachShadow({ mode: "open" });
root.innerHTML = "<h3>Shadow DOM</h3><select multiple></select>";
</script>
</body>
</html>

View file

@ -21,8 +21,8 @@
// Set up a basic shadow DOM
var host = document.querySelector('#shadow');
if (host.createShadowRoot) {
var root = host.createShadowRoot();
if (host.attachShadow) {
let root = host.attachShadow({ mode: "open" });
root.innerHTML = '<h3>Shadow <em>DOM</em></h3><select multiple></select>';
}

View file

@ -6913,6 +6913,27 @@ exports.CSS_PROPERTIES = {
"unset"
]
},
"inset": {
"isInherited": false,
"subproperties": [
"top",
"right",
"bottom",
"left"
],
"supports": [
6,
8
],
"values": [
"-moz-calc",
"auto",
"calc",
"inherit",
"initial",
"unset"
]
},
"inset-block-end": {
"isInherited": false,
"subproperties": [

View file

@ -536,23 +536,5 @@ StyleChildrenIterator::IsNeeded(const Element* aElement)
return false;
}
nsIContent*
StyleChildrenIterator::GetNextChild()
{
while (nsIContent* child = AllChildrenIterator::GetNextChild()) {
if (IsNativeAnonymousImplementationOfPseudoElement(child)) {
// Skip any native-anonymous children that are used to implement pseudo-
// elements. These match pseudo-element selectors instead of being
// considered a child of their host, and thus the style system needs to
// handle them separately.
} else {
return child;
}
}
return nullptr;
}
} // namespace dom
} // namespace mozilla

View file

@ -7,6 +7,8 @@
#define ChildIterator_h
#include "nsIContent.h"
#include "nsIContentInlines.h"
#include <stdint.h>
/**
* Iterates over the children on a node. If a child is an insertion point,
@ -17,9 +19,6 @@
* binding's <xbl:content> element.
*/
#include <stdint.h>
#include "nsAutoPtr.h"
class nsIContent;
namespace mozilla {
@ -189,20 +188,33 @@ protected:
class AllChildrenIterator : private FlattenedChildIterator
{
public:
AllChildrenIterator(const nsIContent* aNode, uint32_t aFlags,
bool aStartAtBeginning = true) :
FlattenedChildIterator(aNode, aFlags, aStartAtBeginning),
mAnonKidsIdx(aStartAtBeginning ? UINT32_MAX : 0),
mFlags(aFlags), mPhase(aStartAtBeginning ? eAtBegin : eAtEnd) { }
AllChildrenIterator(const nsIContent* aNode,
uint32_t aFlags,
bool aStartAtBeginning = true)
: FlattenedChildIterator(aNode, aFlags, aStartAtBeginning)
, mAnonKidsIdx(aStartAtBeginning ? UINT32_MAX : 0)
, mFlags(aFlags), mPhase(aStartAtBeginning ? eAtBegin : eAtEnd)
{
}
AllChildrenIterator(AllChildrenIterator&& aOther)
: FlattenedChildIterator(Move(aOther)),
mAnonKids(Move(aOther.mAnonKids)), mAnonKidsIdx(aOther.mAnonKidsIdx),
mFlags(aOther.mFlags), mPhase(aOther.mPhase)
: FlattenedChildIterator(Move(aOther))
, mAnonKids(Move(aOther.mAnonKids))
, mAnonKidsIdx(aOther.mAnonKidsIdx)
, mFlags(aOther.mFlags)
, mPhase(aOther.mPhase)
#ifdef DEBUG
, mMutationGuard(aOther.mMutationGuard)
, mMutationGuard(aOther.mMutationGuard)
#endif
{}
{
}
AllChildrenIterator& operator=(AllChildrenIterator&& aOther)
{
this->~AllChildrenIterator();
new (this)AllChildrenIterator(Move(aOther));
return *this;
}
#ifdef DEBUG
~AllChildrenIterator() { MOZ_ASSERT(!mMutationGuard.Mutated(0)); }
@ -268,20 +280,43 @@ private:
*/
class StyleChildrenIterator : private AllChildrenIterator {
public:
explicit StyleChildrenIterator(const nsIContent* aContent)
StyleChildrenIterator(const nsIContent* aContent,
bool aStartAtBeginning = true)
: AllChildrenIterator(aContent,
nsIContent::eAllChildren |
nsIContent::eSkipDocumentLevelNativeAnonymousContent)
nsIContent::eSkipDocumentLevelNativeAnonymousContent,
aStartAtBeginning)
{
MOZ_COUNT_CTOR(StyleChildrenIterator);
}
StyleChildrenIterator(StyleChildrenIterator&& aOther)
: AllChildrenIterator(Move(aOther))
{
MOZ_COUNT_CTOR(StyleChildrenIterator);
}
StyleChildrenIterator& operator=(StyleChildrenIterator&& aOther)
{
AllChildrenIterator::operator=(Move(aOther));
return *this;
}
~StyleChildrenIterator() { MOZ_COUNT_DTOR(StyleChildrenIterator); }
nsIContent* GetNextChild();
static nsIContent* GetParent(const nsIContent& aContent)
{
nsINode* node = aContent.GetFlattenedTreeParentNodeForStyle();
return node && node->IsContent() ? node->AsContent() : nullptr;
}
// Returns true if we cannot find all the children we need to style by
// traversing the siblings of the first child.
static bool IsNeeded(const Element* aParent);
using AllChildrenIterator::GetNextChild;
using AllChildrenIterator::GetPreviousChild;
using AllChildrenIterator::Seek;
};
} // namespace dom

View file

@ -4,11 +4,14 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DocumentOrShadowRoot.h"
#include "mozilla/EventStateManager.h"
#include "mozilla/dom/StyleSheetList.h"
#include "nsDocument.h"
#include "nsFocusManager.h"
#include "ShadowRoot.h"
#include "XULDocument.h"
#include "nsLayoutUtils.h"
#include "nsSVGUtils.h"
class nsINode;
class nsIDocument;
@ -144,5 +147,158 @@ DocumentOrShadowRoot::GetRetargetedFocusedElement()
return nullptr;
}
Element*
DocumentOrShadowRoot::GetPointerLockElement()
{
nsCOMPtr<Element> pointerLockedElement =
do_QueryReferent(EventStateManager::sPointerLockedElement);
if (!pointerLockedElement) {
return nullptr;
}
nsIContent* retargetedPointerLockedElement = Retarget(pointerLockedElement);
return
retargetedPointerLockedElement && retargetedPointerLockedElement->IsElement() ?
retargetedPointerLockedElement->AsElement() : nullptr;
}
Element*
DocumentOrShadowRoot::GetFullscreenElement()
{
if (!AsNode().IsInComposedDoc()) {
return nullptr;
}
Element* element = AsNode().OwnerDoc()->FullScreenStackTop();
NS_ASSERTION(!element ||
element->State().HasState(NS_EVENT_STATE_FULL_SCREEN),
"Fullscreen element should have fullscreen styles applied");
nsIContent* retargeted = Retarget(element);
if (retargeted && retargeted->IsElement()) {
return retargeted->AsElement();
}
return nullptr;
}
Element*
DocumentOrShadowRoot::ElementFromPoint(float aX, float aY)
{
return ElementFromPointHelper(aX, aY, false, true);
}
void
DocumentOrShadowRoot::ElementsFromPoint(float aX, float aY,
nsTArray<RefPtr<Element>>& aElements)
{
ElementsFromPointHelper(aX, aY, nsIDocument::FLUSH_LAYOUT, aElements);
}
Element*
DocumentOrShadowRoot::ElementFromPointHelper(float aX, float aY,
bool aIgnoreRootScrollFrame,
bool aFlushLayout)
{
AutoTArray<RefPtr<Element>, 1> elementArray;
ElementsFromPointHelper(aX, aY,
((aIgnoreRootScrollFrame ? nsIDocument::IGNORE_ROOT_SCROLL_FRAME : 0) |
(aFlushLayout ? nsIDocument::FLUSH_LAYOUT : 0) |
nsIDocument::IS_ELEMENT_FROM_POINT),
elementArray);
if (elementArray.IsEmpty()) {
return nullptr;
}
return elementArray[0];
}
void
DocumentOrShadowRoot::ElementsFromPointHelper(float aX, float aY,
uint32_t aFlags,
nsTArray<RefPtr<mozilla::dom::Element>>& aElements)
{
// As per the the spec, we return null if either coord is negative
if (!(aFlags & nsIDocument::IGNORE_ROOT_SCROLL_FRAME) && (aX < 0 || aY < 0)) {
return;
}
nscoord x = nsPresContext::CSSPixelsToAppUnits(aX);
nscoord y = nsPresContext::CSSPixelsToAppUnits(aY);
nsPoint pt(x, y);
nsCOMPtr<nsIDocument> doc = AsNode().OwnerDoc();
// Make sure the layout information we get is up-to-date, and
// ensure we get a root frame (for everything but XUL)
if (aFlags & nsIDocument::FLUSH_LAYOUT) {
doc->FlushPendingNotifications(Flush_Layout);
}
nsIPresShell* ps = doc->GetShell();
if (!ps) {
return;
}
nsIFrame* rootFrame = ps->GetRootFrame();
// XUL docs, unlike HTML, have no frame tree until everything's done loading
if (!rootFrame) {
return; // return null to premature XUL callers as a reminder to wait
}
nsTArray<nsIFrame*> outFrames;
// Emulate what GetFrameAtPoint does, since we want all the frames under our
// point.
nsLayoutUtils::GetFramesForArea(rootFrame, nsRect(pt, nsSize(1, 1)), outFrames,
nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC |
((aFlags & nsIDocument::IGNORE_ROOT_SCROLL_FRAME) ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0));
// Dunno when this would ever happen, as we should at least have a root frame under us?
if (outFrames.IsEmpty()) {
return;
}
// Used to filter out repeated elements in sequence.
nsIContent* lastAdded = nullptr;
for (uint32_t i = 0; i < outFrames.Length(); i++) {
nsIContent* node = doc->GetContentInThisDocument(outFrames[i]);
if (!node || !node->IsElement()) {
// If this helper is called via ElementsFromPoint, we need to make sure
// our frame is an element. Otherwise return whatever the top frame is
// even if it isn't the top-painted element.
// SVG 'text' element's SVGTextFrame doesn't respond to hit-testing, so
// if 'node' is a child of such an element then we need to manually defer
// to the parent here.
if (!(aFlags & nsIDocument::IS_ELEMENT_FROM_POINT) &&
!outFrames[i]->IsSVGText()) {
continue;
}
node = node->GetParent();
if (node) {
ShadowRoot* shadow = ShadowRoot::FromNode(node);
if (shadow) {
node = shadow->Host();
}
}
}
//XXXsmaug There is plenty of unspec'ed behavior here
// https://github.com/w3c/webcomponents/issues/735
// https://github.com/w3c/webcomponents/issues/736
node = Retarget(node);
if (node && node != lastAdded) {
aElements.AppendElement(node->AsElement());
lastAdded = node;
// If this helper is called via ElementFromPoint, just return the first
// element we find.
if (aFlags & nsIDocument::IS_ELEMENT_FROM_POINT) {
return;
}
}
}
}
}
}

View file

@ -114,6 +114,32 @@ public:
~DocumentOrShadowRoot() = default;
Element* GetPointerLockElement();
Element* GetFullscreenElement();
Element* ElementFromPoint(float aX, float aY);
void ElementsFromPoint(float aX, float aY,
nsTArray<RefPtr<mozilla::dom::Element>>& aElements);
/**
* Helper for nsIDOMDocument::elementFromPoint implementation that allows
* ignoring the scroll frame and/or avoiding layout flushes.
*
* @see nsIDOMWindowUtils::elementFromPoint
*/
Element* ElementFromPointHelper(float aX, float aY,
bool aIgnoreRootScrollFrame,
bool aFlushLayout);
enum ElementsFromPointFlags
{
IGNORE_ROOT_SCROLL_FRAME = 1,
FLUSH_LAYOUT = 2,
IS_ELEMENT_FROM_POINT = 4
};
void ElementsFromPointHelper(float aX, float aY, uint32_t aFlags,
nsTArray<RefPtr<mozilla::dom::Element>>& aElements);
protected:
nsIContent* Retarget(nsIContent* aContent) const;

View file

@ -1126,18 +1126,6 @@ Element::AttachShadow(const ShadowRootInit& aInit, ErrorResult& aError)
return nullptr;
}
return AttachShadowInternal(aInit.mMode == ShadowRootMode::Closed, aError);
}
already_AddRefed<ShadowRoot>
Element::CreateShadowRoot(ErrorResult& aError)
{
return AttachShadowInternal(false, aError);
}
already_AddRefed<ShadowRoot>
Element::AttachShadowInternal(bool aClosed, ErrorResult& aError)
{
/**
* 3. If context object is a shadow host, then throw
* an "InvalidStateError" DOMException.
@ -1183,8 +1171,9 @@ Element::AttachShadowInternal(bool aClosed, ErrorResult& aError)
* context object???s node document, host is context object,
* and mode is init???s mode.
*/
bool isClosed = (aInit.mMode == ShadowRootMode::Closed);
RefPtr<ShadowRoot> shadowRoot =
new ShadowRoot(this, aClosed, nodeInfo.forget(), protoBinding);
new ShadowRoot(this, isClosed, nodeInfo.forget(), protoBinding);
shadowRoot->SetIsComposedDocParticipant(IsInComposedDoc());
@ -1725,10 +1714,11 @@ Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
if (!hadParent) {
uint32_t editableDescendantChange = EditableInclusiveDescendantCount(this);
if (editableDescendantChange != 0) {
// If we are binding a subtree root to the document, we need to update
// the editable descendant count of all the ancestors.
// If we are binding a subtree root to the document, we need to update
// the editable descendant count of all the ancestors. However, we don't
// cross the Shadow DOM boundary (expected behavior is unclear).
nsIContent* parent = GetParent();
while (parent) {
while (parent && parent->IsElement()) {
parent->ChangeEditableDescendantCount(editableDescendantChange);
parent = parent->GetParent();
}

View file

@ -470,9 +470,6 @@ protected:
mState &= ~aStates;
}
already_AddRefed<ShadowRoot> AttachShadowInternal(bool aClosed,
ErrorResult& aError);
private:
// Need to allow the ESM, nsGlobalWindow, and the focus manager to
// set our state
@ -930,9 +927,6 @@ public:
void SetSlot(const nsAString& aName, ErrorResult& aError);
void GetSlot(nsAString& aName);
// [deprecated] Shadow DOM v0
already_AddRefed<ShadowRoot> CreateShadowRoot(ErrorResult& aError);
ShadowRoot *FastGetShadowRoot() const
{
nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();

View file

@ -801,6 +801,22 @@ FindChromeAccessOnlySubtreeOwner(nsIContent* aContent)
return aContent;
}
already_AddRefed<nsINode>
FindChromeAccessOnlySubtreeOwner(EventTarget* aTarget)
{
nsCOMPtr<nsINode> node = do_QueryInterface(aTarget);
if (!node || !node->ChromeOnlyAccess()) {
return node.forget();
}
if (!node->IsContent()) {
return nullptr;
}
node = FindChromeAccessOnlySubtreeOwner(node->AsContent());
return node.forget();
}
nsresult
nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor)
{
@ -821,24 +837,11 @@ nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor)
// chrome access only subtree or if we are about to propagate out of
// a shadow root to a shadow root host.
((this == aVisitor.mEvent->mOriginalTarget &&
!ChromeOnlyAccess()) || isAnonForEvents || GetShadowRoot())) {
!ChromeOnlyAccess()) || isAnonForEvents)) {
nsCOMPtr<nsIContent> relatedTarget =
do_QueryInterface(aVisitor.mEvent->AsMouseEvent()->mRelatedTarget);
if (relatedTarget &&
relatedTarget->OwnerDoc() == OwnerDoc()) {
// In the web components case, we may need to stop propagation of events
// at shadow root host.
if (GetShadowRoot()) {
nsIContent* adjustedTarget =
Event::GetShadowRelatedTarget(this, relatedTarget);
if (this == adjustedTarget) {
aVisitor.SetParentTarget(nullptr, false);
aVisitor.mCanHandle = false;
return NS_OK;
}
}
// If current target is anonymous for events or we know that related
// target is descendant of an element which is anonymous for events,
// we may want to stop event propagation.
@ -954,6 +957,104 @@ nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor)
} else {
aVisitor.SetParentTarget(GetComposedDoc(), false);
}
if (!ChromeOnlyAccess() && !aVisitor.mRelatedTargetRetargetedInCurrentScope) {
// We don't support Shadow DOM in native anonymous content yet.
aVisitor.mRelatedTargetRetargetedInCurrentScope = true;
if (aVisitor.mEvent->mOriginalRelatedTarget) {
// https://dom.spec.whatwg.org/#concept-event-dispatch
// Step 3.
// "Let relatedTarget be the result of retargeting event's relatedTarget
// against target if event's relatedTarget is non-null, and null
// otherwise."
//
// This is a bit complicated because the event might be from native
// anonymous content, but we need to deal with non-native anonymous
// content there.
bool initialTarget = this == aVisitor.mEvent->mOriginalTarget;
nsCOMPtr<nsINode> originalTargetAsNode;
// Use of mOriginalTargetIsInAnon is an optimization here.
if (!initialTarget && aVisitor.mOriginalTargetIsInAnon) {
originalTargetAsNode =
FindChromeAccessOnlySubtreeOwner(aVisitor.mEvent->mOriginalTarget);
initialTarget = originalTargetAsNode == this;
}
if (initialTarget) {
nsCOMPtr<nsINode> relatedTargetAsNode =
FindChromeAccessOnlySubtreeOwner(aVisitor.mEvent->mOriginalRelatedTarget);
if (!originalTargetAsNode) {
originalTargetAsNode =
do_QueryInterface(aVisitor.mEvent->mOriginalTarget);
}
if (relatedTargetAsNode && originalTargetAsNode) {
nsINode* retargetedRelatedTarget =
nsContentUtils::Retarget(relatedTargetAsNode, originalTargetAsNode);
if (originalTargetAsNode == retargetedRelatedTarget &&
retargetedRelatedTarget != relatedTargetAsNode) {
// Step 4.
// "If target is relatedTarget and target is not event's
// relatedTarget, then return true."
aVisitor.IgnoreCurrentTargetBecauseOfShadowDOMRetargeting();
// Old code relies on mTarget to point to the first element which
// was not added to the event target chain because of mCanHandle
// being false, but in Shadow DOM case mTarget really should
// point to a node in Shadow DOM.
aVisitor.mEvent->mTarget = aVisitor.mTargetInKnownToBeHandledScope;
return NS_OK;
}
// Part of step 5. Retargeting target has happened already higher
// up in this method.
// "Append to an event path with event, target, targetOverride,
// relatedTarget, and false."
aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
}
} else {
nsCOMPtr<nsINode> relatedTargetAsNode =
FindChromeAccessOnlySubtreeOwner(aVisitor.mEvent->mOriginalRelatedTarget);
if (relatedTargetAsNode) {
// Step 11.3.
// "Let relatedTarget be the result of retargeting event's
// relatedTarget against parent if event's relatedTarget is non-null,
// and null otherwise.".
nsINode* retargetedRelatedTarget =
nsContentUtils::Retarget(relatedTargetAsNode, this);
nsCOMPtr<nsINode> targetInKnownToBeHandledScope =
FindChromeAccessOnlySubtreeOwner(aVisitor.mTargetInKnownToBeHandledScope);
if (nsContentUtils::ContentIsShadowIncludingDescendantOf(
this, targetInKnownToBeHandledScope->SubtreeRoot())) {
// Part of step 11.4.
// "If target's root is a shadow-including inclusive ancestor of
// parent, then"
// "...Append to an event path with event, parent, null, relatedTarget,
// " and slot-in-closed-tree."
aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
} else if (this == retargetedRelatedTarget) {
// Step 11.5
// "Otherwise, if parent and relatedTarget are identical, then set
// parent to null."
aVisitor.IgnoreCurrentTargetBecauseOfShadowDOMRetargeting();
// Old code relies on mTarget to point to the first element which
// was not added to the event target chain because of mCanHandle
// being false, but in Shadow DOM case mTarget really should
// point to a node in Shadow DOM.
aVisitor.mEvent->mTarget = aVisitor.mTargetInKnownToBeHandledScope;
return NS_OK;
} else {
// Step 11.6
aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
}
}
}
}
}
if (slot) {
// Inform that we're about to exit the current scope.
aVisitor.mRelatedTargetRetargetedInCurrentScope = false;
}
return NS_OK;
}

View file

@ -101,6 +101,23 @@ ShadowRoot::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
return mozilla::dom::ShadowRootBinding::Wrap(aCx, this, aGivenProto);
}
void
ShadowRoot::CloneInternalDataFrom(ShadowRoot* aOther)
{
size_t sheetCount = aOther->SheetCount();
for (size_t i = 0; i < sheetCount; ++i) {
StyleSheet* sheet = aOther->SheetAt(i);
if (sheet && sheet->IsApplicable()) {
// TODO: Remove AsGecko() call once Stylo is removed.
RefPtr<CSSStyleSheet> clonedSheet =
sheet->AsGecko()->Clone(nullptr, nullptr, nullptr, nullptr);
if (clonedSheet) {
AppendStyleSheet(*clonedSheet);
}
}
}
}
ShadowRoot*
ShadowRoot::FromNode(nsINode* aNode)
{
@ -311,6 +328,8 @@ ShadowRoot::GetEventTargetParent(EventChainPreVisitor& aVisitor)
{
aVisitor.mCanHandle = true;
aVisitor.mRootOfClosedTree = IsClosed();
// Inform that we're about to exit the current scope.
aVisitor.mRelatedTargetRetargetedInCurrentScope = false;
// https://dom.spec.whatwg.org/#ref-for-get-the-parent%E2%91%A6
if (!aVisitor.mEvent->mFlags.mComposed) {
@ -333,12 +352,10 @@ ShadowRoot::GetEventTargetParent(EventChainPreVisitor& aVisitor)
nsIContent* shadowHost = GetHost();
aVisitor.SetParentTarget(shadowHost, false);
if (aVisitor.mOriginalTargetIsInAnon) {
nsCOMPtr<nsIContent> content(do_QueryInterface(aVisitor.mEvent->mTarget));
if (content && content->GetBindingParent() == shadowHost) {
aVisitor.mEventTargetAtParent = shadowHost;
}
}
nsCOMPtr<nsIContent> content(do_QueryInterface(aVisitor.mEvent->mTarget));
if (content && content->GetBindingParent() == shadowHost) {
aVisitor.mEventTargetAtParent = shadowHost;
}
return NS_OK;
}
@ -488,25 +505,6 @@ ShadowRoot::Host()
return host->AsElement();
}
bool
ShadowRoot::ApplyAuthorStyles()
{
return mProtoBinding->InheritsStyle();
}
void
ShadowRoot::SetApplyAuthorStyles(bool aApplyAuthorStyles)
{
mProtoBinding->SetInheritsStyle(aApplyAuthorStyles);
nsIPresShell* shell = OwnerDoc()->GetShell();
if (shell) {
OwnerDoc()->BeginUpdate(UPDATE_STYLE);
shell->RecordShadowStyleChange(this);
OwnerDoc()->EndUpdate(UPDATE_STYLE);
}
}
void
ShadowRoot::AttributeChanged(nsIDocument* aDocument,
Element* aElement,

View file

@ -59,13 +59,16 @@ public:
// [deprecated] Shadow DOM v0
void InsertSheet(StyleSheet* aSheet, nsIContent* aLinkingContent);
void RemoveSheet(StyleSheet* aSheet);
bool ApplyAuthorStyles();
void SetApplyAuthorStyles(bool aApplyAuthorStyles);
StyleSheetList* StyleSheets()
{
return &DocumentOrShadowRoot::EnsureDOMStyleSheets();
}
/**
* Clones internal state, for example stylesheets, of aOther to 'this'.
*/
void CloneInternalDataFrom(ShadowRoot* aOther);
/**
* Distributes all the explicit children of the pool host to the content
* insertion points in this ShadowRoot.

View file

@ -5,8 +5,7 @@
<body>
<div id="host"></div>
<script>
var s = host.createShadowRoot();
s.innerHTML = '<input type="range" />';
host.attachShadow({ mode: "open" }).innerHTML = '<input type="range" />';
</script>
</body>
</html>

View file

@ -3,7 +3,7 @@
<body>
<script>
var x = document.createElement('span');
x.createShadowRoot();
x.attachShadow({ mode: "open" });
x.id = 'a';
</script>
</body>

View file

@ -1,33 +0,0 @@
<!DOCTYPE html>
<html>
<body>
<style>
#foo {
overflow: scroll;
height: 100px;
}
</style>
<div id="foo">
Mozilla Firefox<br>
Mozilla Firefox<br>
Mozilla Firefox<br>
Mozilla Firefox<br>
Mozilla Firefox<br>
Mozilla Firefox<br>
Mozilla Firefox<br>
Mozilla Firefox<br>
Mozilla Firefox<br>
Mozilla Firefox<br>
Mozilla Firefox<br>
Mozilla Firefox<br>
Mozilla Firefox<br>
Mozilla Firefox<br>
Mozilla Firefox<br>
Mozilla Firefox<br>
Mozilla Firefox<br>
Mozilla Firefox<br>
<script>
foo.createShadowRoot().innerHTML = "<content></content>";
</script>
</body>
</html>

View file

@ -0,0 +1,17 @@
<html>
<head>
<script>
function fun_0() {
document.implementation.createDocument('', '', null).adoptNode(o2);
}
o1 = document.createElement('map');
o2 = document.createElement('iframe');
document.documentElement.appendChild(o1);
document.documentElement.appendChild(o2);
o1.textContent = 'x';
document.addEventListener('DOMNodeRemoved', fun_0, false);
o1.innerText = 'x';
</script>
</head>
</html>

View file

@ -209,6 +209,7 @@ load 1230422.html
load 1251361.html
load 1304437.html
pref(clipboard.autocopy,true) load 1385272-1.html
load 1393806.html
pref(dom.webcomponents.enabled,true) load 1341693.html
pref(dom.webcomponents.enabled,true) load 1419799.html
pref(dom.webcomponents.enabled,false) load 1422931.html

View file

@ -2248,6 +2248,34 @@ nsContentUtils::ContentIsHostIncludingDescendantOf(
return false;
}
bool
nsContentUtils::ContentIsShadowIncludingDescendantOf(
const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor)
{
MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
if (aPossibleAncestor == aPossibleDescendant->GetComposedDoc()) {
return true;
}
do {
if (aPossibleDescendant == aPossibleAncestor) {
return true;
}
if (aPossibleDescendant->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
ShadowRoot* shadowRoot =
ShadowRoot::FromNode(const_cast<nsINode*>(aPossibleDescendant));
aPossibleDescendant = shadowRoot ? shadowRoot->GetHost() : nullptr;
} else {
aPossibleDescendant = aPossibleDescendant->GetParentNode();
}
} while (aPossibleDescendant);
return false;
}
// static
bool
nsContentUtils::ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
@ -2266,6 +2294,30 @@ nsContentUtils::ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
return false;
}
// static
nsINode*
nsContentUtils::Retarget(nsINode* aTargetA, nsINode* aTargetB)
{
while (true && aTargetA) {
// If A's root is not a shadow root...
nsINode* root = aTargetA->SubtreeRoot();
// TODO: replace with IsShadowRoot() check once bug 1427511 lands.
if (!ShadowRoot::FromNode(root)) {
// ...then return A.
return aTargetA;
}
// or A's root is a shadow-including inclusive ancestor of B...
if (nsContentUtils::ContentIsShadowIncludingDescendantOf(aTargetB, root)) {
// ...then return A.
return aTargetA;
}
aTargetA = ShadowRoot::FromNode(root)->GetHost();
}
return nullptr;
}
// static
nsresult
@ -6384,9 +6436,7 @@ nsContentUtils::IsFocusedContent(const nsIContent* aContent)
bool
nsContentUtils::IsSubDocumentTabbable(nsIContent* aContent)
{
//XXXsmaug Shadow DOM spec issue!
// We may need to change this to GetComposedDoc().
nsIDocument* doc = aContent->GetUncomposedDoc();
nsIDocument* doc = aContent->GetComposedDoc();
if (!doc) {
return false;
}

View file

@ -279,6 +279,13 @@ public:
static bool ContentIsHostIncludingDescendantOf(
const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor);
/**
* Similar to above, but does special case only ShadowRoot,
* not HTMLTemplateElement.
*/
static bool ContentIsShadowIncludingDescendantOf(
const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor);
/**
* Similar to ContentIsDescendantOf except it crosses document boundaries,
* this function uses ancestor/descendant relations in the composed document
@ -287,6 +294,13 @@ public:
static bool ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
nsINode* aPossibleAncestor);
/**
* Retarget an object A against an object B
* @see https://dom.spec.whatwg.org/#retarget
*/
static nsINode*
Retarget(nsINode* aTargetA, nsINode* aTargetB);
/*
* This method fills the |aArray| with all ancestor nodes of |aNode|
* including |aNode| at the zero index.

View file

@ -3120,106 +3120,6 @@ nsDocument::ElementFromPoint(float aX, float aY, nsIDOMElement** aReturn)
return NS_OK;
}
Element*
nsIDocument::ElementFromPoint(float aX, float aY)
{
return ElementFromPointHelper(aX, aY, false, true);
}
void
nsIDocument::ElementsFromPoint(float aX, float aY,
nsTArray<RefPtr<Element>>& aElements)
{
ElementsFromPointHelper(aX, aY, nsIDocument::FLUSH_LAYOUT, aElements);
}
Element*
nsDocument::ElementFromPointHelper(float aX, float aY,
bool aIgnoreRootScrollFrame,
bool aFlushLayout)
{
AutoTArray<RefPtr<Element>, 1> elementArray;
ElementsFromPointHelper(aX, aY,
((aIgnoreRootScrollFrame ? nsIDocument::IGNORE_ROOT_SCROLL_FRAME : 0) |
(aFlushLayout ? nsIDocument::FLUSH_LAYOUT : 0) |
nsIDocument::IS_ELEMENT_FROM_POINT),
elementArray);
if (elementArray.IsEmpty()) {
return nullptr;
}
return elementArray[0];
}
void
nsDocument::ElementsFromPointHelper(float aX, float aY,
uint32_t aFlags,
nsTArray<RefPtr<mozilla::dom::Element>>& aElements)
{
// As per the the spec, we return null if either coord is negative
if (!(aFlags & nsIDocument::IGNORE_ROOT_SCROLL_FRAME) && (aX < 0 || aY < 0)) {
return;
}
nscoord x = nsPresContext::CSSPixelsToAppUnits(aX);
nscoord y = nsPresContext::CSSPixelsToAppUnits(aY);
nsPoint pt(x, y);
// Make sure the layout information we get is up-to-date, and
// ensure we get a root frame (for everything but XUL)
if (aFlags & nsIDocument::FLUSH_LAYOUT) {
FlushPendingNotifications(Flush_Layout);
}
nsIPresShell *ps = GetShell();
if (!ps) {
return;
}
nsIFrame *rootFrame = ps->GetRootFrame();
// XUL docs, unlike HTML, have no frame tree until everything's done loading
if (!rootFrame) {
return; // return null to premature XUL callers as a reminder to wait
}
nsTArray<nsIFrame*> outFrames;
// Emulate what GetFrameAtPoint does, since we want all the frames under our
// point.
nsLayoutUtils::GetFramesForArea(rootFrame, nsRect(pt, nsSize(1, 1)), outFrames,
nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC |
((aFlags & nsIDocument::IGNORE_ROOT_SCROLL_FRAME) ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0));
// Dunno when this would ever happen, as we should at least have a root frame under us?
if (outFrames.IsEmpty()) {
return;
}
// Used to filter out repeated elements in sequence.
nsIContent* lastAdded = nullptr;
for (uint32_t i = 0; i < outFrames.Length(); i++) {
nsIContent* node = GetContentInThisDocument(outFrames[i]);
if (!node || !node->IsElement()) {
// If this helper is called via ElementsFromPoint, we need to make sure
// our frame is an element. Otherwise return whatever the top frame is
// even if it isn't the top-painted element.
if (!(aFlags & nsIDocument::IS_ELEMENT_FROM_POINT)) {
continue;
}
node = node->GetParent();
}
if (node && node != lastAdded) {
aElements.AppendElement(node->AsElement());
lastAdded = node;
// If this helper is called via ElementFromPoint, just return the first
// element we find.
if (aFlags & nsIDocument::IS_ELEMENT_FROM_POINT) {
return;
}
}
}
}
nsresult
nsDocument::NodesFromRectHelper(float aX, float aY,
float aTopSize, float aRightSize,
@ -5923,14 +5823,9 @@ nsIDocument::ImportNode(nsINode& aNode, bool aDeep, ErrorResult& rv) const
case nsIDOMNode::COMMENT_NODE:
case nsIDOMNode::DOCUMENT_TYPE_NODE:
{
nsCOMPtr<nsINode> newNode;
nsCOMArray<nsINode> nodesWithProperties;
rv = nsNodeUtils::Clone(imported, aDeep, mNodeInfoManager,
nodesWithProperties, getter_AddRefs(newNode));
if (rv.Failed()) {
return nullptr;
}
return newNode.forget();
return nsNodeUtils::Clone(imported, aDeep, mNodeInfoManager,
&nodesWithProperties, rv);
}
default:
{
@ -7096,8 +6991,8 @@ nsIDocument::AdoptNode(nsINode& aAdoptedNode, ErrorResult& rv)
}
nsCOMArray<nsINode> nodesWithProperties;
rv = nsNodeUtils::Adopt(adoptedNode, sameDocument ? nullptr : mNodeInfoManager,
newScope, nodesWithProperties);
nsNodeUtils::Adopt(adoptedNode, sameDocument ? nullptr : mNodeInfoManager,
newScope, nodesWithProperties, rv);
if (rv.Failed()) {
// Disconnect all nodes from their parents, since some have the old document
// as their ownerDocument and some have this as their ownerDocument.
@ -8392,7 +8287,7 @@ nsDocument::DoUnblockOnload()
}
nsIContent*
nsDocument::GetContentInThisDocument(nsIFrame* aFrame) const
nsIDocument::GetContentInThisDocument(nsIFrame* aFrame) const
{
for (nsIFrame* f = aFrame; f;
f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
@ -8603,7 +8498,7 @@ nsDocument::OnPageHide(bool aPersisted,
EnumerateActivityObservers(NotifyActivityChanged, nullptr);
ClearPendingFullscreenRequests(this);
if (GetFullscreenElement()) {
if (FullScreenStackTop()) {
// If this document was fullscreen, we should exit fullscreen in this
// doctree branch. This ensures that if the user navigates while in
// fullscreen mode we don't leave its still visible ancestor documents
@ -10307,7 +10202,7 @@ nsIDocument::AsyncExitFullscreen(nsIDocument* aDoc)
static bool
CountFullscreenSubDocuments(nsIDocument* aDoc, void* aData)
{
if (aDoc->GetFullscreenElement()) {
if (aDoc->FullScreenStackTop()) {
uint32_t* count = static_cast<uint32_t*>(aData);
(*count)++;
}
@ -10327,7 +10222,7 @@ nsDocument::IsFullscreenLeaf()
{
// A fullscreen leaf document is fullscreen, and has no fullscreen
// subdocuments.
if (!GetFullscreenElement()) {
if (!FullScreenStackTop()) {
return false;
}
return CountFullscreenSubDocuments(this) == 0;
@ -10336,11 +10231,11 @@ nsDocument::IsFullscreenLeaf()
static bool
ResetFullScreen(nsIDocument* aDocument, void* aData)
{
if (aDocument->GetFullscreenElement()) {
if (aDocument->FullScreenStackTop()) {
NS_ASSERTION(CountFullscreenSubDocuments(aDocument) <= 1,
"Should have at most 1 fullscreen subdocument.");
static_cast<nsDocument*>(aDocument)->CleanupFullscreenState();
NS_ASSERTION(!aDocument->GetFullscreenElement(),
NS_ASSERTION(!aDocument->FullScreenStackTop(),
"Should reset full-screen");
auto changed = reinterpret_cast<nsCOMArray<nsIDocument>*>(aData);
changed->AppendElement(aDocument);
@ -10388,7 +10283,7 @@ nsIDocument::ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc)
UnlockPointer();
nsCOMPtr<nsIDocument> root = aMaybeNotARootDoc->GetFullscreenRoot();
if (!root || !root->GetFullscreenElement()) {
if (!root || !root->FullScreenStackTop()) {
// If a document was detached before exiting from fullscreen, it is
// possible that the root had left fullscreen state. In this case,
// we would not get anything from the ResetFullScreen() call. Root's
@ -10417,7 +10312,7 @@ nsIDocument::ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc)
DispatchFullScreenChange(changed[changed.Length() - i - 1]);
}
NS_ASSERTION(!root->GetFullscreenElement(),
NS_ASSERTION(!root->FullScreenStackTop(),
"Fullscreen root should no longer be a fullscreen doc...");
// Move the top-level window out of fullscreen mode.
@ -10434,7 +10329,7 @@ GetFullscreenLeaf(nsIDocument* aDoc, void* aData)
nsIDocument** result = static_cast<nsIDocument**>(aData);
*result = aDoc;
return false;
} else if (aDoc->GetFullscreenElement()) {
} else if (aDoc->FullScreenStackTop()) {
aDoc->EnumerateSubDocuments(GetFullscreenLeaf, aData);
}
return true;
@ -10453,7 +10348,7 @@ GetFullscreenLeaf(nsIDocument* aDoc)
nsIDocument* root = nsContentUtils::GetRootDocument(aDoc);
// Check that the root is actually fullscreen so we don't waste time walking
// around its descendants.
if (!root->GetFullscreenElement()) {
if (!root->FullScreenStackTop()) {
return nullptr;
}
GetFullscreenLeaf(root, &leaf);
@ -10463,10 +10358,10 @@ GetFullscreenLeaf(nsIDocument* aDoc)
void
nsDocument::RestorePreviousFullScreenState()
{
NS_ASSERTION(!GetFullscreenElement() || !FullscreenRoots::IsEmpty(),
NS_ASSERTION(!FullScreenStackTop() || !FullscreenRoots::IsEmpty(),
"Should have at least 1 fullscreen root when fullscreen!");
if (!GetFullscreenElement() || !GetWindow() || FullscreenRoots::IsEmpty()) {
if (!FullScreenStackTop() || !GetWindow() || FullscreenRoots::IsEmpty()) {
return;
}
@ -10641,7 +10536,7 @@ nsDocument::FullScreenStackPush(Element* aElement)
}
EventStateManager::SetFullScreenState(aElement, true);
mFullScreenStack.AppendElement(do_GetWeakReference(aElement));
NS_ASSERTION(GetFullscreenElement() == aElement, "Should match");
NS_ASSERTION(FullScreenStackTop() == aElement, "Should match");
UpdateViewportScrollbarOverrideForFullscreen(this);
return true;
}
@ -10689,7 +10584,7 @@ nsDocument::FullScreenStackTop()
uint32_t last = mFullScreenStack.Length() - 1;
nsCOMPtr<Element> element(do_QueryReferent(mFullScreenStack[last]));
NS_ASSERTION(element, "Should have full-screen element!");
NS_ASSERTION(element->IsInUncomposedDoc(), "Full-screen element should be in doc");
NS_ASSERTION(element->IsInComposedDoc(), "Full-screen element should be in doc");
NS_ASSERTION(element->OwnerDoc() == this, "Full-screen element should be in this doc");
return element;
}
@ -10767,7 +10662,7 @@ nsresult nsDocument::RemoteFrameFullscreenReverted()
}
/* static */ bool
nsDocument::IsUnprefixedFullscreenEnabled(JSContext* aCx, JSObject* aObject)
nsIDocument::IsUnprefixedFullscreenEnabled(JSContext* aCx, JSObject* aObject)
{
MOZ_ASSERT(NS_IsMainThread());
return nsContentUtils::IsCallerChrome() ||
@ -10815,10 +10710,10 @@ nsDocument::FullscreenElementReadyCheck(Element* aElement,
{
NS_ASSERTION(aElement,
"Must pass non-null element to nsDocument::RequestFullScreen");
if (!aElement || aElement == GetFullscreenElement()) {
if (!aElement || aElement == FullScreenStackTop()) {
return false;
}
if (!aElement->IsInUncomposedDoc()) {
if (!aElement->IsInComposedDoc()) {
DispatchFullscreenError("FullscreenDeniedNotInDocument");
return false;
}
@ -10842,8 +10737,11 @@ nsDocument::FullscreenElementReadyCheck(Element* aElement,
DispatchFullscreenError("FullscreenDeniedSubDocFullScreen");
return false;
}
if (GetFullscreenElement() &&
!nsContentUtils::ContentIsDescendantOf(aElement, GetFullscreenElement())) {
//XXXsmaug Note, we don't follow the latest fullscreen spec here.
// This whole check could be probably removed.
if (FullScreenStackTop() &&
!nsContentUtils::ContentIsHostIncludingDescendantOf(aElement,
FullScreenStackTop())) {
// If this document is full-screen, only grant full-screen requests from
// a descendant of the current full-screen element.
DispatchFullscreenError("FullscreenDeniedNotDescendant");
@ -11203,16 +11101,6 @@ nsDocument::GetMozFullScreenElement(nsIDOMElement **aFullScreenElement)
return NS_OK;
}
Element*
nsDocument::GetFullscreenElement()
{
Element* element = FullScreenStackTop();
NS_ASSERTION(!element ||
element->State().HasState(NS_EVENT_STATE_FULL_SCREEN),
"Fullscreen element should have fullscreen styles applied");
return element;
}
NS_IMETHODIMP
nsDocument::GetMozFullScreen(bool *aFullScreen)
{
@ -11331,7 +11219,7 @@ GetPointerLockError(Element* aElement, Element* aCurrentLock,
return "PointerLockDeniedInUse";
}
if (!aElement->IsInUncomposedDoc()) {
if (!aElement->IsInComposedDoc()) {
return "PointerLockDeniedNotInDocument";
}
@ -11382,11 +11270,11 @@ ChangePointerLockedElement(Element* aElement, nsIDocument* aDocument,
MOZ_ASSERT(aDocument);
MOZ_ASSERT(aElement != aPointerLockedElement);
if (aPointerLockedElement) {
MOZ_ASSERT(aPointerLockedElement->GetUncomposedDoc() == aDocument);
MOZ_ASSERT(aPointerLockedElement->GetComposedDoc() == aDocument);
aPointerLockedElement->ClearPointerLock();
}
if (aElement) {
MOZ_ASSERT(aElement->GetUncomposedDoc() == aDocument);
MOZ_ASSERT(aElement->GetComposedDoc() == aDocument);
aElement->SetPointerLock();
EventStateManager::sPointerLockedElement = do_GetWeakReference(aElement);
EventStateManager::sPointerLockedDoc = do_GetWeakReference(aDocument);
@ -11410,9 +11298,9 @@ PointerLockRequest::Run()
nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
nsDocument* d = static_cast<nsDocument*>(doc.get());
const char* error = nullptr;
if (!e || !d || !e->GetUncomposedDoc()) {
if (!e || !d || !e->GetComposedDoc()) {
error = "PointerLockDeniedNotInDocument";
} else if (e->GetUncomposedDoc() != d) {
} else if (e->GetComposedDoc() != d) {
error = "PointerLockDeniedMovedDocument";
}
if (!error) {
@ -11578,25 +11466,6 @@ nsDocument::GetMozPointerLockElement(nsIDOMElement** aPointerLockedElement)
return NS_OK;
}
Element*
nsIDocument::GetPointerLockElement()
{
nsCOMPtr<Element> pointerLockedElement =
do_QueryReferent(EventStateManager::sPointerLockedElement);
if (!pointerLockedElement) {
return nullptr;
}
// Make sure pointer locked element is in the same document.
nsCOMPtr<nsIDocument> pointerLockedDoc =
do_QueryReferent(EventStateManager::sPointerLockedDoc);
if (pointerLockedDoc != this) {
return nullptr;
}
return pointerLockedElement;
}
nsresult
nsDocument::Observe(nsISupports *aSubject,
const char *aTopic,

View file

@ -692,14 +692,6 @@ public:
nsIAtom* aAttrName,
const nsAString& aAttrValue) const override;
virtual Element* ElementFromPointHelper(float aX, float aY,
bool aIgnoreRootScrollFrame,
bool aFlushLayout) override;
virtual void ElementsFromPointHelper(float aX, float aY,
uint32_t aFlags,
nsTArray<RefPtr<mozilla::dom::Element>>& aElements) override;
virtual nsresult NodesFromRectHelper(float aX, float aY,
float aTopSize, float aRightSize,
float aBottomSize, float aLeftSize,
@ -896,8 +888,6 @@ public:
//
already_AddRefed<nsSimpleContentList> BlockedTrackingNodes() const;
static bool IsUnprefixedFullscreenEnabled(JSContext* aCx, JSObject* aObject);
// Do the "fullscreen element ready check" from the fullscreen spec.
// It returns true if the given element is allowed to go into fullscreen.
bool FullscreenElementReadyCheck(Element* aElement, bool aWasCallerChrome);
@ -921,11 +911,10 @@ public:
void FullScreenStackPop();
// Returns the top element from the full-screen stack.
Element* FullScreenStackTop();
Element* FullScreenStackTop() override;
// DOM-exposed fullscreen API
bool FullscreenEnabled() override;
Element* GetFullscreenElement() override;
void RequestPointerLock(Element* aElement) override;
bool SetPointerLock(Element* aElement, int aCursorStyle);
@ -1315,16 +1304,6 @@ private:
nsresult InitCSP(nsIChannel* aChannel);
/**
* Find the (non-anonymous) content in this document for aFrame. It will
* be aFrame's content node if that content is in this document and not
* anonymous. Otherwise, when aFrame is in a subdocument, we use the frame
* element containing the subdocument containing aFrame, and/or find the
* nearest non-anonymous ancestor in this document.
* Returns null if there is no such element.
*/
nsIContent* GetContentInThisDocument(nsIFrame* aFrame) const;
// Just like EnableStyleSheetsForSet, but doesn't check whether
// aSheetSet is null and allows the caller to control whether to set
// aSheetSet as the preferred set in the CSSLoader.

View file

@ -8,10 +8,12 @@
#include "nsFocusManager.h"
#include "AccessibleCaretEventHub.h"
#include "ChildIterator.h"
#include "nsAttrValueInlines.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsGkAtoms.h"
#include "nsContentUtils.h"
#include "nsIDocument.h"
#include "nsDocument.h"
#include "nsIEditor.h"
#include "nsPIDOMWindow.h"
#include "nsIDOMChromeWindow.h"
@ -46,7 +48,9 @@
#include "mozilla/ContentEvents.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/ShadowRoot.h"
#include "mozilla/dom/HTMLInputElement.h"
#include "mozilla/dom/HTMLSlotElement.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/EventStateManager.h"
#include "mozilla/EventStates.h"
@ -2978,6 +2982,378 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindowOuter* aWindow,
return NS_OK;
}
static bool
IsHostOrSlot(nsIContent* aContent)
{
return aContent && (aContent->GetShadowRoot() ||
aContent->IsHTMLElement(nsGkAtoms::slot));
}
// Helper class to iterate contents in scope by traversing flattened tree
// in tree order
class MOZ_STACK_CLASS ScopedContentTraversal
{
public:
ScopedContentTraversal(nsIContent* aStartContent, nsIContent* aOwner)
: mCurrent(aStartContent)
, mOwner(aOwner)
{
MOZ_ASSERT(aStartContent);
}
void Next();
void Prev();
void Reset()
{
SetCurrent(mOwner);
}
nsIContent* GetCurrent()
{
return mCurrent;
}
private:
void SetCurrent(nsIContent* aContent)
{
mCurrent = aContent;
}
nsIContent* mCurrent;
nsIContent* mOwner;
};
void
ScopedContentTraversal::Next()
{
MOZ_ASSERT(mCurrent);
// Get mCurrent's first child if it's in the same scope.
if (!IsHostOrSlot(mCurrent) || mCurrent == mOwner) {
StyleChildrenIterator iter(mCurrent);
nsIContent* child = iter.GetNextChild();
if (child) {
SetCurrent(child);
return;
}
}
// If mOwner has no children, END traversal
if (mCurrent == mOwner) {
SetCurrent(nullptr);
return;
}
nsIContent* current = mCurrent;
while (1) {
// Create parent's iterator and move to current
nsIContent* parent = current->GetFlattenedTreeParent();
StyleChildrenIterator parentIter(parent);
parentIter.Seek(current);
// Get next sibling of current
if (nsIContent* next = parentIter.GetNextChild()) {
SetCurrent(next);
return;
}
// If no next sibling and parent is mOwner, END traversal
if (parent == mOwner) {
SetCurrent(nullptr);
return;
}
current = parent;
}
}
void
ScopedContentTraversal::Prev()
{
MOZ_ASSERT(mCurrent);
nsIContent* parent;
nsIContent* last;
if (mCurrent == mOwner) {
// Get last child of mOwner
StyleChildrenIterator ownerIter(mOwner, false /* aStartAtBeginning */);
last = ownerIter.GetPreviousChild();
parent = last;
} else {
// Create parent's iterator and move to mCurrent
parent = mCurrent->GetFlattenedTreeParent();
StyleChildrenIterator parentIter(parent);
parentIter.Seek(mCurrent);
// Get previous sibling
last = parentIter.GetPreviousChild();
}
while (last) {
parent = last;
if (parent->GetShadowRoot() ||
parent->IsHTMLElement(nsGkAtoms::slot)) {
// Skip contents in other scopes
break;
}
// Find last child
StyleChildrenIterator iter(parent, false /* aStartAtBeginning */);
last = iter.GetPreviousChild();
}
// If parent is mOwner and no previous sibling remains, END traversal
SetCurrent(parent == mOwner ? nullptr : parent);
}
nsIContent*
nsFocusManager::FindOwner(nsIContent* aContent)
{
nsIContent* currentContent = aContent;
while (currentContent) {
nsIContent* parent = currentContent->GetFlattenedTreeParent();
// Shadow host / Slot
if (IsHostOrSlot(parent)) {
return parent;
}
currentContent = parent;
}
return nullptr;
}
/**
* Host and Slot elements need to be handled as if they had tabindex 0 even
* when they don't have the attribute. This is a helper method to get the
* right value for focus navigation. If aIsFocusable is passed, it is set to
* true if the element itself is focusable.
*/
static int32_t
HostOrSlotTabIndexValue(nsIContent* aContent,
bool* aIsFocusable = nullptr)
{
MOZ_ASSERT(IsHostOrSlot(aContent));
if (aIsFocusable) {
*aIsFocusable = false;
nsIFrame* frame = aContent->GetPrimaryFrame();
if (frame) {
int32_t tabIndex;
frame->IsFocusable(&tabIndex, 0);
*aIsFocusable = tabIndex >= 0;
}
}
const nsAttrValue* attrVal =
aContent->AsElement()->GetParsedAttr(nsGkAtoms::tabindex);
if (!attrVal) {
return 0;
}
if (attrVal->Type() == nsAttrValue::eInteger) {
return attrVal->GetIntegerValue();
}
return -1;
}
nsIContent*
nsFocusManager::GetNextTabbableContentInScope(nsIContent* aOwner,
nsIContent* aStartContent,
nsIContent* aOriginalStartContent,
bool aForward,
int32_t aCurrentTabIndex,
bool aIgnoreTabIndex,
bool aForDocumentNavigation,
bool aSkipOwner)
{
MOZ_ASSERT(IsHostOrSlot(aOwner), "Scope owner should be host or slot");
if (!aSkipOwner && (aForward && aOwner == aStartContent)) {
int32_t tabIndex = -1;
nsIFrame* frame = aOwner->GetPrimaryFrame();
if (frame && frame->IsFocusable(&tabIndex, false) && tabIndex >= 0) {
return aOwner;
}
}
//
// Iterate contents in scope
//
ScopedContentTraversal contentTraversal(aStartContent, aOwner);
nsCOMPtr<nsIContent> iterContent;
nsIContent* firstNonChromeOnly = aStartContent->IsInNativeAnonymousSubtree() ?
aStartContent->FindFirstNonChromeOnlyAccessContent() : nullptr;
while (1) {
// Iterate tab index to find corresponding contents in scope
while (1) {
// Iterate remaining contents in scope to find next content to focus
// Get next content
aForward ? contentTraversal.Next() : contentTraversal.Prev();
iterContent = contentTraversal.GetCurrent();
if (firstNonChromeOnly && firstNonChromeOnly == iterContent) {
// We just broke out from the native anonymous content, so move
// to the previous/next node of the native anonymous owner.
if (aForward) {
contentTraversal.Next();
} else {
contentTraversal.Prev();
}
iterContent = contentTraversal.GetCurrent();
}
if (!iterContent) {
// Reach the end
break;
}
// Get the tab index of the next element. For NAC we rely on frames.
//XXXsmaug we should probably use frames also for Shadow DOM and special
// case only display:contents elements.
int32_t tabIndex = 0;
if (iterContent->IsInNativeAnonymousSubtree() &&
iterContent->GetPrimaryFrame()) {
iterContent->GetPrimaryFrame()->IsFocusable(&tabIndex);
} else if (IsHostOrSlot(iterContent)) {
tabIndex = HostOrSlotTabIndexValue(iterContent);
} else {
iterContent->IsFocusable(&tabIndex);
}
if (tabIndex < 0 || !(aIgnoreTabIndex || tabIndex == aCurrentTabIndex)) {
continue;
}
if (!IsHostOrSlot(iterContent)) {
nsCOMPtr<nsIContent> elementInFrame;
bool checkSubDocument = true;
if (aForDocumentNavigation &&
TryDocumentNavigation(iterContent, &checkSubDocument,
getter_AddRefs(elementInFrame))) {
return elementInFrame;
}
if (!checkSubDocument) {
continue;
}
if (TryToMoveFocusToSubDocument(iterContent, aOriginalStartContent,
aForward, aForDocumentNavigation,
getter_AddRefs(elementInFrame))) {
return elementInFrame;
}
// Found content to focus
return iterContent;
}
// Search in scope owned by iterContent
nsIContent* contentToFocus =
GetNextTabbableContentInScope(iterContent, iterContent,
aOriginalStartContent, aForward,
aForward ? 1 : 0, aIgnoreTabIndex,
aForDocumentNavigation,
false /* aSkipOwner */);
if (contentToFocus) {
return contentToFocus;
}
};
// If already at lowest priority tab (0), end search completely.
// A bit counterintuitive but true, tabindex order goes 1, 2, ... 32767, 0
if (aCurrentTabIndex == (aForward ? 0 : 1)) {
break;
}
// Continue looking for next highest priority tabindex
aCurrentTabIndex = GetNextTabIndex(aOwner, aCurrentTabIndex, aForward);
contentTraversal.Reset();
}
// Return scope owner at last for backward navigation if its tabindex
// is non-negative
if (!aSkipOwner && !aForward) {
int32_t tabIndex = -1;
nsIFrame* frame = aOwner->GetPrimaryFrame();
if (frame && frame->IsFocusable(&tabIndex, false) && tabIndex >= 0) {
return aOwner;
}
}
return nullptr;
}
nsIContent*
nsFocusManager::GetNextTabbableContentInAncestorScopes(
nsIContent* aStartOwner,
nsIContent** aStartContent,
nsIContent* aOriginalStartContent,
bool aForward,
int32_t* aCurrentTabIndex,
bool aIgnoreTabIndex,
bool aForDocumentNavigation)
{
MOZ_ASSERT(aStartOwner == FindOwner(*aStartContent),
"aStartOwner should be the scope owner of aStartContent");
MOZ_ASSERT(IsHostOrSlot(aStartOwner), "scope owner should be host or slot");
nsIContent* owner = aStartOwner;
nsIContent* startContent = *aStartContent;
while (IsHostOrSlot(owner)) {
int32_t tabIndex = 0;
if (IsHostOrSlot(startContent)) {
tabIndex = HostOrSlotTabIndexValue(startContent);
} else {
startContent->IsFocusable(&tabIndex);
}
nsIContent* contentToFocus =
GetNextTabbableContentInScope(owner, startContent, aOriginalStartContent,
aForward, tabIndex, aIgnoreTabIndex,
aForDocumentNavigation,
false /* aSkipOwner */);
if (contentToFocus) {
return contentToFocus;
}
startContent = owner;
owner = FindOwner(startContent);
}
// If not found in shadow DOM, search from the top level shadow host in light DOM
*aStartContent = startContent;
*aCurrentTabIndex = HostOrSlotTabIndexValue(startContent);
return nullptr;
}
static nsIContent*
GetTopLevelScopeOwner(nsIContent* aContent)
{
nsIContent* topLevelScopeOwner = nullptr;
while (aContent) {
if (HTMLSlotElement* slot = aContent->GetAssignedSlot()) {
aContent = slot;
} else if (ShadowRoot* shadowRoot = aContent->GetContainingShadow()) {
aContent = shadowRoot->Host();
topLevelScopeOwner = aContent;
} else {
// TODO: replaced with FromNode.
if (HTMLSlotElement::FromContentOrNull(aContent)) {
topLevelScopeOwner = aContent;
}
aContent = aContent->GetParent();
}
}
return topLevelScopeOwner;
}
nsresult
nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
nsIContent* aRootContent,
@ -2995,70 +3371,163 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
if (!startContent)
return NS_OK;
nsIContent* currentTopLevelScopeOwner = GetTopLevelScopeOwner(aStartContent);
LOGCONTENTNAVIGATION("GetNextTabbable: %s", aStartContent);
LOGFOCUSNAVIGATION((" tabindex: %d", aCurrentTabIndex));
if (nsDocument::IsWebComponentsEnabled(aRootContent)) {
// If aStartContent is a shadow host or slot in forward navigation,
// search in scope owned by aStartContent.
if (aForward && IsHostOrSlot(aStartContent)) {
nsIContent* contentToFocus =
GetNextTabbableContentInScope(aStartContent, aStartContent,
aOriginalStartContent, aForward,
aForward ? 1 : 0, aIgnoreTabIndex,
aForDocumentNavigation,
true /* aSkipOwner */);
if (contentToFocus) {
NS_ADDREF(*aResultContent = contentToFocus);
return NS_OK;
}
}
// If aStartContent is in a scope owned by Shadow DOM search from scope
// including aStartContent.
if (nsIContent* owner = FindOwner(aStartContent)) {
nsIContent* contentToFocus =
GetNextTabbableContentInAncestorScopes(owner,
&aStartContent,
aOriginalStartContent,
aForward,
&aCurrentTabIndex,
aIgnoreTabIndex,
aForDocumentNavigation);
if (contentToFocus) {
NS_ADDREF(*aResultContent = contentToFocus);
return NS_OK;
}
}
// If we reach here, it means no next tabbable content in shadow DOM.
// We need to continue searching in light DOM, starting at the top level
// shadow host in light DOM (updated aStartContent) and its tabindex
// (updated aCurrentTabIndex).
MOZ_ASSERT(!FindOwner(aStartContent),
"aStartContent should not be owned by Shadow DOM at this point");
}
nsPresContext* presContext = aPresShell->GetPresContext();
bool getNextFrame = true;
nsCOMPtr<nsIContent> iterStartContent = aStartContent;
// Iterate tab index to find corresponding contents
while (1) {
nsIFrame* startFrame = iterStartContent->GetPrimaryFrame();
nsIFrame* frame = iterStartContent->GetPrimaryFrame();
// if there is no frame, look for another content node that has a frame
if (!startFrame) {
while (!frame) {
// if the root content doesn't have a frame, just return
if (iterStartContent == aRootContent)
if (iterStartContent == aRootContent) {
return NS_OK;
}
// look for the next or previous content node in tree order
iterStartContent = aForward ? iterStartContent->GetNextNode() : iterStartContent->GetPreviousContent();
iterStartContent = aForward ? iterStartContent->GetNextNode()
: iterStartContent->GetPreviousContent();
if (!iterStartContent) {
break;
}
frame = iterStartContent->GetPrimaryFrame();
// Host without frame, enter its scope.
if (nsDocument::IsWebComponentsEnabled(aRootContent) &&
(!frame && iterStartContent->GetShadowRoot())) {
int32_t tabIndex = HostOrSlotTabIndexValue(iterStartContent);
if (tabIndex >= 0 &&
(aIgnoreTabIndex || aCurrentTabIndex == tabIndex)) {
nsIContent* contentToFocus = GetNextTabbableContentInScope(
iterStartContent, iterStartContent, aOriginalStartContent,
aForward, aForward ? 1 : 0, aIgnoreTabIndex,
aForDocumentNavigation, true /* aSkipOwner */);
if (contentToFocus) {
NS_ADDREF(*aResultContent = contentToFocus);
return NS_OK;
}
}
}
// we've already skipped over the initial focused content, so we
// don't want to traverse frames.
getNextFrame = false;
if (iterStartContent)
continue;
// otherwise, as a last attempt, just look at the root content
iterStartContent = aRootContent;
continue;
}
// For tab navigation, pass false for aSkipPopupChecks so that we don't
// iterate into or out of a popup. For document naviation pass true to
// ignore these boundaries.
nsCOMPtr<nsIFrameEnumerator> frameTraversal;
nsresult rv = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
presContext, startFrame,
ePreOrder,
false, // aVisual
false, // aLockInScrollView
true, // aFollowOOFs
aForDocumentNavigation // aSkipPopupChecks
);
NS_ENSURE_SUCCESS(rv, rv);
if (frame) {
// For tab navigation, pass false for aSkipPopupChecks so that we don't
// iterate into or out of a popup. For document navigation, pass true to
// ignore these boundaries.
nsresult rv = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
presContext,
frame,
ePreOrder,
false, // aVisual
false, // aLockInScrollView
true, // aFollowOOFs
aForDocumentNavigation // aSkipPopupChecks
);
NS_ENSURE_SUCCESS(rv, rv);
if (iterStartContent == aRootContent) {
if (!aForward) {
frameTraversal->Last();
} else if (aRootContent->IsFocusable()) {
frameTraversal->Next();
if (iterStartContent == aRootContent) {
if (!aForward) {
frameTraversal->Last();
} else if (aRootContent->IsFocusable()) {
frameTraversal->Next();
}
frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
} else if (getNextFrame &&
(!iterStartContent ||
!iterStartContent->IsHTMLElement(nsGkAtoms::area))) {
// Need to do special check in case we're in an imagemap which has multiple
// content nodes per frame, so don't skip over the starting frame.
if (aForward) {
frameTraversal->Next();
} else {
frameTraversal->Prev();
}
frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
}
}
else if (getNextFrame &&
(!iterStartContent ||
!iterStartContent->IsHTMLElement(nsGkAtoms::area))) {
// Need to do special check in case we're in an imagemap which has multiple
// content nodes per frame, so don't skip over the starting frame.
if (aForward)
frameTraversal->Next();
else
frameTraversal->Prev();
}
// Walk frames to find something tabbable matching mCurrentTabIndex
nsIFrame* frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
while (frame) {
nsIContent* currentContent = frame->GetContent();
if (nsDocument::IsWebComponentsEnabled(currentContent)) {
// Try to find the topmost scope owner, since we want to skip the node
// that is not owned by document in frame traversal.
nsIContent* oldTopLevelScopeOwner = currentTopLevelScopeOwner;
if (!aForward || oldTopLevelScopeOwner != currentContent) {
currentTopLevelScopeOwner = GetTopLevelScopeOwner(currentContent);
} else {
currentTopLevelScopeOwner = currentContent;
}
if (currentTopLevelScopeOwner) {
if (currentTopLevelScopeOwner == oldTopLevelScopeOwner) {
// We're within non-document scope, continue.
do {
if (aForward) {
frameTraversal->Next();
} else {
frameTraversal->Prev();
}
frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
// For the usage of GetPrevContinuation, see the comment
// at the end of while (frame) loop.
} while (frame && frame->GetPrevContinuation());
continue;
}
currentContent = currentTopLevelScopeOwner;
}
}
// For document navigation, check if this element is an open panel. Since
// panels aren't focusable (tabIndex would be -1), we'll just assume that
@ -3092,7 +3561,7 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
// and root content, so that we only find content within the panel.
// Note also that we pass false for aForDocumentNavigation since we
// want to locate the first content, not the first document.
rv = GetNextTabbableContent(aPresShell, currentContent,
nsresult rv = GetNextTabbableContent(aPresShell, currentContent,
nullptr, currentContent,
true, 1, false, false,
aResultContent);
@ -3103,6 +3572,34 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
}
}
// As of now, 2018/04/12, sequential focus navigation is still
// in the obsolete Shadow DOM specification.
// http://w3c.github.io/webcomponents/spec/shadow/#sequential-focus-navigation
// "if ELEMENT is focusable, a shadow host, or a slot element,
// append ELEMENT to NAVIGATION-ORDER."
// and later in "For each element ELEMENT in NAVIGATION-ORDER: "
// hosts and slots are handled before other elements.
if (nsDocument::IsWebComponentsEnabled(currentContent) &&
IsHostOrSlot(currentContent)) {
bool focusableHostSlot;
int32_t tabIndex =
HostOrSlotTabIndexValue(currentContent, &focusableHostSlot);
// Host or slot itself isn't focusable or going backwards, enter its scope.
if ((!aForward || !focusableHostSlot) && tabIndex >= 0 &&
(aIgnoreTabIndex || aCurrentTabIndex == tabIndex)) {
nsIContent* contentToFocus =
GetNextTabbableContentInScope(currentContent, currentContent,
aOriginalStartContent, aForward,
aForward ? 1 : 0, aIgnoreTabIndex,
aForDocumentNavigation,
true /* aSkipOwner */);
if (contentToFocus) {
NS_ADDREF(*aResultContent = contentToFocus);
return NS_OK;
}
}
}
// TabIndex not set defaults to 0 for form elements, anchors and other
// elements that are normally focusable. Tabindex defaults to -1
// for elements that are not normally focusable.
@ -3151,54 +3648,22 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
// Next, for document navigation, check if this a non-remote child document.
bool checkSubDocument = true;
if (aForDocumentNavigation) {
nsIContent* docRoot = GetRootForChildDocument(currentContent);
if (docRoot) {
// If GetRootForChildDocument returned something then call
// FocusFirst to find the root or first element to focus within
// the child document. If this is a frameset though, skip this and
// fall through to the checkSubDocument block below to iterate into
// the frameset's frames and locate the first focusable frame.
if (!docRoot->IsHTMLElement(nsGkAtoms::frameset)) {
return FocusFirst(docRoot, aResultContent);
}
} else {
// Set checkSubDocument to false, as this was neither a frame
// type element or a child document that was focusable.
checkSubDocument = false;
}
if (aForDocumentNavigation &&
TryDocumentNavigation(currentContent, &checkSubDocument,
aResultContent)) {
return NS_OK;
}
if (checkSubDocument) {
// found a node with a matching tab index. Check if it is a child
// frame. If so, navigate into the child frame instead.
nsIDocument* doc = currentContent->GetComposedDoc();
NS_ASSERTION(doc, "content not in document");
nsIDocument* subdoc = doc->GetSubDocumentFor(currentContent);
if (subdoc && !subdoc->EventHandlingSuppressed()) {
if (aForward) {
// when tabbing forward into a frame, return the root
// frame so that the canvas becomes focused.
nsCOMPtr<nsPIDOMWindowOuter> subframe = subdoc->GetWindow();
if (subframe) {
*aResultContent = GetRootForFocus(subframe, subdoc, false, true);
if (*aResultContent) {
NS_ADDREF(*aResultContent);
return NS_OK;
}
}
}
Element* rootElement = subdoc->GetRootElement();
nsIPresShell* subShell = subdoc->GetShell();
if (rootElement && subShell) {
rv = GetNextTabbableContent(subShell, rootElement,
aOriginalStartContent, rootElement,
aForward, (aForward ? 1 : 0),
false, aForDocumentNavigation, aResultContent);
NS_ENSURE_SUCCESS(rv, rv);
if (*aResultContent)
return NS_OK;
}
if (TryToMoveFocusToSubDocument(currentContent,
aOriginalStartContent,
aForward,
aForDocumentNavigation,
aResultContent)) {
MOZ_ASSERT(*aResultContent);
return NS_OK;
}
// otherwise, use this as the next content node to tab to, unless
// this was the element we started on. This would happen for
@ -3269,6 +3734,77 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
return NS_OK;
}
bool
nsFocusManager::TryDocumentNavigation(nsIContent* aCurrentContent,
bool* aCheckSubDocument,
nsIContent** aResultContent)
{
*aCheckSubDocument = true;
nsIContent* docRoot = GetRootForChildDocument(aCurrentContent);
if (docRoot) {
// If GetRootForChildDocument returned something then call
// FocusFirst to find the root or first element to focus within
// the child document. If this is a frameset though, skip this and
// fall through to normal tab navigation to iterate into
// the frameset's frames and locate the first focusable frame.
if (!docRoot->IsHTMLElement(nsGkAtoms::frameset)) {
*aCheckSubDocument = false;
Unused << FocusFirst(docRoot, aResultContent);
return *aResultContent != nullptr;
}
} else {
// Set aCheckSubDocument to false, as this was neither a frame
// type element or a child document that was focusable.
*aCheckSubDocument = false;
}
return false;
}
bool
nsFocusManager::TryToMoveFocusToSubDocument(nsIContent* aCurrentContent,
nsIContent* aOriginalStartContent,
bool aForward,
bool aForDocumentNavigation,
nsIContent** aResultContent)
{
nsIDocument* doc = aCurrentContent->GetComposedDoc();
NS_ASSERTION(doc, "content not in document");
nsIDocument* subdoc = doc->GetSubDocumentFor(aCurrentContent);
if (subdoc && !subdoc->EventHandlingSuppressed()) {
if (aForward) {
// when tabbing forward into a frame, return the root
// frame so that the canvas becomes focused.
nsCOMPtr<nsPIDOMWindowOuter> subframe = subdoc->GetWindow();
if (subframe) {
*aResultContent = GetRootForFocus(subframe, subdoc, false, true);
if (*aResultContent) {
NS_ADDREF(*aResultContent);
return true;
}
}
}
Element* rootElement = subdoc->GetRootElement();
nsIPresShell* subShell = subdoc->GetShell();
if (rootElement && subShell) {
nsresult rv = GetNextTabbableContent(subShell,
rootElement,
aOriginalStartContent,
rootElement,
aForward,
(aForward ? 1 : 0),
false,
aForDocumentNavigation,
aResultContent);
NS_ENSURE_SUCCESS(rv, false);
if (*aResultContent) {
return true;
}
}
}
return false;
}
nsIContent*
nsFocusManager::GetNextTabbableMapArea(bool aForward,
int32_t aCurrentTabIndex,
@ -3315,15 +3851,20 @@ nsFocusManager::GetNextTabIndex(nsIContent* aParent,
bool aForward)
{
int32_t tabIndex, childTabIndex;
StyleChildrenIterator iter(aParent);
if (aForward) {
tabIndex = 0;
for (nsIContent* child = aParent->GetFirstChild();
for (nsIContent* child = iter.GetNextChild();
child;
child = child->GetNextSibling()) {
childTabIndex = GetNextTabIndex(child, aCurrentTabIndex, aForward);
if (childTabIndex > aCurrentTabIndex && childTabIndex != tabIndex) {
tabIndex = (tabIndex == 0 || childTabIndex < tabIndex) ? childTabIndex : tabIndex;
child = iter.GetNextChild()) {
// Skip child's descendants if child is a shadow host, as they are
// in the focus navigation scope owned by child's shadow root
if (!(nsDocument::IsWebComponentsEnabled(aParent) && IsHostOrSlot(child))) {
childTabIndex = GetNextTabIndex(child, aCurrentTabIndex, aForward);
if (childTabIndex > aCurrentTabIndex && childTabIndex != tabIndex) {
tabIndex = (tabIndex == 0 || childTabIndex < tabIndex) ? childTabIndex : tabIndex;
}
}
nsAutoString tabIndexStr;
@ -3337,13 +3878,17 @@ nsFocusManager::GetNextTabIndex(nsIContent* aParent,
}
else { /* !aForward */
tabIndex = 1;
for (nsIContent* child = aParent->GetFirstChild();
for (nsIContent* child = iter.GetNextChild();
child;
child = child->GetNextSibling()) {
childTabIndex = GetNextTabIndex(child, aCurrentTabIndex, aForward);
if ((aCurrentTabIndex == 0 && childTabIndex > tabIndex) ||
(childTabIndex < aCurrentTabIndex && childTabIndex > tabIndex)) {
tabIndex = childTabIndex;
child = iter.GetNextChild()) {
// Skip child's descendants if child is a shadow host, as they are
// in the focus navigation scope owned by child's shadow root
if (!(nsDocument::IsWebComponentsEnabled(aParent) && IsHostOrSlot(child))) {
childTabIndex = GetNextTabIndex(child, aCurrentTabIndex, aForward);
if ((aCurrentTabIndex == 0 && childTabIndex > tabIndex) ||
(childTabIndex < aCurrentTabIndex && childTabIndex > tabIndex)) {
tabIndex = childTabIndex;
}
}
nsAutoString tabIndexStr;

View file

@ -384,6 +384,91 @@ protected:
int32_t aType, bool aNoParentTraversal,
nsIContent** aNextContent);
/**
* Returns scope owner of aContent.
* A scope owner is either a document root, shadow host, or slot.
*/
nsIContent* FindOwner(nsIContent* aContent);
/**
* Retrieve the next tabbable element in scope owned by aOwner, using
* focusability and tabindex to determine the tab order.
*
* aOwner is the owner of scope to search in.
*
* aStartContent is the starting point for this call of this method.
*
* aOriginalStartContent is the initial starting point for sequential
* navigation.
*
* aForward should be true for forward navigation or false for backward
* navigation.
*
* aCurrentTabIndex is the current tabindex.
*
* aIgnoreTabIndex to ignore the current tabindex and find the element
* irrespective or the tab index.
*
* aForDocumentNavigation informs whether we're navigating only through
* documents.
*
* aSkipOwner to skip owner while searching. The flag is set when caller is
* |GetNextTabbableContent| in order to let caller handle owner.
*
* NOTE:
* Consider the method searches downwards in flattened subtree
* rooted at aOwner.
*/
nsIContent* GetNextTabbableContentInScope(nsIContent* aOwner,
nsIContent* aStartContent,
nsIContent* aOriginalStartContent,
bool aForward,
int32_t aCurrentTabIndex,
bool aIgnoreTabIndex,
bool aForDocumentNavigation,
bool aSkipOwner);
/**
* Retrieve the next tabbable element in scope including aStartContent
* and the scope's ancestor scopes, using focusability and tabindex to
* determine the tab order.
*
* aStartOwner is the scope owner of the aStartContent.
*
* aStartContent an in/out paremeter. It as input is the starting point
* for this call of this method; as output it is the shadow host in
* light DOM if the next tabbable element is not found in shadow DOM,
* in order to continue searching in light DOM.
*
* aOriginalStartContent is the initial starting point for sequential
* navigation.
*
* aForward should be true for forward navigation or false for backward
* navigation.
*
* aCurrentTabIndex returns tab index of shadow host in light DOM if the
* next tabbable element is not found in shadow DOM, in order to continue
* searching in light DOM.
*
* aIgnoreTabIndex to ignore the current tabindex and find the element
* irrespective or the tab index.
*
* aForDocumentNavigation informs whether we're navigating only through
* documents.
*
* NOTE:
* Consider the method searches upwards in all shadow host- or slot-rooted
* flattened subtrees that contains aStartContent as non-root, except
* the flattened subtree rooted at shadow host in light DOM.
*/
nsIContent* GetNextTabbableContentInAncestorScopes(nsIContent* aStartOwner,
nsIContent** aStartContent,
nsIContent* aOriginalStartContent,
bool aForward,
int32_t* aCurrentTabIndex,
bool aIgnoreTabIndex,
bool aForDocumentNavigation);
/**
* Retrieve the next tabbable element within a document, using focusability
* and tabindex to determine the tab order. The element is returned in
@ -503,6 +588,16 @@ private:
void SetFocusedWindowInternal(nsPIDOMWindowOuter* aWindow);
bool TryDocumentNavigation(nsIContent* aCurrentContent,
bool* aCheckSubDocument,
nsIContent** aResultContent);
bool TryToMoveFocusToSubDocument(nsIContent* aCurrentContent,
nsIContent* aOriginalStartContent,
bool aForward,
bool aForDocumentNavigation,
nsIContent** aResultContent);
// the currently active and front-most top-most window
nsCOMPtr<nsPIDOMWindowOuter> mActiveWindow;

View file

@ -1811,26 +1811,6 @@ public:
nsIAtom* aAttrName,
const nsAString& aAttrValue) const = 0;
/**
* Helper for nsIDOMDocument::elementFromPoint implementation that allows
* ignoring the scroll frame and/or avoiding layout flushes.
*
* @see nsIDOMWindowUtils::elementFromPoint
*/
virtual Element* ElementFromPointHelper(float aX, float aY,
bool aIgnoreRootScrollFrame,
bool aFlushLayout) = 0;
enum ElementsFromPointFlags {
IGNORE_ROOT_SCROLL_FRAME = 1,
FLUSH_LAYOUT = 2,
IS_ELEMENT_FROM_POINT = 4
};
virtual void ElementsFromPointHelper(float aX, float aY,
uint32_t aFlags,
nsTArray<RefPtr<mozilla::dom::Element>>& aElements) = 0;
virtual nsresult NodesFromRectHelper(float aX, float aY,
float aTopSize, float aRightSize,
float aBottomSize, float aLeftSize,
@ -2637,17 +2617,17 @@ public:
nsIURI* GetDocumentURIObject() const;
// Not const because all the full-screen goop is not const
virtual bool FullscreenEnabled() = 0;
virtual Element* GetFullscreenElement() = 0;
virtual Element* FullScreenStackTop() = 0;
bool Fullscreen()
{
return !!GetFullscreenElement();
}
void ExitFullscreen();
Element* GetPointerLockElement();
void ExitPointerLock()
{
UnlockPointer(this);
}
static bool IsUnprefixedFullscreenEnabled(JSContext* aCx, JSObject* aObject);
#ifdef MOZILLA_INTERNAL_API
bool Hidden() const
{
@ -2664,10 +2644,6 @@ public:
void GetPreferredStyleSheetSet(nsAString& aSheetSet);
virtual mozilla::dom::DOMStringList* StyleSheetSets() = 0;
virtual void EnableStyleSheetsForSet(const nsAString& aSheetSet) = 0;
Element* ElementFromPoint(float aX, float aY);
void ElementsFromPoint(float aX,
float aY,
nsTArray<RefPtr<mozilla::dom::Element>>& aElements);
/**
* Retrieve the location of the caret position (DOM node and character
@ -2835,6 +2811,16 @@ public:
bool ModuleScriptsEnabled();
/**
* Find the (non-anonymous) content in this document for aFrame. It will
* be aFrame's content node if that content is in this document and not
* anonymous. Otherwise, when aFrame is in a subdocument, we use the frame
* element containing the subdocument containing aFrame, and/or find the
* nearest non-anonymous ancestor in this document.
* Returns null if there is no such element.
*/
nsIContent* GetContentInThisDocument(nsIFrame* aFrame) const;
virtual void AddResizeObserver(mozilla::dom::ResizeObserver* aResizeObserver) = 0;
virtual void ScheduleResizeObserversNotification() const = 0;

View file

@ -273,6 +273,18 @@ nsINode* nsINode::GetRootNode(const GetRootNodeOptions& aOptions)
return SubtreeRoot();
}
// TODO: was marked as constant.
nsINode*
nsINode::GetParentOrHostNode()
{
if (mParent) {
return mParent;
}
ShadowRoot* shadowRoot = ShadowRoot::FromNode(this);
return shadowRoot ? shadowRoot->GetHost() : nullptr;
}
nsINode*
nsINode::SubtreeRoot() const
{
@ -1543,32 +1555,25 @@ nsINode::SetExplicitBaseURI(nsIURI* aURI)
return rv;
}
static nsresult
AdoptNodeIntoOwnerDoc(nsINode *aParent, nsINode *aNode)
static void
AdoptNodeIntoOwnerDoc(nsINode *aParent, nsINode *aNode, ErrorResult& aError)
{
NS_ASSERTION(!aNode->GetParentNode(),
"Should have removed from parent already");
nsIDocument *doc = aParent->OwnerDoc();
nsresult rv;
nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc, &rv);
NS_ENSURE_SUCCESS(rv, rv);
DebugOnly<nsINode*> adoptedNode = doc->AdoptNode(*aNode, aError);
nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMNode> adoptedNode;
rv = domDoc->AdoptNode(node, getter_AddRefs(adoptedNode));
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(aParent->OwnerDoc() == doc,
"ownerDoc chainged while adopting");
NS_ASSERTION(adoptedNode == node, "Uh, adopt node changed nodes?");
NS_ASSERTION(aParent->OwnerDoc() == aNode->OwnerDoc(),
#ifdef DEBUG
if (!aError.Failed()) {
MOZ_ASSERT(aParent->OwnerDoc() == doc,
"ownerDoc changed while adopting");
MOZ_ASSERT(adoptedNode == aNode, "Uh, adopt node changed nodes?");
MOZ_ASSERT(aParent->OwnerDoc() == aNode->OwnerDoc(),
"ownerDocument changed again after adopting!");
return NS_OK;
}
#endif // DEBUG
}
static nsresult
@ -1593,19 +1598,20 @@ ReparentWrappersInSubtree(nsIContent* aRoot)
rootedGlobal = xpc::GetXBLScope(cx, rootedGlobal);
nsresult rv;
ErrorResult rv;
JS::Rooted<JSObject*> reflector(cx);
for (nsIContent* cur = aRoot; cur; cur = cur->GetNextNode(aRoot)) {
if ((reflector = cur->GetWrapper())) {
JSAutoCompartment ac(cx, reflector);
rv = ReparentWrapper(cx, reflector);
if NS_FAILED(rv) {
ReparentWrapper(cx, reflector, rv);
rv.WouldReportJSException();
if (rv.Failed()) {
// We _could_ consider BlastSubtreeToPieces here, but it's not really
// needed. Having some nodes in here accessible to content while others
// are not is probably OK. We just need to fail out of the actual
// insertion, so they're not in the DOM. Returning a failure here will
// do that.
return rv;
return rv.StealNSResult();
}
}
}
@ -1619,7 +1625,6 @@ nsINode::doInsertChildAt(nsIContent* aKid, uint32_t aIndex,
{
NS_PRECONDITION(!aKid->GetParentNode(),
"Inserting node that already has parent");
nsresult rv;
// The id-handling code, and in the future possibly other code, need to
// react to unexpected attribute changes.
@ -1630,15 +1635,23 @@ nsINode::doInsertChildAt(nsIContent* aKid, uint32_t aIndex,
mozAutoDocUpdate updateBatch(GetComposedDoc(), UPDATE_CONTENT_MODEL, aNotify);
if (OwnerDoc() != aKid->OwnerDoc()) {
rv = AdoptNodeIntoOwnerDoc(this, aKid);
NS_ENSURE_SUCCESS(rv, rv);
ErrorResult error;
AdoptNodeIntoOwnerDoc(this, aKid, error);
// Need to WouldReportJSException() if our callee can throw a JS
// exception (which it can) and we're neither propagating the
// error out nor unconditionally suppressing it.
error.WouldReportJSException();
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
}
uint32_t childCount = aChildArray.ChildCount();
NS_ENSURE_TRUE(aIndex <= childCount, NS_ERROR_ILLEGAL_VALUE);
bool isAppend = (aIndex == childCount);
rv = aChildArray.InsertChildAt(aKid, aIndex);
nsresult rv = aChildArray.InsertChildAt(aKid, aIndex);
NS_ENSURE_SUCCESS(rv, rv);
if (aIndex == 0) {
mFirstChild = aKid;
@ -2475,7 +2488,7 @@ nsINode::ReplaceOrInsertBefore(bool aReplace, nsINode* aNewChild,
// inserting them w/o calling AdoptNode().
nsIDocument* doc = OwnerDoc();
if (doc != newContent->OwnerDoc()) {
aError = AdoptNodeIntoOwnerDoc(this, aNewChild);
AdoptNodeIntoOwnerDoc(this, aNewChild, aError);
if (aError.Failed()) {
return nullptr;
}
@ -3072,9 +3085,7 @@ nsINode::WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
already_AddRefed<nsINode>
nsINode::CloneNode(bool aDeep, ErrorResult& aError)
{
nsCOMPtr<nsINode> result;
aError = nsNodeUtils::CloneNodeImpl(this, aDeep, getter_AddRefs(result));
return result.forget();
return nsNodeUtils::CloneNodeImpl(this, aDeep, aError);
}
nsDOMAttributeMap*

View file

@ -942,6 +942,12 @@ public:
return mParent;
}
/**
* This is similar to above, but in case 'this' is ShadowRoot, we return its
* host element.
*/
nsINode* GetParentOrHostNode();
/**
* Returns the node that is the parent of this node in the flattened
* tree. This differs from the normal parent if the node is filtered

View file

@ -1430,13 +1430,11 @@ nsImageLoadingContent::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
bool aCompileEventHandlers)
{
// We may be entering the document, so if our image should be tracked,
// track it.
if (!aDocument)
return;
TrackImage(mCurrentRequest);
TrackImage(mPendingRequest);
// We may be getting connected and our image should be tracked.
if (GetOurCurrentDoc()) {
TrackImage(mCurrentRequest);
TrackImage(mPendingRequest);
}
if (mCurrentRequestFlags & REQUEST_BLOCKS_ONLOAD)
aDocument->BlockOnload();

View file

@ -404,28 +404,20 @@ nsNodeUtils::TraverseUserData(nsINode* aNode,
}
/* static */
nsresult
nsNodeUtils::CloneNodeImpl(nsINode *aNode, bool aDeep, nsINode **aResult)
already_AddRefed<nsINode>
nsNodeUtils::CloneNodeImpl(nsINode *aNode, bool aDeep, ErrorResult& aError)
{
*aResult = nullptr;
nsCOMPtr<nsINode> newNode;
nsCOMArray<nsINode> nodesWithProperties;
nsresult rv = Clone(aNode, aDeep, nullptr, nodesWithProperties,
getter_AddRefs(newNode));
NS_ENSURE_SUCCESS(rv, rv);
newNode.forget(aResult);
return NS_OK;
return Clone(aNode, aDeep, nullptr, &nodesWithProperties, aError);
}
/* static */
nsresult
already_AddRefed<nsINode>
nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
nsNodeInfoManager *aNewNodeInfoManager,
JS::Handle<JSObject*> aReparentScope,
nsCOMArray<nsINode> &aNodesWithProperties,
nsINode *aParent, nsINode **aResult)
nsCOMArray<nsINode> *aNodesWithProperties,
nsINode *aParent, ErrorResult& aError)
{
NS_PRECONDITION((!aClone && aNewNodeInfoManager) || !aReparentScope,
"If cloning or not getting a new nodeinfo we shouldn't "
@ -433,14 +425,11 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
NS_PRECONDITION(!aParent || aNode->IsNodeOfType(nsINode::eCONTENT),
"Can't insert document or attribute nodes into a parent");
*aResult = nullptr;
// First deal with aNode and walk its attributes (and their children). Then,
// if aDeep is true, deal with aNode's children (and recurse into their
// attributes and children).
nsAutoScriptBlocker scriptBlocker;
nsresult rv;
nsNodeInfoManager *nodeInfoManager = aNewNodeInfoManager;
@ -452,14 +441,20 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
// Don't allow importing/adopting nodes from non-privileged "scriptable"
// documents to "non-scriptable" documents.
nsIDocument* newDoc = nodeInfoManager->GetDocument();
NS_ENSURE_STATE(newDoc);
if (NS_WARN_IF(!newDoc)) {
aError.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
bool hasHadScriptHandlingObject = false;
if (!newDoc->GetScriptHandlingObject(hasHadScriptHandlingObject) &&
!hasHadScriptHandlingObject) {
nsIDocument* currentDoc = aNode->OwnerDoc();
NS_ENSURE_STATE((nsContentUtils::IsChromeDoc(currentDoc) ||
(!currentDoc->GetScriptHandlingObject(hasHadScriptHandlingObject) &&
!hasHadScriptHandlingObject)));
if (NS_WARN_IF(!nsContentUtils::IsChromeDoc(currentDoc) &&
(currentDoc->GetScriptHandlingObject(hasHadScriptHandlingObject) ||
hasHadScriptHandlingObject))) {
aError.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
}
newNodeInfo = nodeInfoManager->GetNodeInfo(nodeInfo->NameAtom(),
@ -475,8 +470,11 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
nsCOMPtr<nsINode> clone;
if (aClone) {
rv = aNode->Clone(nodeInfo, getter_AddRefs(clone));
NS_ENSURE_SUCCESS(rv, rv);
nsresult rv = aNode->Clone(nodeInfo, getter_AddRefs(clone));
if (NS_WARN_IF(NS_FAILED(rv))) {
aError.Throw(rv);
return nullptr;
}
if (CustomElementRegistry::IsCustomElementEnabled() &&
clone->IsHTMLElement()) {
@ -514,7 +512,10 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
// parent.
rv = aParent->AppendChildTo(static_cast<nsIContent*>(clone.get()),
false);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
aError.Throw(rv);
return nullptr;
}
}
else if (aDeep && clone->IsNodeOfType(nsINode::eDOCUMENT)) {
// After cloning the document itself, we want to clone the children into
@ -613,8 +614,8 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
if ((wrapper = aNode->GetWrapper())) {
MOZ_ASSERT(IsDOMObject(wrapper));
JSAutoCompartment ac(cx, wrapper);
rv = ReparentWrapper(cx, wrapper);
if (NS_FAILED(rv)) {
ReparentWrapper(cx, wrapper, aError);
if (aError.Failed()) {
if (wasRegistered) {
aNode->OwnerDoc()->UnregisterActivityObserver(aNode->AsElement());
}
@ -622,17 +623,17 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
if (wasRegistered) {
aNode->OwnerDoc()->RegisterActivityObserver(aNode->AsElement());
}
return rv;
return nullptr;
}
}
}
}
if (aNode->HasProperties()) {
bool ok = aNodesWithProperties.AppendObject(aNode);
bool ok = aNodesWithProperties->AppendObject(aNode);
MOZ_RELEASE_ASSERT(ok, "Out of memory");
if (aClone) {
ok = aNodesWithProperties.AppendObject(clone);
ok = aNodesWithProperties->AppendObject(clone);
MOZ_RELEASE_ASSERT(ok, "Out of memory");
}
}
@ -642,11 +643,47 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
for (nsIContent* cloneChild = aNode->GetFirstChild();
cloneChild;
cloneChild = cloneChild->GetNextSibling()) {
nsCOMPtr<nsINode> child;
rv = CloneAndAdopt(cloneChild, aClone, true, nodeInfoManager,
aReparentScope, aNodesWithProperties, clone,
getter_AddRefs(child));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsINode> child =
CloneAndAdopt(cloneChild, aClone, true, nodeInfoManager,
aReparentScope, aNodesWithProperties, clone,
aError);
if (NS_WARN_IF(aError.Failed())) {
return nullptr;
}
}
}
if (aDeep && aNode->IsElement()) {
ShadowRoot* shadowRoot = aNode->AsElement()->GetShadowRoot();
if (!shadowRoot) {
// Nothing to do here.
} else if (aClone && clone->OwnerDoc()->IsStaticDocument()) {
ShadowRootInit init;
init.mMode = shadowRoot->Mode();
RefPtr<ShadowRoot> newShadowRoot =
clone->AsElement()->AttachShadow(init, aError);
if (NS_WARN_IF(aError.Failed())) {
return nullptr;
}
newShadowRoot->CloneInternalDataFrom(shadowRoot);
for (nsIContent* originalChild = shadowRoot->GetFirstChild();
originalChild;
originalChild = originalChild->GetNextSibling()) {
nsCOMPtr<nsINode> child = CloneAndAdopt(originalChild, aClone, aDeep, nodeInfoManager,
aReparentScope, aNodesWithProperties, newShadowRoot,
aError);
if (NS_WARN_IF(aError.Failed())) {
return nullptr;
}
}
} else if (!aClone) {
nsCOMPtr<nsINode> child = CloneAndAdopt(shadowRoot, aClone, aDeep, nodeInfoManager,
aReparentScope, aNodesWithProperties, clone,
aError);
if (NS_WARN_IF(aError.Failed())) {
return nullptr;
}
}
}
@ -665,11 +702,13 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
for (nsIContent* cloneChild = origContent->GetFirstChild();
cloneChild;
cloneChild = cloneChild->GetNextSibling()) {
nsCOMPtr<nsINode> child;
rv = CloneAndAdopt(cloneChild, aClone, aDeep, ownerNodeInfoManager,
aReparentScope, aNodesWithProperties, cloneContent,
getter_AddRefs(child));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsINode> child =
CloneAndAdopt(cloneChild, aClone, aDeep, ownerNodeInfoManager,
aReparentScope, aNodesWithProperties, cloneContent,
aError);
if (NS_WARN_IF(aError.Failed())) {
return nullptr;
}
}
}
@ -692,9 +731,7 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
}
#endif
clone.forget(aResult);
return NS_OK;
return clone.forget();
}

View file

@ -17,6 +17,7 @@ template<class E> class nsCOMArray;
class nsCycleCollectionTraversalCallback;
namespace mozilla {
struct NonOwningAnimationTarget;
class ErrorResult;
namespace dom {
class Animation;
} // namespace dom
@ -181,25 +182,17 @@ public:
* @param aNodesWithProperties All nodes (from amongst aNode and its
* descendants) with properties. Every node will
* be followed by its clone.
* @param aResult *aResult will contain the cloned node.
* @param aError The error, if any.
*
* @return The newly created node. Null in error conditions.
*/
static nsresult Clone(nsINode *aNode, bool aDeep,
nsNodeInfoManager *aNewNodeInfoManager,
nsCOMArray<nsINode> &aNodesWithProperties,
nsINode **aResult)
static already_AddRefed<nsINode> Clone(nsINode *aNode, bool aDeep,
nsNodeInfoManager *aNewNodeInfoManager,
nsCOMArray<nsINode> *aNodesWithProperties,
mozilla::ErrorResult& aError)
{
return CloneAndAdopt(aNode, true, aDeep, aNewNodeInfoManager,
nullptr, aNodesWithProperties, nullptr, aResult);
}
/**
* Clones aNode, its attributes and, if aDeep is true, its descendant nodes
*/
static nsresult Clone(nsINode *aNode, bool aDeep, nsINode **aResult)
{
nsCOMArray<nsINode> dummyNodeWithProperties;
return CloneAndAdopt(aNode, true, aDeep, nullptr, nullptr,
dummyNodeWithProperties, aNode->GetParent(), aResult);
nullptr, aNodesWithProperties, nullptr, aError);
}
/**
@ -218,19 +211,20 @@ public:
* should be done.
* @param aNodesWithProperties All nodes (from amongst aNode and its
* descendants) with properties.
* @param aError The error, if any.
*/
static nsresult Adopt(nsINode *aNode, nsNodeInfoManager *aNewNodeInfoManager,
JS::Handle<JSObject*> aReparentScope,
nsCOMArray<nsINode> &aNodesWithProperties)
static void Adopt(nsINode *aNode, nsNodeInfoManager *aNewNodeInfoManager,
JS::Handle<JSObject*> aReparentScope,
nsCOMArray<nsINode>& aNodesWithProperties,
mozilla::ErrorResult& aError)
{
nsCOMPtr<nsINode> node;
nsresult rv = CloneAndAdopt(aNode, false, true, aNewNodeInfoManager,
aReparentScope, aNodesWithProperties,
nullptr, getter_AddRefs(node));
// Just need to store the return value of CloneAndAdopt in a
// temporary nsCOMPtr to make sure we release it.
nsCOMPtr<nsINode> node = CloneAndAdopt(aNode, false, true, aNewNodeInfoManager,
aReparentScope, &aNodesWithProperties,
nullptr, aError);
nsMutationGuard::DidMutate();
return rv;
}
/**
@ -248,9 +242,12 @@ public:
*
* @param aNode the node to clone
* @param aDeep if true all descendants will be cloned too
* @param aResult the clone
* @param aError the error, if any.
*
* @return the clone, or null if an error occurs.
*/
static nsresult CloneNodeImpl(nsINode *aNode, bool aDeep, nsINode **aResult);
static already_AddRefed<nsINode> CloneNodeImpl(nsINode *aNode, bool aDeep,
mozilla::ErrorResult& aError);
/**
* Release the UserData for aNode.
@ -286,7 +283,7 @@ private:
*
* @param aNode Node to adopt/clone.
* @param aClone If true the node will be cloned and the cloned node will
* be in aResult.
* be returned.
* @param aDeep If true the function will be called recursively on
* descendants of the node
* @param aNewNodeInfoManager The nodeinfo manager to use to create new
@ -302,14 +299,18 @@ private:
* @param aParent If aClone is true the cloned node will be appended to
* aParent's children. May be null. If not null then aNode
* must be an nsIContent.
* @param aResult If aClone is true then *aResult will contain the cloned
* node.
* @param aError The error, if any.
*
* @return If aClone is true then the cloned node will be returned,
* unless an error occurred. In error conditions, null
* will be returned.
*/
static nsresult CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
nsNodeInfoManager *aNewNodeInfoManager,
JS::Handle<JSObject*> aReparentScope,
nsCOMArray<nsINode> &aNodesWithProperties,
nsINode *aParent, nsINode **aResult);
static already_AddRefed<nsINode>
CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
nsNodeInfoManager* aNewNodeInfoManager,
JS::Handle<JSObject*> aReparentScope,
nsCOMArray<nsINode> *aNodesWithProperties,
nsINode *aParent, mozilla::ErrorResult& aError);
enum class AnimationMutationType
{

View file

@ -304,14 +304,6 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
return NS_OK;
}
// Check for a ShadowRoot because link elements are inert in a
// ShadowRoot.
ShadowRoot* containingShadow = thisContent->GetContainingShadow();
if (thisContent->IsHTMLElement(nsGkAtoms::link) &&
(aOldShadowRoot || containingShadow)) {
return NS_OK;
}
// XXXheycam ServoStyleSheets do not support <style scoped>.
Element* oldScopeElement = nullptr;
if (mStyleSheet) {
@ -346,15 +338,16 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
}
}
// When static documents are created, stylesheets are cloned manually.
if (mDontLoadStyle || !mUpdatesEnabled ||
thisContent->OwnerDoc()->IsStaticDocument()) {
nsCOMPtr<nsIDocument> doc = thisContent->IsInShadowTree() ?
thisContent->OwnerDoc() : thisContent->GetUncomposedDoc();
// Loader could be null during unlink, see bug 1425866.
if (!doc || !doc->CSSLoader() || !doc->CSSLoader()->GetEnabled()) {
return NS_OK;
}
nsCOMPtr<nsIDocument> doc = thisContent->IsInShadowTree() ?
thisContent->OwnerDoc() : thisContent->GetUncomposedDoc();
if (!doc || !doc->CSSLoader()->GetEnabled()) {
// When static documents are created, stylesheets are cloned manually.
if (mDontLoadStyle || !mUpdatesEnabled || doc->IsStaticDocument()) {
return NS_OK;
}
@ -426,8 +419,7 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
rv = doc->CSSLoader()->
LoadInlineStyle(thisContent, text, mLineNumber, title, media,
scopeElement, aObserver, &doneLoading, &isAlternate, &isExplicitlyEnabled);
}
else {
} else {
nsAutoString integrity;
thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::integrity, integrity);
if (!integrity.IsEmpty()) {

View file

@ -0,0 +1,903 @@
<html>
<head>
<title>Test for Bug 1453693</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<script>
class TestNode extends HTMLElement {
constructor() {
super();
const styles = "<style>:focus{background-color:yellow;}</style>";
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML =
`${styles}<div tabindex='-1'>test node</div> <slot></slot>`;
}}
window.customElements.define('test-node', TestNode);
var lastFocusTarget;
function focusLogger(event) {
lastFocusTarget = event.target;
console.log(event.target + " under " + event.target.parentNode);
event.stopPropagation();
}
function testTabbingThroughShadowDOMWithTabIndexes() {
var anchor = document.createElement("a");
anchor.onfocus = focusLogger;
anchor.href = "#";
anchor.textContent = "in light DOM";
document.body.appendChild(anchor);
var host = document.createElement("div");
document.body.appendChild(host);
var sr = host.attachShadow({mode: "open"});
var shadowAnchor = anchor.cloneNode(false);
shadowAnchor.onfocus = focusLogger;
shadowAnchor.textContent = "in shadow DOM";
sr.appendChild(shadowAnchor);
var shadowInput = document.createElement("input");
shadowInput.onfocus = focusLogger;
shadowInput.tabIndex = 1;
sr.appendChild(shadowInput);
var shadowDate = document.createElement("input");
shadowDate.type = "date";
shadowDate.onfocus = focusLogger;
shadowDate.tabIndex = 1;
sr.appendChild(shadowDate);
var shadowIframe = document.createElement("iframe");
shadowIframe.tabIndex = 1;
sr.appendChild(shadowIframe);
shadowIframe.contentDocument.body.innerHTML = "<input>";
var input = document.createElement("input");
input.onfocus = focusLogger;
input.tabIndex = 1;
document.body.appendChild(input);
var input2 = document.createElement("input");
input2.onfocus = focusLogger;
document.body.appendChild(input2);
document.body.offsetLeft;
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input, "Should have focused input element. (3)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, anchor, "Should have focused anchor element. (3)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM. (3)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (3)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (3)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (3)");
synthesizeKey("KEY_Tab");
opener.is(shadowIframe.contentDocument.activeElement,
shadowIframe.contentDocument.documentElement,
"Should have focused document element in shadow iframe. (3)");
synthesizeKey("KEY_Tab");
opener.is(shadowIframe.contentDocument.activeElement,
shadowIframe.contentDocument.body.firstChild,
"Should have focused input element in shadow iframe. (3)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, shadowAnchor, "Should have focused anchor element in shadow DOM. (3)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input2, "Should have focused input[2] element. (3)");
// Backwards
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, shadowAnchor, "Should have focused anchor element in shadow DOM. (4)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(shadowIframe.contentDocument.activeElement,
shadowIframe.contentDocument.body.firstChild,
"Should have focused input element in shadow iframe. (4)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(shadowIframe.contentDocument.activeElement,
shadowIframe.contentDocument.documentElement,
"Should have focused document element in shadow iframe. (4)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (4)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (4)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (4)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM. (4)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, anchor, "Should have focused anchor element. (4)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, input, "Should have focused input element. (4)");
document.body.innerHTML = null;
}
function testTabbingThroughSimpleShadowDOM() {
var anchor = document.createElement("a");
anchor.onfocus = focusLogger;
anchor.href = "#";
anchor.textContent = "in light DOM";
document.body.appendChild(anchor);
anchor.focus();
var host = document.createElement("div");
document.body.appendChild(host);
var sr = host.attachShadow({mode: "open"});
var shadowAnchor = anchor.cloneNode(false);
shadowAnchor.onfocus = focusLogger;
shadowAnchor.textContent = "in shadow DOM";
sr.appendChild(shadowAnchor);
var shadowInput = document.createElement("input");
shadowInput.onfocus = focusLogger;
sr.appendChild(shadowInput);
var hiddenShadowButton = document.createElement("button");
hiddenShadowButton.setAttribute("style", "display: none;");
sr.appendChild(hiddenShadowButton);
var input = document.createElement("input");
input.onfocus = focusLogger;
document.body.appendChild(input);
var input2 = document.createElement("input");
input2.onfocus = focusLogger;
document.body.appendChild(input2);
document.body.offsetLeft;
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, shadowAnchor, "Should have focused anchor element in shadow DOM.");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM.");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input, "Should have focused input element.");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input2, "Should have focused input[2] element.");
// Backwards
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, input, "Should have focused input element. (2)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM. (2)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, shadowAnchor, "Should have focused anchor element in shadow DOM. (2)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, anchor, "Should have focused anchor element. (2)");
host.remove();
input.remove();
input2.remove();
}
function testTabbingThroughNestedShadowDOM() {
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus. (1)");
var host = document.createElement("div");
host.id = "host";
document.body.appendChild(host);
var sr0 = host.attachShadow({mode: "open"});
sr0.innerHTML = "<button id='button'>X</button><br id='br'><div id='h1'></div><div id='h2'></div>";
var button = sr0.getElementById("button");
button.onfocus = focusLogger;
var h1 = sr0.getElementById("h1");
var sr1 = h1.attachShadow({mode: "open"});
sr1.innerHTML = "h1 <input id='h11' placeholder='click me and press tab'><input id='h12' placeholder='and then tab again'>";
var input11 = sr1.getElementById("h11");
input11.onfocus = focusLogger;
var input12 = sr1.getElementById("h12");
input12.onfocus = focusLogger;
var h2 = sr0.getElementById("h2");
var sr2 = h2.attachShadow({mode: "open"});
sr2.innerHTML = "h2 <input id='h21'><input id='h22'>";
var input21 = sr2.getElementById("h21");
input21.onfocus = focusLogger;
var input22 = sr2.getElementById("h22");
input22.onfocus = focusLogger;
document.body.offsetLeft;
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, button, "[nested shadow] Should have focused button element. (1)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input11, "[nested shadow] Should have focused input element. (1)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input12, "[nested shadow] Should have focused input element. (2)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input21, "[nested shadow] Should have focused input element. (3)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input22, "[nested shadow] Should have focused input element. (4)");
// Backwards
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, input21, "[nested shadow] Should have focused input element. (5)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, input12, "[nested shadow] Should have focused input element. (6)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, input11, "[nested shadow] Should have focused input element. (7)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, button, "[nested shadow] Should have focused button element. (8)");
// Back to beginning, outside of Shadow DOM.
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus. (2)");
host.remove();
}
function testTabbingThroughDisplayContentsHost() {
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus. (1)");
var host = document.createElement("div");
host.id = "host";
host.setAttribute("style", "display: contents; border: 1px solid black;");
document.body.appendChild(host);
var sr0 = host.attachShadow({mode: "open"});
sr0.innerHTML = "<input id='shadowInput1'><input id='shadowInput2'>";
var shadowInput1 = sr0.getElementById("shadowInput1");
shadowInput1.onfocus = focusLogger;
var shadowInput2 = sr0.getElementById("shadowInput2");
shadowInput2.onfocus = focusLogger;
var host1 = document.createElement("div");
host1.id = "host";
host1.tabIndex = 0;
host1.setAttribute("style", "display: contents; border: 1px solid black;");
document.body.appendChild(host1);
var sr1 = host1.attachShadow({mode: "open"});
sr1.innerHTML = "<input id='shadowInput1'><input id='shadowInput2'>";
var shadowInput3 = sr1.getElementById("shadowInput1");
shadowInput3.onfocus = focusLogger;
var shadowInput4 = sr1.getElementById("shadowInput2");
shadowInput4.onfocus = focusLogger;
document.body.offsetLeft;
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, shadowInput1, "Should have focused input element. (1)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, shadowInput2, "Should have focused input element. (2)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, shadowInput3, "Should have focused input element. (3)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, shadowInput4, "Should have focused input element. (4)");
// Backwards
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, shadowInput3, "Should have focused input element. (5)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, shadowInput2, "Should have focused input element. (6)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, shadowInput1, "Should have focused input element. (7)");
// Back to beginning, outside of Shadow DOM.
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus. (2)");
host.remove();
host1.remove();
}
function testTabbingThroughLightDOMShadowDOMLightDOM() {
opener.is(document.activeElement, document.body.firstChild,
"body's first child should have focus.");
var host = document.createElement("span");
host.innerHTML = "\n";
host.id = "host";
document.body.appendChild(host);
var sr0 = host.attachShadow({mode: "open"});
sr0.innerHTML = document.getElementById("template").innerHTML;
var p1 = sr0.getElementById("p1");
p1.onfocus = focusLogger;
var p2 = sr0.getElementById("p2");
p2.onfocus = focusLogger;
var p = document.createElement("p");
p.innerHTML = " <a href='#p'>link 1</a> ";
var a = p.firstElementChild;
a.onfocus = focusLogger;
document.body.appendChild(p);
document.body.offsetLeft;
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, p1, "Should have focused p1.");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, p2, "Should have focused p2.");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, a, "Should have focused a.");
// Backwards
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, p2, "Should have focused p2.");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, p1, "Should have focused p1.");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(document.activeElement, document.body.firstChild,
"body's first child should have focus.");
host.remove();
p.remove();
}
function testFocusableHost() {
opener.is(document.activeElement, document.body.firstChild,
"body's first child should have focus.");
var host = document.createElement("div");
host.id = "host";
host.tabIndex = 0;
host.onfocus = focusLogger;
document.body.appendChild(host);
var slotted = document.createElement("div");
slotted.tabIndex = 0;
slotted.onfocus = focusLogger;
host.appendChild(slotted);
var sr0 = host.attachShadow({mode: "open"});
sr0.appendChild(document.createElement("slot"));
var p = document.createElement("p");
p.innerHTML = " <a href='#p'>link 1</a> ";
var a = p.firstElementChild;
a.onfocus = focusLogger;
document.body.appendChild(p);
document.body.offsetLeft;
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, host, "Should have focused host.");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, slotted, "Should have focused slotted.");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, a, "Should have focused a.");
// Backwards
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, slotted, "Should have focused slotted.");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, host, "Should have focused host.");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(document.activeElement, document.body.firstChild,
"body's first child should have focus.");
host.remove();
p.remove();
}
function testShiftTabbingThroughFocusableHost() {
opener.is(document.activeElement, document.body.firstChild,
"body's first child should have focus.");
var host = document.createElement("div");
host.id = "host";
host.tabIndex = 0;
host.onfocus = focusLogger;
document.body.appendChild(host);
var sr = host.attachShadow({mode: "open"});
var shadowButton = document.createElement("button");
shadowButton.innerText = "X";
shadowButton.onfocus = focusLogger;
sr.appendChild(shadowButton);
var shadowInput = document.createElement("input");
shadowInput.onfocus = focusLogger;
sr.appendChild(shadowInput);
sr.appendChild(document.createElement("br"));
var input = document.createElement("input");
input.onfocus = focusLogger;
document.body.appendChild(input);
document.body.offsetLeft;
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, host, "Should have focused host element. (1)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, shadowButton, "Should have focused button element in shadow DOM. (2)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM. (3)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input, "Should have focused input element. (4)");
// Backwards
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM. (5)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, shadowButton, "Should have focused button element in shadow DOM. (6)");
synthesizeKey("KEY_Tab", {shiftKey: true});
// focus is already on host
opener.is(sr.activeElement, null,
"Focus should have left button element in shadow DOM. (7)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(document.activeElement, document.body.firstChild,
"body's first child should have focus.");
host.remove();
input.remove();
}
function testTabbingThroughNestedSlot() {
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
var host0 = document.createElement("div");
var sr0 = host0.attachShadow({mode: "open"});
sr0.innerHTML = "<slot></slot>";
document.body.appendChild(host0);
// focusable
var host00 = document.createElement("div");
var sr00 = host00.attachShadow({mode: "open"});
var div00 = document.createElement("div");
div00.tabIndex = 0;
div00.onfocus = focusLogger;
sr00.appendChild(div00);
host0.appendChild(host00);
// not focusable
var host01 = document.createElement("div");
var sr01 = host01.attachShadow({mode: "open"});
sr01.innerHTML = "<div></div>";
host0.appendChild(host01);
// focusable
var host02 = document.createElement("div");
var sr02 = host02.attachShadow({mode: "open"});
var div02 = document.createElement("div");
div02.tabIndex = 0;
div02.onfocus = focusLogger;
sr02.appendChild(div02);
host0.appendChild(host02);
var host1 = document.createElement("div");
var sr1 = host1.attachShadow({mode: "open"});
sr1.innerHTML = "<slot></slot>";
document.body.appendChild(host1);
var host10 = document.createElement("div");
var sr10 = host10.attachShadow({mode: "open"});
sr10.innerHTML = "<slot></slot>";
host1.appendChild(host10);
var input10 = document.createElement("input");
input10.onfocus = focusLogger;
host10.appendChild(input10);
var host11 = document.createElement("div");
var sr11 = host11.attachShadow({mode: "open"});
sr11.innerHTML = "<slot></slot>";
host1.appendChild(host11);
var input11 = document.createElement("input");
input11.onfocus = focusLogger;
host11.appendChild(input11);
document.body.offsetLeft;
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, div00, "Should have focused div element in shadow DOM. (1)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, div02, "Should have focused div element in shadow DOM. (2)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input10, "Should have focused input element in shadow DOM. (3)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input11, "Should have focused button element in shadow DOM. (4)");
// Backwards
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, input10, "Should have focused input element in shadow DOM. (5)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, div02, "Should have focused input element in shadow DOM. (6)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, div00, "Should have focused input element in shadow DOM. (7)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(document.activeElement, document.body.firstChild,
"body's first child should have focus.");
host0.remove();
host1.remove();
}
function testTabbingThroughSlotInLightDOM() {
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
var input0 = document.createElement("input");
input0.onfocus = focusLogger;
document.body.appendChild(input0);
var slot1 = document.createElement("slot");
document.body.appendChild(slot1);
var input10 = document.createElement("input");
input10.onfocus = focusLogger;
slot1.appendChild(input10);
var input11 = document.createElement("input");
input11.onfocus = focusLogger;
slot1.appendChild(input11);
var input2 = document.createElement("input");
input2.onfocus = focusLogger;
document.body.appendChild(input2);
document.body.offsetLeft;
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input0, "Should have focused input element. (1)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input10, "Should have focused input element in slot. (2)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input11, "Should have focused input element in slot. (3)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input2, "Should have focused input element. (4)");
// Backwards
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, input11, "Should have focused input element in slot. (5)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, input10, "Should have focused input element in slot. (6)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, input0, "Should have focused input element. (7)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(document.activeElement, document.body.firstChild,
"body's first child should have focus.");
input0.remove();
slot1.remove();
input2.remove();
}
function testTabbingThroughFocusableSlotInLightDOM() {
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
var slot0 = document.createElement("slot");
slot0.tabIndex = 0;
slot0.setAttribute("style", "display: inline;");
slot0.onfocus = focusLogger;
document.body.appendChild(slot0);
var slot00 = document.createElement("slot");
slot00.tabIndex = 0;
slot00.setAttribute("style", "display: inline;");
slot00.onfocus = focusLogger;
slot0.appendChild(slot00);
var input000 = document.createElement("input");
input000.onfocus = focusLogger;
slot00.appendChild(input000);
var input01 = document.createElement("input");
input01.onfocus = focusLogger;
slot0.appendChild(input01);
var input1 = document.createElement("input");
input1.onfocus = focusLogger;
document.body.appendChild(input1);
document.body.offsetLeft;
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, slot0, "Should have focused slot element. (1)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, slot00, "Should have focused slot element. (2)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input000, "Should have focused input element in slot. (3)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input01, "Should have focused input element in slot. (4)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input1, "Should have focused input element. (5)");
// Backwards
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, input01, "Should have focused input element in slot. (6)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, input000, "Should have focused input element in slot. (7)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, slot00, "Should have focused slot element. (8)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, slot0, "Should have focused slot element. (9)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(document.activeElement, document.body.firstChild,
"body's first child should have focus.");
slot0.remove();
input1.remove();
}
function testTabbingThroughScrollableShadowDOM() {
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
var host0 = document.createElement("div");
host0.setAttribute("style", "height: 50px; overflow: auto;");
host0.onfocus = focusLogger;
document.body.appendChild(host0);
var sr0 = host0.attachShadow({mode: "open"});
sr0.innerHTML = `
<style>
div,slot {
height: 30px;
display: block;
overflow: auto;
}
input {
display: block;
}
</style>
`;
var input00 = document.createElement("input");
input00.setAttribute("style", "background-color: red;");
input00.onfocus = focusLogger;
sr0.appendChild(input00);
var container01 = document.createElement("div");
container01.onfocus = focusLogger;
sr0.appendChild(container01);
var input010 = document.createElement("input");
input010.onfocus = focusLogger;
container01.appendChild(input010);
var input011 = document.createElement("input");
input011.onfocus = focusLogger;
container01.appendChild(input011);
var slot02 = document.createElement("slot");
slot02.onfocus = focusLogger;
sr0.appendChild(slot02);
var input020 = document.createElement("input");
input020.setAttribute("style", "display: block;");
input020.onfocus = focusLogger;
host0.appendChild(input020);
var input021 = document.createElement("input");
input021.setAttribute("style", "display: block;");
input021.onfocus = focusLogger;
host0.appendChild(input021);
var input1 = document.createElement("input");
input1.onfocus = focusLogger;
document.body.appendChild(input1);
document.body.offsetLeft;
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, host0, "Should have focused shadow host element. (1)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input00, "Should have focused input element in shadow dom. (2)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, container01, "Should have focused scrollable element in shadow dom. (3)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input010, "Should have focused input element in shadow dom. (4)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input011, "Should have focused input element in shadow dom. (5)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, slot02, "Should have focused slot element in shadow dom. (6)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input020, "Should have focused input element in slot. (7)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input021, "Should have focused input element in slot. (8)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input1, "Should have focused input element in light dom. (9)");
// Backwards
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, input021, "Should have focused input element in slot. (10)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, input020, "Should have focused input element in slot. (11)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, slot02, "Should have focused slot element in shadow dom. (12)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, input011, "Should have focused input element in shadow dom. (13)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, input010, "Should have focused input element in shadow dom. (14)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, container01, "Should have focused scrollable element in shadow dom. (15)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, input00, "Should have focused input element in shadow dom. (16)");
synthesizeKey("KEY_Tab", {shiftKey: true});
// focus is already on host
opener.is(sr0.activeElement, null,
"Focus should have left input element in shadow DOM. (7)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(document.activeElement, document.body.firstChild,
"body's first child should have focus.");
host0.remove();
input1.remove();
}
function testDeeplyNestedShadowTree() {
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
var host1 = document.createElement("test-node");
var lastHost = host1;
for (var i = 0; i < 20; ++i) {
lastHost.appendChild(document.createElement("test-node"));
lastHost = lastHost.firstChild;
}
var input = document.createElement("input");
document.body.appendChild(host1);
document.body.appendChild(input);
document.body.offsetLeft;
// Test shadow tree which doesn't have anything tab-focusable.
host1.shadowRoot.querySelector("div").focus();
synthesizeKey("KEY_Tab");
is(document.activeElement, input, "Should have focused input element.");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
// Same test but with focusable elements in the tree...
var input2 = document.createElement("input");
var host2 = host1.firstChild;
var host3 = host2.firstChild;
host2.insertBefore(input2, host3);
var input3 = document.createElement("input");
lastHost.appendChild(input3);
document.body.offsetLeft;
host3.shadowRoot.querySelector("div").focus();
synthesizeKey("KEY_Tab");
is(document.activeElement, input3, "Should have focused input3 element.");
// ...and backwards
host3.shadowRoot.querySelector("div").focus();
synthesizeKey("KEY_Tab", {shiftKey: true});
is(document.activeElement, input2, "Should have focused input2 element.");
// Remove elements added to body element.
host1.remove();
input.remove();
// Tests expect body.firstChild to have focus.
document.body.firstChild.focus();
}
// Bug 1558393
function testBackwardsTabbingWithSlotsWithoutFocusableContent() {
let first = document.createElement("div");
first.tabIndex = 0;
let host = document.createElement("div");
host.tabIndex = 0;
let second = document.createElement("div");
second.tabIndex = 0;
host.appendChild(document.createTextNode("foo"));
host.attachShadow({ mode: "open" }).innerHTML = `<slot></slot>`;
document.body.appendChild(first);
document.body.appendChild(host);
document.body.appendChild(second);
document.body.offsetLeft;
first.focus();
opener.is(document.activeElement, first, "First light div should have focus");
synthesizeKey("KEY_Tab");
opener.is(document.activeElement, host, "Host should be focused");
synthesizeKey("KEY_Tab");
opener.is(document.activeElement, second, "Second light div should be focused");
// Now backwards
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(document.activeElement, host, "Focus should return to host");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(document.activeElement, first, "Focus should return to first light div");
second.remove();
host.remove();
first.remove();
}
function runTest() {
testTabbingThroughShadowDOMWithTabIndexes();
testTabbingThroughSimpleShadowDOM();
testTabbingThroughNestedShadowDOM();
testTabbingThroughDisplayContentsHost();
testTabbingThroughLightDOMShadowDOMLightDOM();
testFocusableHost();
testShiftTabbingThroughFocusableHost();
testTabbingThroughNestedSlot();
testTabbingThroughSlotInLightDOM();
testTabbingThroughFocusableSlotInLightDOM();
testTabbingThroughScrollableShadowDOM();
testDeeplyNestedShadowTree();
testBackwardsTabbingWithSlotsWithoutFocusableContent();
opener.didRunTests();
window.close();
}
function init() {
SimpleTest.waitForFocus(runTest);
}
</script>
<style>
</style>
<template id="template">
<div style="overflow: hidden">
<p tabindex="0" id="p1">component</p>
<p tabindex="0" id="p2">/component</p>
</div>
</template>
</head>
<body onload="init()">
</body>
</html>

View file

@ -129,6 +129,7 @@ support-files =
file_bug1263696_frame_pass.html
file_bug1263696_frame_fail.html
file_bug1274806.html
file_bug1453693.html
file_general_document.html
file_htmlserializer_1.html
file_htmlserializer_1_bodyonly.html
@ -609,6 +610,8 @@ skip-if = toolkit == 'android'
[test_bug1308069.html]
[test_bug1314032.html]
[test_bug1375050.html]
[test_bug1453693.html]
skip-if = os == "mac" # Different tab focus behavior on mac
[test_caretPositionFromPoint.html]
[test_change_policy.html]
[test_classList.html]
@ -648,6 +651,8 @@ skip-if = toolkit == 'android' #bug 904183
[test_fileapi.html]
[test_fileapi_slice.html]
skip-if = (toolkit == 'android') # Android: Bug 775227
[test_focus_shadow_dom_root.html]
skip-if = os == "mac" # Different tab focus behavior on mac
[test_getAttribute_after_createAttribute.html]
[test_getElementById.html]
[test_getTranslationNodes.html]

View file

@ -0,0 +1,32 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1453693
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1453693</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
/** Test for Bug 1453693 **/
SimpleTest.waitForExplicitFinish();
function runTests() {
win = window.open("file_bug1453693.html", "", "width=300, height=300");
}
function didRunTests() {
setTimeout("SimpleTest.finish()");
}
;
</script>
</head>
<body onload="SimpleTest.waitForFocus(runTests);">
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1453693">Mozilla Bug 1453693</a>
</body>
</html>

View file

@ -0,0 +1,36 @@
<!doctype html>
<title>Test for bug 1544826</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<div id="host"><a href="#" id="slotted">This is focusable too</a></div>
<script>
const host = document.getElementById("host");
const shadow = host.attachShadow({ mode: "open" });
shadow.innerHTML = `
<a id="shadow-1" href="#">This is focusable</a>
<slot></slot>
<a id="shadow-2" href="#">So is this</a>
`;
document.documentElement.remove();
document.appendChild(host);
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
is(document.documentElement, host, "Host is the document element");
host.offsetTop;
synthesizeKey("KEY_Tab");
is(shadow.activeElement.id, "shadow-1", "First link in Shadow DOM is focused");
synthesizeKey("KEY_Tab");
is(document.activeElement.id, "slotted", "Slotted link is focused");
synthesizeKey("KEY_Tab");
is(shadow.activeElement.id, "shadow-2", "Second link in Shadow DOM is focused");
// Now backwards.
synthesizeKey("KEY_Tab", {shiftKey: true});
is(document.activeElement.id, "slotted", "Backwards: Slotted link is focused");
synthesizeKey("KEY_Tab", {shiftKey: true});
is(shadow.activeElement.id, "shadow-1", "Backwards: First slotted link is focused");
SimpleTest.finish();
});
</script>

View file

@ -2106,16 +2106,19 @@ DictionaryBase::AppendJSONToString(const char16_t* aJSONData,
return true;
}
nsresult
ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
void
ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg, ErrorResult& aError)
{
js::AssertSameCompartment(aCx, aObjArg);
aError.MightThrowJSException();
// Check if we're anywhere near the stack limit before we reach the
// transplanting code, since it has no good way to handle errors. This uses
// the untrusted script limit, which is not strictly necessary since no
// actual script should run.
JS_CHECK_RECURSION_CONSERVATIVE(aCx, return NS_ERROR_FAILURE);
// TODO: Make sure to retain 'onerror' if bug 1342439 lands.
JS_CHECK_RECURSION_CONSERVATIVE(aCx, aError.StealExceptionFromJSContext(aCx); return);
JS::Rooted<JSObject*> aObj(aCx, aObjArg);
const DOMJSClass* domClass = GetDOMClass(aObj);
@ -2135,12 +2138,12 @@ ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
JSCompartment* newCompartment = js::GetObjectCompartment(newParent);
if (oldCompartment == newCompartment) {
MOZ_ASSERT(oldParent == newParent);
return NS_OK;
return;
}
nsISupports* native = UnwrapDOMObjectToISupports(aObj);
if (!native) {
return NS_OK;
return;
}
bool isProxy = js::IsProxy(aObj);
@ -2156,12 +2159,14 @@ ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
JS::Handle<JSObject*> proto = (domClass->mGetProto)(aCx);
if (!proto) {
return NS_ERROR_FAILURE;
aError.StealExceptionFromJSContext(aCx);
return;
}
JS::Rooted<JSObject*> newobj(aCx, JS_CloneObject(aCx, aObj, proto));
if (!newobj) {
return NS_ERROR_FAILURE;
aError.StealExceptionFromJSContext(aCx);
return;
}
JS::Rooted<JSObject*> propertyHolder(aCx);
@ -2169,11 +2174,13 @@ ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
if (copyFrom) {
propertyHolder = JS_NewObjectWithGivenProto(aCx, nullptr, nullptr);
if (!propertyHolder) {
return NS_ERROR_OUT_OF_MEMORY;
aError.StealExceptionFromJSContext(aCx);
return;
}
if (!JS_CopyPropertiesFrom(aCx, propertyHolder, copyFrom)) {
return NS_ERROR_FAILURE;
aError.StealExceptionFromJSContext(aCx);
return;
}
} else {
propertyHolder = nullptr;
@ -2189,7 +2196,8 @@ ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
// if expandos are present then the wrapper will already have been preserved
// for this native.
if (!xpc::XrayUtils::CloneExpandoChain(aCx, newobj, aObj)) {
return NS_ERROR_FAILURE;
aError.StealExceptionFromJSContext(aCx);
return;
}
// We've set up |newobj|, so we make it own the native by setting its reserved
@ -2244,9 +2252,6 @@ ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
if (htmlobject) {
htmlobject->SetupProtoChain(aCx, aObj);
}
// Now we can just return the wrapper
return NS_OK;
}
GlobalObject::GlobalObject(JSContext* aCx, JSObject* aObject)

View file

@ -2758,8 +2758,8 @@ const nsAString& NonNullHelper(const binding_detail::FakeString& aArg)
// Reparent the wrapper of aObj to whatever its native now thinks its
// parent should be.
nsresult
ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObj);
void
ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObj, ErrorResult& aError);
/**
* Used to implement the Symbol.hasInstance property of an interface object.

View file

@ -116,10 +116,9 @@ ContentEventHandler::InitBasic()
// If text frame which has overflowing selection underline is dirty,
// we need to flush the pending reflow here.
mPresShell->FlushPendingNotifications(Flush_Layout);
// Flushing notifications can cause mPresShell to be destroyed (bug 577963).
NS_ENSURE_TRUE(!mPresShell->IsDestroying(), NS_ERROR_FAILURE);
if (nsIDocument* doc = mPresShell->GetDocument()) {
doc->FlushPendingNotifications(Flush_Layout);
}
return NS_OK;
}
@ -170,11 +169,11 @@ ContentEventHandler::InitRootContent(Selection* aNormalSelection)
}
// See bug 537041 comment 5, the range could have removed node.
if (NS_WARN_IF(startNode->GetUncomposedDoc() != mPresShell->GetDocument())) {
if (NS_WARN_IF(startNode->GetComposedDoc() != mPresShell->GetDocument())) {
return NS_ERROR_FAILURE;
}
NS_ASSERTION(startNode->GetUncomposedDoc() == endNode->GetUncomposedDoc(),
NS_ASSERTION(startNode->GetComposedDoc() == endNode->GetComposedDoc(),
"firstNormalSelectionRange crosses the document boundary");
mRootContent = startNode->GetSelectionRootContent(mPresShell);
@ -2882,31 +2881,6 @@ ContentEventHandler::AdjustCollapsedRangeMaybeIntoTextNode(nsRange* aRange)
return NS_OK;
}
nsresult
ContentEventHandler::GetStartFrameAndOffset(const nsRange* aRange,
nsIFrame*& aFrame,
int32_t& aOffsetInFrame)
{
MOZ_ASSERT(aRange);
aFrame = nullptr;
aOffsetInFrame = -1;
nsINode* node = aRange->GetStartParent();
if (NS_WARN_IF(!node) ||
NS_WARN_IF(!node->IsNodeOfType(nsINode::eCONTENT))) {
return NS_ERROR_FAILURE;
}
nsIContent* content = static_cast<nsIContent*>(node);
RefPtr<nsFrameSelection> fs = mPresShell->FrameSelection();
aFrame = fs->GetFrameForNodeOffset(content, aRange->StartOffset(),
fs->GetHint(), &aOffsetInFrame);
if (NS_WARN_IF(!aFrame)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
nsresult
ContentEventHandler::ConvertToRootRelativeOffset(nsIFrame* aFrame,
nsRect& aRect)

View file

@ -287,10 +287,6 @@ protected:
// If the aRange isn't in text node but next to a text node, this method
// modifies it in the text node. Otherwise, not modified.
nsresult AdjustCollapsedRangeMaybeIntoTextNode(nsRange* aCollapsedRange);
// Find the first frame for the range and get the start offset in it.
nsresult GetStartFrameAndOffset(const nsRange* aRange,
nsIFrame*& aFrame,
int32_t& aOffsetInFrame);
// Convert the frame relative offset to be relative to the root frame of the
// root presContext (but still measured in appUnits of aFrame's presContext).
nsresult ConvertToRootRelativeOffset(nsIFrame* aFrame,

View file

@ -148,6 +148,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Event)
tmp->mEvent->mCurrentTarget = nullptr;
tmp->mEvent->mOriginalTarget = nullptr;
tmp->mEvent->mRelatedTarget = nullptr;
tmp->mEvent->mOriginalRelatedTarget = nullptr;
switch (tmp->mEvent->mClass) {
case eDragEventClass: {
WidgetDragEvent* dragEvent = tmp->mEvent->AsDragEvent();
@ -176,6 +177,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Event)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mCurrentTarget)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mOriginalTarget)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mRelatedTarget)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mOriginalRelatedTarget)
switch (tmp->mEvent->mClass) {
case eDragEventClass: {
WidgetDragEvent* dragEvent = tmp->mEvent->AsDragEvent();
@ -555,6 +557,26 @@ Event::SetEventType(const nsAString& aEventTypeArg)
mEvent->SetDefaultComposedInNativeAnonymousContent();
}
already_AddRefed<EventTarget>
Event::EnsureWebAccessibleRelatedTarget(EventTarget* aRelatedTarget)
{
nsCOMPtr<EventTarget> relatedTarget = aRelatedTarget;
if (relatedTarget) {
nsCOMPtr<nsIContent> content = do_QueryInterface(relatedTarget);
if (content && content->ChromeOnlyAccess() &&
!nsContentUtils::CanAccessNativeAnon()) {
content = content->FindFirstNonChromeOnlyAccessContent();
relatedTarget = do_QueryInterface(content);
}
if (relatedTarget) {
relatedTarget = relatedTarget->GetTargetForDOMEvent();
}
}
return relatedTarget.forget();
}
void
Event::InitEvent(const nsAString& aEventTypeArg,
bool aCanBubbleArg,
@ -1227,44 +1249,6 @@ Event::SetOwner(EventTarget* aOwner)
#endif
}
// static
nsIContent*
Event::GetShadowRelatedTarget(nsIContent* aCurrentTarget,
nsIContent* aRelatedTarget)
{
if (!aCurrentTarget || !aRelatedTarget) {
return nullptr;
}
// Walk up the ancestor node trees of the related target until
// we encounter the node tree of the current target in order
// to find the adjusted related target. Walking up the tree may
// not find a common ancestor node tree if the related target is in
// an ancestor tree, but in that case it does not need to be adjusted.
ShadowRoot* currentTargetShadow = aCurrentTarget->GetContainingShadow();
if (!currentTargetShadow) {
return nullptr;
}
nsIContent* relatedTarget = aCurrentTarget;
while (relatedTarget) {
ShadowRoot* ancestorShadow = relatedTarget->GetContainingShadow();
if (currentTargetShadow == ancestorShadow) {
return relatedTarget;
}
// Didn't find the ancestor tree, thus related target does not have to
// adjusted.
if (!ancestorShadow) {
return nullptr;
}
relatedTarget = ancestorShadow->GetHost();
}
return nullptr;
}
NS_IMETHODIMP
Event::GetCancelBubble(bool* aCancelBubble)
{

View file

@ -251,14 +251,6 @@ public:
return mIsMainThreadEvent;
}
/**
* For a given current target, returns the related target adjusted with
* shadow DOM retargeting rules. Returns nullptr if related target
* is not adjusted.
*/
static nsIContent* GetShadowRelatedTarget(nsIContent* aCurrentTarget,
nsIContent* aRelatedTarget);
void MarkUninitialized()
{
mEvent->mMessage = eVoidEvent;
@ -295,6 +287,9 @@ protected:
mEvent->SetComposed(aComposed);
}
already_AddRefed<EventTarget>
EnsureWebAccessibleRelatedTarget(EventTarget* aRelatedTarget);
mozilla::WidgetEvent* mEvent;
RefPtr<nsPresContext> mPresContext;
nsCOMPtr<EventTarget> mExplicitOriginalTarget;

View file

@ -204,6 +204,16 @@ public:
mNewTarget = aNewTarget;
}
EventTarget* GetRetargetedRelatedTarget()
{
return mRetargetedRelatedTarget;
}
void SetRetargetedRelatedTarget(EventTarget* aTarget)
{
mRetargetedRelatedTarget = aTarget;
}
void SetForceContentDispatch(bool aForce)
{
mFlags.mForceContentDispatch = aForce;
@ -354,6 +364,7 @@ public:
private:
nsCOMPtr<EventTarget> mTarget;
nsCOMPtr<EventTarget> mRetargetedRelatedTarget;
class EventTargetChainFlags
{
@ -422,6 +433,7 @@ EventTargetChainItem::GetEventTargetParent(EventChainPreVisitor& aVisitor)
SetWantsPreHandleEvent(aVisitor.mWantsPreHandleEvent);
SetPreHandleEventOnly(aVisitor.mWantsPreHandleEvent && !aVisitor.mCanHandle);
SetRootOfClosedTree(aVisitor.mRootOfClosedTree);
SetRetargetedRelatedTarget(aVisitor.mRetargetedRelatedTarget);
mItemFlags = aVisitor.mItemFlags;
mItemData = aVisitor.mItemData;
}
@ -454,6 +466,7 @@ EventTargetChainItem::HandleEventTargetChain(
{
// Save the target so that it can be restored later.
nsCOMPtr<EventTarget> firstTarget = aVisitor.mEvent->mTarget;
nsCOMPtr<EventTarget> firstRelatedTarget = aVisitor.mEvent->mRelatedTarget;
uint32_t chainLength = aChain.Length();
uint32_t firstCanHandleEventTargetIdx =
EventTargetChainItem::GetFirstCanHandleEventTargetIdx(aChain);
@ -483,6 +496,30 @@ EventTargetChainItem::HandleEventTargetChain(
}
}
}
// https://dom.spec.whatwg.org/#dispatching-events
// Step 14.2
// "Set event's relatedTarget to tuple's relatedTarget."
// Note, the initial retargeting was done already when creating
// event target chain, so we need to do this only after calling
// HandleEvent, not before, like in the specification.
if (item.GetRetargetedRelatedTarget()) {
bool found = false;
for (uint32_t j = i; j > 0; --j) {
uint32_t childIndex = j - 1;
EventTarget* relatedTarget =
aChain[childIndex].GetRetargetedRelatedTarget();
if (relatedTarget) {
found = true;
aVisitor.mEvent->mRelatedTarget = relatedTarget;
break;
}
}
if (!found) {
aVisitor.mEvent->mRelatedTarget =
aVisitor.mEvent->mOriginalRelatedTarget;
}
}
}
// Target
@ -511,6 +548,14 @@ EventTargetChainItem::HandleEventTargetChain(
aVisitor.mEvent->mTarget = newTarget;
}
// https://dom.spec.whatwg.org/#dispatching-events
// Step 15.2
// "Set event's relatedTarget to tuple's relatedTarget."
EventTarget* relatedTarget = item.GetRetargetedRelatedTarget();
if (relatedTarget) {
aVisitor.mEvent->mRelatedTarget = relatedTarget;
}
if (aVisitor.mEvent->mFlags.mBubbles || newTarget) {
if ((!aVisitor.mEvent->mFlags.mNoContentDispatch ||
item.ForceContentDispatch()) &&
@ -542,6 +587,7 @@ EventTargetChainItem::HandleEventTargetChain(
// Retarget for system event group (which does the default handling too).
// Setting back the target which was used also for default event group.
aVisitor.mEvent->mTarget = firstTarget;
aVisitor.mEvent->mRelatedTarget = aVisitor.mEvent->mOriginalRelatedTarget;
aVisitor.mEvent->mFlags.mInSystemGroup = true;
HandleEventTargetChain(aChain,
aVisitor,
@ -599,6 +645,7 @@ MayRetargetToChromeIfCanNotHandleEvent(
EventTargetChainItem::DestroyLast(aChain, aTargetEtci);
}
if (aPreVisitor.mAutomaticChromeDispatch && aContent) {
aPreVisitor.mRelatedTargetRetargetedInCurrentScope = false;
// Event target couldn't handle the event. Try to propagate to chrome.
EventTargetChainItem* chromeTargetEtci =
EventTargetChainItemForChromeTarget(aChain, aContent, aChildEtci);
@ -762,9 +809,10 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
aEvent->mOriginalTarget = aEvent->mTarget;
}
aEvent->mOriginalRelatedTarget = aEvent->mRelatedTarget;
nsCOMPtr<nsIContent> content = do_QueryInterface(aEvent->mOriginalTarget);
bool isInAnon = (content && (content->IsInAnonymousSubtree() ||
content->IsInShadowTree()));
bool isInAnon = (content && content->IsInAnonymousSubtree());
aEvent->mFlags.mIsBeingDispatched = true;
@ -772,7 +820,7 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
// GetEventTargetParent for the original target.
nsEventStatus status = aEventStatus ? *aEventStatus : nsEventStatus_eIgnore;
EventChainPreVisitor preVisitor(aPresContext, aEvent, aDOMEvent, status,
isInAnon);
isInAnon, aEvent->mTarget);
targetEtci->GetEventTargetParent(preVisitor);
if (!preVisitor.mCanHandle) {
@ -810,14 +858,21 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
if (preVisitor.mEventTargetAtParent) {
// Need to set the target of the event
// so that also the next retargeting works.
preVisitor.mTargetInKnownToBeHandledScope = preVisitor.mEvent->mTarget;
preVisitor.mEvent->mTarget = preVisitor.mEventTargetAtParent;
parentEtci->SetNewTarget(preVisitor.mEventTargetAtParent);
}
if (preVisitor.mRetargetedRelatedTarget) {
preVisitor.mEvent->mRelatedTarget = preVisitor.mRetargetedRelatedTarget;
}
parentEtci->GetEventTargetParent(preVisitor);
if (preVisitor.mCanHandle) {
preVisitor.mTargetInKnownToBeHandledScope = preVisitor.mEvent->mTarget;
topEtci = parentEtci;
} else {
bool ignoreBecauseOfShadowDOM = preVisitor.mIgnoreBecauseOfShadowDOM;
nsCOMPtr<nsINode> disabledTarget = do_QueryInterface(parentTarget);
parentEtci = MayRetargetToChromeIfCanNotHandleEvent(chain,
preVisitor,
@ -825,9 +880,14 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
topEtci,
disabledTarget);
if (parentEtci && preVisitor.mCanHandle) {
preVisitor.mTargetInKnownToBeHandledScope = preVisitor.mEvent->mTarget;
EventTargetChainItem* item =
EventTargetChainItem::GetFirstCanHandleEventTarget(chain);
item->SetNewTarget(parentTarget);
if (!ignoreBecauseOfShadowDOM) {
// If we ignored the target because of Shadow DOM retargeting, we
// shouldn't treat the target to be in the event path at all.
item->SetNewTarget(parentTarget);
}
topEtci = parentEtci;
continue;
}
@ -871,6 +931,19 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
aEvent->mFlags.mIsBeingDispatched = false;
aEvent->mFlags.mDispatchedAtLeastOnce = true;
// https://dom.spec.whatwg.org/#concept-event-dispatch
// Step 18
// "If target's root is a shadow root, then set event's target attribute and
// event's relatedTarget to null."
nsCOMPtr<nsIContent> finalTarget = do_QueryInterface(aEvent->mTarget);
// TODO: replace with IsShadowRoot() check once bug 1427511 lands.
if (finalTarget && ShadowRoot::FromNode(finalTarget->SubtreeRoot())) {
aEvent->mTarget = nullptr;
aEvent->mOriginalTarget = nullptr;
aEvent->mRelatedTarget = nullptr;
aEvent->mOriginalRelatedTarget = nullptr;
}
if (!externalDOMEvent && preVisitor.mDOMEvent) {
// An dom::Event was created while dispatching the event.
// Duplicate private data if someone holds a pointer to it.

View file

@ -113,7 +113,8 @@ public:
WidgetEvent* aEvent,
nsIDOMEvent* aDOMEvent,
nsEventStatus aEventStatus,
bool aIsInAnon)
bool aIsInAnon,
dom::EventTarget* aTargetInKnownToBeHandledScope)
: EventChainVisitor(aPresContext, aEvent, aDOMEvent, aEventStatus)
, mCanHandle(true)
, mAutomaticChromeDispatch(true)
@ -126,8 +127,12 @@ public:
, mRootOfClosedTree(false)
, mParentIsSlotInClosedTree(false)
, mParentIsChromeHandler(false)
, mRelatedTargetRetargetedInCurrentScope(false)
, mIgnoreBecauseOfShadowDOM(false)
, mParentTarget(nullptr)
, mEventTargetAtParent(nullptr)
, mRetargetedRelatedTarget(nullptr)
, mTargetInKnownToBeHandledScope(aTargetInKnownToBeHandledScope)
{
}
@ -144,8 +149,13 @@ public:
mRootOfClosedTree = false;
mParentIsSlotInClosedTree = false;
mParentIsChromeHandler = false;
// Note, we don't clear mRelatedTargetRetargetedInCurrentScope explicitly,
// since it is used during event path creation to indicate whether
// relatedTarget may need to be retargeted.
mIgnoreBecauseOfShadowDOM = false;
mParentTarget = nullptr;
mEventTargetAtParent = nullptr;
mRetargetedRelatedTarget = nullptr;
}
dom::EventTarget* GetParentTarget()
@ -161,6 +171,14 @@ public:
}
}
void IgnoreCurrentTargetBecauseOfShadowDOMRetargeting()
{
mCanHandle = false;
mIgnoreBecauseOfShadowDOM = true;
SetParentTarget(nullptr, false);
mEventTargetAtParent = nullptr;
}
/**
* Member that must be set in GetEventTargetParent by event targets. If set to
* false, indicates that this event target will not be handling the event and
@ -229,6 +247,19 @@ public:
*/
bool mParentIsChromeHandler;
/**
* True if event's related target has been already retargeted in the
* current 'scope'. This should be set to false initially and whenever
* event path creation crosses shadow boundary.
*/
bool mRelatedTargetRetargetedInCurrentScope;
/**
* True if Shadow DOM relatedTarget retargeting causes the current item
* to not show up in the event path.
*/
bool mIgnoreBecauseOfShadowDOM;
private:
/**
* Parent item in the event target chain.
@ -241,6 +272,19 @@ public:
* which should be used when the event is handled at mParentTarget.
*/
dom::EventTarget* mEventTargetAtParent;
/**
* If the related target of the event needs to be retargeted, set this
* to a new EventTarget.
*/
dom::EventTarget* mRetargetedRelatedTarget;
/**
* Set to the value of mEvent->mTarget of the previous scope in case of
* Shadow DOM or such, and if there is no anonymous content this just points
* to the initial target.
*/
dom::EventTarget* mTargetInKnownToBeHandledScope;
};
class EventChainPostVisitor : public mozilla::EventChainVisitor

View file

@ -30,14 +30,14 @@ NS_IMETHODIMP
FocusEvent::GetRelatedTarget(nsIDOMEventTarget** aRelatedTarget)
{
NS_ENSURE_ARG_POINTER(aRelatedTarget);
NS_IF_ADDREF(*aRelatedTarget = GetRelatedTarget());
*aRelatedTarget = GetRelatedTarget().take();
return NS_OK;
}
EventTarget*
already_AddRefed<EventTarget>
FocusEvent::GetRelatedTarget()
{
return mEvent->AsFocusEvent()->mRelatedTarget;
return EnsureWebAccessibleRelatedTarget(mEvent->AsFocusEvent()->mRelatedTarget);
}
void

View file

@ -32,7 +32,7 @@ public:
nsPresContext* aPresContext,
InternalFocusEvent* aEvent);
EventTarget* GetRelatedTarget();
already_AddRefed<EventTarget> GetRelatedTarget();
static already_AddRefed<FocusEvent> Constructor(const GlobalObject& aGlobal,
const nsAString& aType,

View file

@ -300,27 +300,7 @@ MouseEvent::GetRelatedTarget()
break;
}
if (relatedTarget) {
nsCOMPtr<nsIContent> content = do_QueryInterface(relatedTarget);
nsCOMPtr<nsIContent> currentTarget =
do_QueryInterface(mEvent->mCurrentTarget);
nsIContent* shadowRelatedTarget = GetShadowRelatedTarget(currentTarget, content);
if (shadowRelatedTarget) {
relatedTarget = shadowRelatedTarget;
}
if (content && content->ChromeOnlyAccess() &&
!nsContentUtils::CanAccessNativeAnon()) {
relatedTarget = do_QueryInterface(content->FindFirstNonChromeOnlyAccessContent());
}
if (relatedTarget) {
relatedTarget = relatedTarget->GetTargetForDOMEvent();
}
return relatedTarget.forget();
}
return nullptr;
return EnsureWebAccessibleRelatedTarget(relatedTarget);
}
void

View file

@ -623,8 +623,9 @@ HTMLImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
}
if (HaveSrcsetOrInPicture()) {
if (aDocument && !mInDocResponsiveContent) {
aDocument->AddResponsiveContent(this);
nsIDocument* doc = GetComposedDoc();
if (doc && !mInDocResponsiveContent) {
doc->AddResponsiveContent(this);
mInDocResponsiveContent = true;
}

View file

@ -162,7 +162,8 @@ HTMLLinkElement::HasDeferredDNSPrefetchRequest()
}
nsresult
HTMLLinkElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
HTMLLinkElement::BindToTree(nsIDocument* aDocument,
nsIContent* aParent,
nsIContent* aBindingParent,
bool aCompileEventHandlers)
{
@ -173,12 +174,8 @@ HTMLLinkElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
aCompileEventHandlers);
NS_ENSURE_SUCCESS(rv, rv);
// Link must be inert in ShadowRoot.
if (aDocument && !GetContainingShadow()) {
aDocument->RegisterPendingLinkUpdate(this);
}
if (IsInComposedDoc()) {
if (nsIDocument* doc = GetComposedDoc()) {
doc->RegisterPendingLinkUpdate(this);
TryDNSPrefetchPreconnectOrPrefetch();
}
@ -222,11 +219,7 @@ HTMLLinkElement::UnbindFromTree(bool aDeep, bool aNullParent)
// If this is reinserted back into the document it will not be
// from the parser.
nsCOMPtr<nsIDocument> oldDoc = GetUncomposedDoc();
// Check for a ShadowRoot because link elements are inert in a
// ShadowRoot.
ShadowRoot* oldShadowRoot = GetBindingParent() ?
GetBindingParent()->GetShadowRoot() : nullptr;
ShadowRoot* oldShadowRoot = GetContainingShadow();
OwnerDoc()->UnregisterPendingLinkUpdate(this);

View file

@ -463,7 +463,7 @@ nsGenericHTMLElement::IntrinsicState() const
uint32_t
nsGenericHTMLElement::EditableInclusiveDescendantCount()
{
bool isEditable = IsInUncomposedDoc() && HasFlag(NODE_IS_EDITABLE) &&
bool isEditable = IsInComposedDoc() && HasFlag(NODE_IS_EDITABLE) &&
GetContentEditableValue() == eTrue;
return EditableDescendantCount() + isEditable;
}
@ -484,12 +484,14 @@ nsGenericHTMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
aDocument->
AddToNameTable(this, GetParsedAttr(nsGkAtoms::name)->GetAtomValue());
}
}
if (HasFlag(NODE_IS_EDITABLE) && GetContentEditableValue() == eTrue) {
nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(aDocument);
if (htmlDocument) {
htmlDocument->ChangeContentEditableCount(this, +1);
}
if (HasFlag(NODE_IS_EDITABLE) && GetContentEditableValue() == eTrue &&
IsInComposedDoc()) {
nsCOMPtr<nsIHTMLDocument> htmlDocument =
do_QueryInterface(GetComposedDoc());
if (htmlDocument) {
htmlDocument->ChangeContentEditableCount(this, +1);
}
}
@ -514,8 +516,7 @@ nsGenericHTMLElement::UnbindFromTree(bool aDeep, bool aNullParent)
RemoveFromNameTable();
if (GetContentEditableValue() == eTrue) {
//XXXsmaug Fix this for Shadow DOM, bug 1066965.
nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(GetUncomposedDoc());
nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(GetComposedDoc());
if (htmlDocument) {
htmlDocument->ChangeContentEditableCount(this, -1);
}
@ -2774,8 +2775,7 @@ MakeContentDescendantsEditable(nsIContent *aContent, nsIDocument *aDocument)
void
nsGenericHTMLElement::ChangeEditableState(int32_t aChange)
{
//XXXsmaug Fix this for Shadow DOM, bug 1066965.
nsIDocument* document = GetUncomposedDoc();
nsIDocument* document = GetComposedDoc();
if (!document) {
return;
}
@ -2788,7 +2788,8 @@ nsGenericHTMLElement::ChangeEditableState(int32_t aChange)
}
nsIContent* parent = GetParent();
while (parent) {
// Don't update across Shadow DOM boundary.
while (parent && parent->IsElement()) {
parent->ChangeEditableDescendantCount(aChange);
parent = parent->GetParent();
}

View file

@ -0,0 +1,52 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1430305
Bug 1430305 - Implement ShadowRoot.fullscreenElement
-->
<head>
<title>Bug 1430305</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js">
</script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js">
</script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=1430305">
Mozilla Bug 1430305</a>
<div id="host"></div>
<pre id="test">
<script type="application/javascript">
function begin() {
var host = document.getElementById("host");
var shadowRoot = host.attachShadow({mode: "open"});
shadowRoot.innerHTML = "<div>test</div>";
var elem = shadowRoot.firstChild;
var gotFullscreenEvent = false;
document.addEventListener("fullscreenchange", function (e) {
if (document.fullscreenElement === host) {
is(shadowRoot.fullscreenElement, elem,
"Expected element entered fullsceen");
gotFullscreenEvent = true;
document.exitFullscreen();
} else {
opener.ok(gotFullscreenEvent, "Entered fullscreen as expected");
is(shadowRoot.fullscreenElement, null,
"Shouldn't have fullscreenElement anymore.");
is(document.fullscreenElement, null,
"Shouldn't have fullscreenElement anymore.");
opener.nextTest();
}
});
elem.requestFullscreen();
}
</script>
</pre>
</body>
</html>

View file

@ -72,6 +72,7 @@ support-files =
file_fullscreen-rollback.html
file_fullscreen-scrollbar.html
file_fullscreen-selector.html
file_fullscreen-shadowdom.html
file_fullscreen-svg-element.html
file_fullscreen-top-layer.html
file_fullscreen-unprefix-disabled-inner.html
@ -444,6 +445,7 @@ support-files =
[test_bug1146116.html]
[test_bug1264157.html]
[test_bug1287321.html]
[test_bug1323815.html]
[test_change_crossorigin.html]
[test_checked.html]
[test_dir_attributes_reflection.html]

View file

@ -0,0 +1,50 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1323815
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1323815</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
/** Test for Bug 1323815 **/
SimpleTest.waitForExplicitFinish();
function test() {
var n = document.getElementById("number");
var t = document.getElementById("text");
t.focus();
var gotBlur = false;
t.onblur = function(e) {
try {
is(e.relatedTarget.localName, "input");
} catch(ex) {
ok(false, "Accessing properties on the relatedTarget shouldn't throw! " + ex);
}
gotBlur = true;
}
n.focus();
ok(gotBlur);
SimpleTest.finish();
}
SimpleTest.waitForFocus(test);
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1323815">Mozilla Bug 1323815</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
<input type="number" id="number"><input type="text" id="text">
</body>
</html>

View file

@ -39,6 +39,7 @@ var gTestWindows = [
"file_fullscreen-navigation.html",
"file_fullscreen-scrollbar.html",
"file_fullscreen-selector.html",
"file_fullscreen-shadowdom.html",
"file_fullscreen-top-layer.html",
"file_fullscreen-backdrop.html",
"file_fullscreen-nested.html",
@ -96,7 +97,8 @@ addLoadEvent(function() {
["full-screen-api.unprefix.enabled", true],
["full-screen-api.allow-trusted-requests-only", false],
["full-screen-api.transition-duration.enter", "0 0"],
["full-screen-api.transition-duration.leave", "0 0"]
["full-screen-api.transition-duration.leave", "0 0"],
["dom.webcomponents.enabled", true]
]}, nextTest);
});
SimpleTest.waitForExplicitFinish();

View file

@ -110,7 +110,7 @@ public:
MOZ_ASSERT(mLoggingInfo.nextVersionChangeTransactionSerialNumber() >
INT64_MIN);
if (aMode == IDBTransaction::VERSION_CHANGE) {
if (aMode == IDBTransaction::Mode::VersionChange) {
return mLoggingInfo.nextVersionChangeTransactionSerialNumber()--;
}

View file

@ -9005,7 +9005,7 @@ public:
MOZ_ASSERT(mLoggingInfo.nextVersionChangeTransactionSerialNumber() >
INT64_MIN);
if (aMode == IDBTransaction::VERSION_CHANGE) {
if (aMode == IDBTransaction::Mode::VersionChange) {
return mLoggingInfo.nextVersionChangeTransactionSerialNumber()--;
}
@ -11330,11 +11330,11 @@ AutoSavepoint::~AutoSavepoint()
if (mConnection) {
mConnection->AssertIsOnConnectionThread();
MOZ_ASSERT(mDEBUGTransaction);
MOZ_ASSERT(mDEBUGTransaction->GetMode() == IDBTransaction::READ_WRITE ||
MOZ_ASSERT(mDEBUGTransaction->GetMode() == IDBTransaction::Mode::ReadWrite ||
mDEBUGTransaction->GetMode() ==
IDBTransaction::READ_WRITE_FLUSH ||
mDEBUGTransaction->GetMode() == IDBTransaction::CLEANUP ||
mDEBUGTransaction->GetMode() == IDBTransaction::VERSION_CHANGE);
IDBTransaction::Mode::ReadWriteFlush ||
mDEBUGTransaction->GetMode() == IDBTransaction::Mode::Cleanup ||
mDEBUGTransaction->GetMode() == IDBTransaction::Mode::VersionChange);
if (NS_FAILED(mConnection->RollbackSavepoint())) {
NS_WARNING("Failed to rollback savepoint!");
@ -11347,10 +11347,10 @@ DatabaseConnection::
AutoSavepoint::Start(const TransactionBase* aTransaction)
{
MOZ_ASSERT(aTransaction);
MOZ_ASSERT(aTransaction->GetMode() == IDBTransaction::READ_WRITE ||
aTransaction->GetMode() == IDBTransaction::READ_WRITE_FLUSH ||
aTransaction->GetMode() == IDBTransaction::CLEANUP ||
aTransaction->GetMode() == IDBTransaction::VERSION_CHANGE);
MOZ_ASSERT(aTransaction->GetMode() == IDBTransaction::Mode::ReadWrite ||
aTransaction->GetMode() == IDBTransaction::Mode::ReadWriteFlush ||
aTransaction->GetMode() == IDBTransaction::Mode::Cleanup ||
aTransaction->GetMode() == IDBTransaction::Mode::VersionChange);
DatabaseConnection* connection = aTransaction->GetDatabase()->GetConnection();
MOZ_ASSERT(connection);
@ -14478,19 +14478,19 @@ Database::AllocPBackgroundIDBTransactionParent(
return nullptr;
}
if (NS_WARN_IF(aMode != IDBTransaction::READ_ONLY &&
aMode != IDBTransaction::READ_WRITE &&
aMode != IDBTransaction::READ_WRITE_FLUSH &&
aMode != IDBTransaction::CLEANUP)) {
if (NS_WARN_IF(aMode != IDBTransaction::Mode::ReadOnly &&
aMode != IDBTransaction::Mode::ReadWrite &&
aMode != IDBTransaction::Mode::ReadWriteFlush &&
aMode != IDBTransaction::Mode::Cleanup)) {
ASSERT_UNLESS_FUZZING();
return nullptr;
}
// If this is a readwrite transaction to a chrome database make sure the child
// has write access.
if (NS_WARN_IF((aMode == IDBTransaction::READ_WRITE ||
aMode == IDBTransaction::READ_WRITE_FLUSH ||
aMode == IDBTransaction::CLEANUP) &&
if (NS_WARN_IF((aMode == IDBTransaction::Mode::ReadWrite ||
aMode == IDBTransaction::Mode::ReadWriteFlush ||
aMode == IDBTransaction::Mode::Cleanup) &&
mPrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo &&
!mChromeWriteAccessAllowed)) {
return nullptr;
@ -14553,10 +14553,10 @@ Database::RecvPBackgroundIDBTransactionConstructor(
AssertIsOnBackgroundThread();
MOZ_ASSERT(aActor);
MOZ_ASSERT(!aObjectStoreNames.IsEmpty());
MOZ_ASSERT(aMode == IDBTransaction::READ_ONLY ||
aMode == IDBTransaction::READ_WRITE ||
aMode == IDBTransaction::READ_WRITE_FLUSH ||
aMode == IDBTransaction::CLEANUP);
MOZ_ASSERT(aMode == IDBTransaction::Mode::ReadOnly ||
aMode == IDBTransaction::Mode::ReadWrite ||
aMode == IDBTransaction::Mode::ReadWriteFlush ||
aMode == IDBTransaction::Mode::Cleanup);
MOZ_ASSERT(!mClosed);
if (IsInvalidated()) {
@ -14578,7 +14578,7 @@ Database::RecvPBackgroundIDBTransactionConstructor(
mMetadata->mDatabaseId,
transaction->LoggingSerialNumber(),
aObjectStoreNames,
aMode != IDBTransaction::READ_ONLY);
aMode != IDBTransaction::Mode::ReadOnly);
transaction->SetActive(transactionId);
@ -14715,14 +14715,14 @@ StartTransactionOp::DoDatabaseWork(DatabaseConnection* aConnection)
Transaction()->SetActiveOnConnectionThread();
if (Transaction()->GetMode() == IDBTransaction::CLEANUP) {
if (Transaction()->GetMode() == IDBTransaction::Mode::Cleanup) {
nsresult rv = aConnection->DisableQuotaChecks();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
if (Transaction()->GetMode() != IDBTransaction::READ_ONLY) {
if (Transaction()->GetMode() != IDBTransaction::Mode::ReadOnly) {
nsresult rv = aConnection->BeginWriteTransaction();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
@ -15032,10 +15032,10 @@ TransactionBase::VerifyRequestParams(const RequestParams& aParams) const
}
case RequestParams::TObjectStoreDeleteParams: {
if (NS_WARN_IF(mMode != IDBTransaction::READ_WRITE &&
mMode != IDBTransaction::READ_WRITE_FLUSH &&
mMode != IDBTransaction::CLEANUP &&
mMode != IDBTransaction::VERSION_CHANGE)) {
if (NS_WARN_IF(mMode != IDBTransaction::Mode::ReadWrite &&
mMode != IDBTransaction::Mode::ReadWriteFlush &&
mMode != IDBTransaction::Mode::Cleanup &&
mMode != IDBTransaction::Mode::VersionChange)) {
ASSERT_UNLESS_FUZZING();
return false;
}
@ -15056,10 +15056,10 @@ TransactionBase::VerifyRequestParams(const RequestParams& aParams) const
}
case RequestParams::TObjectStoreClearParams: {
if (NS_WARN_IF(mMode != IDBTransaction::READ_WRITE &&
mMode != IDBTransaction::READ_WRITE_FLUSH &&
mMode != IDBTransaction::CLEANUP &&
mMode != IDBTransaction::VERSION_CHANGE)) {
if (NS_WARN_IF(mMode != IDBTransaction::Mode::ReadWrite &&
mMode != IDBTransaction::Mode::ReadWriteFlush &&
mMode != IDBTransaction::Mode::Cleanup &&
mMode != IDBTransaction::Mode::VersionChange)) {
ASSERT_UNLESS_FUZZING();
return false;
}
@ -15243,9 +15243,9 @@ TransactionBase::VerifyRequestParams(const ObjectStoreAddPutParams& aParams)
{
AssertIsOnBackgroundThread();
if (NS_WARN_IF(mMode != IDBTransaction::READ_WRITE &&
mMode != IDBTransaction::READ_WRITE_FLUSH &&
mMode != IDBTransaction::VERSION_CHANGE)) {
if (NS_WARN_IF(mMode != IDBTransaction::Mode::ReadWrite &&
mMode != IDBTransaction::Mode::ReadWriteFlush &&
mMode != IDBTransaction::Mode::VersionChange)) {
ASSERT_UNLESS_FUZZING();
return false;
}
@ -15864,7 +15864,7 @@ NormalTransaction::DeallocPBackgroundIDBCursorParent(
VersionChangeTransaction::VersionChangeTransaction(
OpenDatabaseOp* aOpenDatabaseOp)
: TransactionBase(aOpenDatabaseOp->mDatabase,
IDBTransaction::VERSION_CHANGE)
IDBTransaction::Mode::VersionChange)
, mOpenDatabaseOp(aOpenDatabaseOp)
, mActorWasAlive(false)
{
@ -22234,7 +22234,7 @@ OpenDatabaseOp::DispatchToWorkThread()
MOZ_ASSERT(mState == State::WaitingForTransactionsToComplete);
MOZ_ASSERT(mVersionChangeTransaction);
MOZ_ASSERT(mVersionChangeTransaction->GetMode() ==
IDBTransaction::VERSION_CHANGE);
IDBTransaction::Mode::VersionChange);
MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
@ -23739,10 +23739,10 @@ CommitOp::WriteAutoIncrementCounts()
{
MOZ_ASSERT(mTransaction);
mTransaction->AssertIsOnConnectionThread();
MOZ_ASSERT(mTransaction->GetMode() == IDBTransaction::READ_WRITE ||
mTransaction->GetMode() == IDBTransaction::READ_WRITE_FLUSH ||
mTransaction->GetMode() == IDBTransaction::CLEANUP ||
mTransaction->GetMode() == IDBTransaction::VERSION_CHANGE);
MOZ_ASSERT(mTransaction->GetMode() == IDBTransaction::Mode::ReadWrite ||
mTransaction->GetMode() == IDBTransaction::Mode::ReadWriteFlush ||
mTransaction->GetMode() == IDBTransaction::Mode::Cleanup ||
mTransaction->GetMode() == IDBTransaction::Mode::VersionChange);
const nsTArray<RefPtr<FullObjectStoreMetadata>>& metadataArray =
mTransaction->mModifiedAutoIncrementObjectStoreMetadataArray;
@ -23807,10 +23807,10 @@ CommitOp::CommitOrRollbackAutoIncrementCounts()
{
MOZ_ASSERT(mTransaction);
mTransaction->AssertIsOnConnectionThread();
MOZ_ASSERT(mTransaction->GetMode() == IDBTransaction::READ_WRITE ||
mTransaction->GetMode() == IDBTransaction::READ_WRITE_FLUSH ||
mTransaction->GetMode() == IDBTransaction::CLEANUP ||
mTransaction->GetMode() == IDBTransaction::VERSION_CHANGE);
MOZ_ASSERT(mTransaction->GetMode() == IDBTransaction::Mode::ReadWrite ||
mTransaction->GetMode() == IDBTransaction::Mode::ReadWriteFlush ||
mTransaction->GetMode() == IDBTransaction::Mode::Cleanup ||
mTransaction->GetMode() == IDBTransaction::Mode::VersionChange);
nsTArray<RefPtr<FullObjectStoreMetadata>>& metadataArray =
mTransaction->mModifiedAutoIncrementObjectStoreMetadataArray;
@ -23841,7 +23841,7 @@ CommitOp::AssertForeignKeyConsistency(DatabaseConnection* aConnection)
MOZ_ASSERT(aConnection);
MOZ_ASSERT(mTransaction);
mTransaction->AssertIsOnConnectionThread();
MOZ_ASSERT(mTransaction->GetMode() != IDBTransaction::READ_ONLY);
MOZ_ASSERT(mTransaction->GetMode() != IDBTransaction::Mode::ReadOnly);
DatabaseConnection::CachedStatement pragmaStmt;
MOZ_ALWAYS_SUCCEEDS(
@ -23891,7 +23891,7 @@ CommitOp::Run()
mTransaction->LoggingSerialNumber(),
mLoggingSerialNumber);
if (mTransaction->GetMode() != IDBTransaction::READ_ONLY &&
if (mTransaction->GetMode() != IDBTransaction::Mode::ReadOnly &&
mTransaction->mHasBeenActiveOnConnectionThread) {
Database* database = mTransaction->GetDatabase();
MOZ_ASSERT(database);
@ -23920,7 +23920,7 @@ CommitOp::Run()
NS_WARNING_ASSERTION(NS_SUCCEEDED(mResultCode), "Commit failed!");
if (NS_SUCCEEDED(mResultCode) &&
mTransaction->GetMode() == IDBTransaction::READ_WRITE_FLUSH) {
mTransaction->GetMode() == IDBTransaction::Mode::ReadWriteFlush) {
mResultCode = connection->Checkpoint();
}
@ -23943,7 +23943,7 @@ CommitOp::Run()
connection->FinishWriteTransaction();
if (mTransaction->GetMode() == IDBTransaction::CLEANUP) {
if (mTransaction->GetMode() == IDBTransaction::Mode::Cleanup) {
connection->DoIdleProcessing(/* aNeedsCheckpoint */ true);
connection->EnableQuotaChecks();
@ -25789,7 +25789,7 @@ NormalTransactionOp::ObjectStoreHasIndexes(NormalTransactionOp* aOp,
MOZ_ASSERT(aHasIndexes);
bool hasIndexes;
if (aOp->Transaction()->GetMode() == IDBTransaction::VERSION_CHANGE &&
if (aOp->Transaction()->GetMode() == IDBTransaction::Mode::VersionChange &&
aMayHaveIndexes) {
// If this is a version change transaction then mObjectStoreMayHaveIndexes
// could be wrong (e.g. if a unique index failed to be created due to a

View file

@ -685,7 +685,7 @@ IDBCursor::Update(JSContext* aCx, JS::Handle<JS::Value> aValue,
return nullptr;
}
if (mTransaction->GetMode() == IDBTransaction::CLEANUP ||
if (mTransaction->GetMode() == IDBTransaction::Mode::Cleanup ||
IsSourceDeleted() ||
!mHaveValue ||
mType == Type_ObjectStoreKey ||

View file

@ -425,7 +425,7 @@ IDBDatabase::CreateObjectStore(
IDBTransaction* transaction = IDBTransaction::GetCurrent();
if (!transaction ||
transaction->Database() != this ||
transaction->GetMode() != IDBTransaction::VERSION_CHANGE) {
transaction->GetMode() != IDBTransaction::Mode::VersionChange) {
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
return nullptr;
}
@ -503,7 +503,7 @@ IDBDatabase::DeleteObjectStore(const nsAString& aName, ErrorResult& aRv)
IDBTransaction* transaction = IDBTransaction::GetCurrent();
if (!transaction ||
transaction->Database() != this ||
transaction->GetMode() != IDBTransaction::VERSION_CHANGE) {
transaction->GetMode() != IDBTransaction::Mode::VersionChange) {
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
return;
}
@ -666,21 +666,21 @@ IDBDatabase::Transaction(JSContext* aCx,
IDBTransaction::Mode mode;
switch (aMode) {
case IDBTransactionMode::Readonly:
mode = IDBTransaction::READ_ONLY;
mode = IDBTransaction::Mode::ReadOnly;
break;
case IDBTransactionMode::Readwrite:
if (mQuotaExceeded) {
mode = IDBTransaction::CLEANUP;
mode = IDBTransaction::Mode::Cleanup;
mQuotaExceeded = false;
} else {
mode = IDBTransaction::READ_WRITE;
mode = IDBTransaction::Mode::ReadWrite;
}
break;
case IDBTransactionMode::Readwriteflush:
mode = IDBTransaction::READ_WRITE_FLUSH;
mode = IDBTransaction::Mode::ReadWriteFlush;
break;
case IDBTransactionMode::Cleanup:
mode = IDBTransaction::CLEANUP;
mode = IDBTransaction::Mode::Cleanup;
mQuotaExceeded = false;
break;
case IDBTransactionMode::Versionchange:
@ -715,7 +715,7 @@ IDBDatabase::Transaction(JSContext* aCx,
transaction->SetBackgroundActor(actor);
if (mode == IDBTransaction::CLEANUP) {
if (mode == IDBTransaction::Mode::Cleanup) {
ExpireFileActors(/* aExpireAll */ true);
}
@ -859,14 +859,14 @@ IDBDatabase::AbortTransactions(bool aShouldWarn)
if (aShouldWarn) {
switch (transaction->GetMode()) {
// We ignore transactions that could not have written any data.
case IDBTransaction::READ_ONLY:
case IDBTransaction::Mode::ReadOnly:
break;
// We warn for any transactions that could have written data.
case IDBTransaction::READ_WRITE:
case IDBTransaction::READ_WRITE_FLUSH:
case IDBTransaction::CLEANUP:
case IDBTransaction::VERSION_CHANGE:
case IDBTransaction::Mode::ReadWrite:
case IDBTransaction::Mode::ReadWriteFlush:
case IDBTransaction::Mode::Cleanup:
case IDBTransaction::Mode::VersionChange:
transactionsThatNeedWarning.AppendElement(transaction);
break;

View file

@ -158,7 +158,7 @@ IDBIndex::SetName(const nsAString& aName, ErrorResult& aRv)
IDBTransaction* transaction = mObjectStore->Transaction();
if (transaction->GetMode() != IDBTransaction::VERSION_CHANGE ||
if (transaction->GetMode() != IDBTransaction::Mode::VersionChange ||
mDeletedMetadata) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;

View file

@ -1407,7 +1407,7 @@ IDBObjectStore::AddOrPut(JSContext* aCx,
MOZ_ASSERT(aCx);
MOZ_ASSERT_IF(aFromCursor, aOverwrite);
if (mTransaction->GetMode() == IDBTransaction::CLEANUP ||
if (mTransaction->GetMode() == IDBTransaction::Mode::Cleanup ||
mDeletedSpec) {
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
return nullptr;
@ -1991,7 +1991,7 @@ IDBObjectStore::CreateIndex(const nsAString& aName,
{
AssertIsOnOwningThread();
if (mTransaction->GetMode() != IDBTransaction::VERSION_CHANGE ||
if (mTransaction->GetMode() != IDBTransaction::Mode::VersionChange ||
mDeletedSpec) {
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
return nullptr;
@ -2101,7 +2101,7 @@ IDBObjectStore::DeleteIndex(const nsAString& aName, ErrorResult& aRv)
{
AssertIsOnOwningThread();
if (mTransaction->GetMode() != IDBTransaction::VERSION_CHANGE ||
if (mTransaction->GetMode() != IDBTransaction::Mode::VersionChange ||
mDeletedSpec) {
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
return;
@ -2424,7 +2424,7 @@ IDBObjectStore::SetName(const nsAString& aName, ErrorResult& aRv)
{
AssertIsOnOwningThread();
if (mTransaction->GetMode() != IDBTransaction::VERSION_CHANGE ||
if (mTransaction->GetMode() != IDBTransaction::Mode::VersionChange ||
mDeletedSpec) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;

View file

@ -82,7 +82,7 @@ IDBTransaction::IDBTransaction(IDBDatabase* aDatabase,
, mPendingRequestCount(0)
, mLineNo(0)
, mColumn(0)
, mReadyState(IDBTransaction::INITIAL)
, mReadyState(IDBTransaction::ReadyState::Initial)
, mMode(aMode)
, mCreating(false)
, mRegistered(false)
@ -134,10 +134,10 @@ IDBTransaction::~IDBTransaction()
MOZ_ASSERT(!mPendingRequestCount);
MOZ_ASSERT(!mCreating);
MOZ_ASSERT(mSentCommitOrAbort);
MOZ_ASSERT_IF(mMode == VERSION_CHANGE &&
MOZ_ASSERT_IF(mMode == Mode::VersionChange &&
mBackgroundActor.mVersionChangeBackgroundActor,
mFiredCompleteOrAbort);
MOZ_ASSERT_IF(mMode != VERSION_CHANGE &&
MOZ_ASSERT_IF(mMode != Mode::VersionChange &&
mBackgroundActor.mNormalBackgroundActor,
mFiredCompleteOrAbort);
@ -148,7 +148,7 @@ IDBTransaction::~IDBTransaction()
#endif
}
if (mMode == VERSION_CHANGE) {
if (mMode == Mode::VersionChange) {
if (auto* actor = mBackgroundActor.mVersionChangeBackgroundActor) {
actor->SendDeleteMeInternal(/* aFailedConstructor */ false);
@ -184,7 +184,7 @@ IDBTransaction::CreateVersionChange(
RefPtr<IDBTransaction> transaction =
new IDBTransaction(aDatabase,
emptyObjectStoreNames,
VERSION_CHANGE);
Mode::VersionChange);
aOpenRequest->GetCallerLocation(transaction->mFilename,
&transaction->mLineNo, &transaction->mColumn);
@ -213,10 +213,10 @@ IDBTransaction::Create(JSContext* aCx, IDBDatabase* aDatabase,
MOZ_ASSERT(aDatabase);
aDatabase->AssertIsOnOwningThread();
MOZ_ASSERT(!aObjectStoreNames.IsEmpty());
MOZ_ASSERT(aMode == READ_ONLY ||
aMode == READ_WRITE ||
aMode == READ_WRITE_FLUSH ||
aMode == CLEANUP);
MOZ_ASSERT(aMode == Mode::ReadOnly ||
aMode == Mode::ReadWrite ||
aMode == Mode::ReadWriteFlush ||
aMode == Mode::Cleanup);
RefPtr<IDBTransaction> transaction =
new IDBTransaction(aDatabase, aObjectStoreNames, aMode);
@ -281,7 +281,7 @@ IDBTransaction::SetBackgroundActor(indexedDB::BackgroundTransactionChild* aBackg
AssertIsOnOwningThread();
MOZ_ASSERT(aBackgroundActor);
MOZ_ASSERT(!mBackgroundActor.mNormalBackgroundActor);
MOZ_ASSERT(mMode != VERSION_CHANGE);
MOZ_ASSERT(mMode != Mode::VersionChange);
mBackgroundActor.mNormalBackgroundActor = aBackgroundActor;
}
@ -295,7 +295,7 @@ IDBTransaction::StartRequest(IDBRequest* aRequest, const RequestParams& aParams)
BackgroundRequestChild* actor = new BackgroundRequestChild(aRequest);
if (mMode == VERSION_CHANGE) {
if (mMode == Mode::VersionChange) {
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
mBackgroundActor.mVersionChangeBackgroundActor->
@ -321,7 +321,7 @@ IDBTransaction::OpenCursor(BackgroundCursorChild* aBackgroundActor,
MOZ_ASSERT(aBackgroundActor);
MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None);
if (mMode == VERSION_CHANGE) {
if (mMode == Mode::VersionChange) {
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
mBackgroundActor.mVersionChangeBackgroundActor->
@ -361,8 +361,8 @@ IDBTransaction::OnNewRequest()
AssertIsOnOwningThread();
if (!mPendingRequestCount) {
MOZ_ASSERT(INITIAL == mReadyState);
mReadyState = LOADING;
MOZ_ASSERT(ReadyState::Initial == mReadyState);
mReadyState = ReadyState::Loading;
}
++mPendingRequestCount;
@ -377,7 +377,7 @@ IDBTransaction::OnRequestFinished(bool aActorDestroyedNormally)
--mPendingRequestCount;
if (!mPendingRequestCount) {
mReadyState = COMMITTING;
mReadyState = ReadyState::Committing;
if (aActorDestroyedNormally) {
if (NS_SUCCEEDED(mAbortCode)) {
@ -421,7 +421,7 @@ IDBTransaction::SendCommit()
LoggingSerialNumber(),
requestSerialNumber);
if (mMode == VERSION_CHANGE) {
if (mMode == Mode::VersionChange) {
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
mBackgroundActor.mVersionChangeBackgroundActor->SendCommit();
} else {
@ -454,7 +454,7 @@ IDBTransaction::SendAbort(nsresult aResultCode)
requestSerialNumber,
aResultCode);
if (mMode == VERSION_CHANGE) {
if (mMode == Mode::VersionChange) {
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
mBackgroundActor.mVersionChangeBackgroundActor->SendAbort(aResultCode);
} else {
@ -473,7 +473,7 @@ IDBTransaction::IsOpen() const
AssertIsOnOwningThread();
// If we haven't started anything then we're open.
if (mReadyState == IDBTransaction::INITIAL) {
if (mReadyState == IDBTransaction::ReadyState::Initial) {
return true;
}
@ -482,7 +482,7 @@ IDBTransaction::IsOpen() const
// from the time we were created) then we are open. Otherwise check the
// currently running transaction to see if it's the same. We only allow other
// requests to be made if this transaction is currently running.
if (mReadyState == IDBTransaction::LOADING &&
if (mReadyState == IDBTransaction::ReadyState::Loading &&
(mCreating || GetCurrent() == this)) {
return true;
}
@ -508,7 +508,7 @@ IDBTransaction::CreateObjectStore(const ObjectStoreSpec& aSpec)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aSpec.metadata().id());
MOZ_ASSERT(VERSION_CHANGE == mMode);
MOZ_ASSERT(Mode::VersionChange == mMode);
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
MOZ_ASSERT(IsOpen());
@ -540,7 +540,7 @@ IDBTransaction::DeleteObjectStore(int64_t aObjectStoreId)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aObjectStoreId);
MOZ_ASSERT(VERSION_CHANGE == mMode);
MOZ_ASSERT(Mode::VersionChange == mMode);
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
MOZ_ASSERT(IsOpen());
@ -571,7 +571,7 @@ IDBTransaction::RenameObjectStore(int64_t aObjectStoreId,
{
AssertIsOnOwningThread();
MOZ_ASSERT(aObjectStoreId);
MOZ_ASSERT(VERSION_CHANGE == mMode);
MOZ_ASSERT(Mode::VersionChange == mMode);
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
MOZ_ASSERT(IsOpen());
@ -586,7 +586,7 @@ IDBTransaction::CreateIndex(IDBObjectStore* aObjectStore,
AssertIsOnOwningThread();
MOZ_ASSERT(aObjectStore);
MOZ_ASSERT(aMetadata.id());
MOZ_ASSERT(VERSION_CHANGE == mMode);
MOZ_ASSERT(Mode::VersionChange == mMode);
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
MOZ_ASSERT(IsOpen());
@ -601,7 +601,7 @@ IDBTransaction::DeleteIndex(IDBObjectStore* aObjectStore,
AssertIsOnOwningThread();
MOZ_ASSERT(aObjectStore);
MOZ_ASSERT(aIndexId);
MOZ_ASSERT(VERSION_CHANGE == mMode);
MOZ_ASSERT(Mode::VersionChange == mMode);
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
MOZ_ASSERT(IsOpen());
@ -617,7 +617,7 @@ IDBTransaction::RenameIndex(IDBObjectStore* aObjectStore,
AssertIsOnOwningThread();
MOZ_ASSERT(aObjectStore);
MOZ_ASSERT(aIndexId);
MOZ_ASSERT(VERSION_CHANGE == mMode);
MOZ_ASSERT(Mode::VersionChange == mMode);
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
MOZ_ASSERT(IsOpen());
@ -637,12 +637,12 @@ IDBTransaction::AbortInternal(nsresult aAbortCode,
RefPtr<DOMError> error = aError;
const bool isVersionChange = mMode == VERSION_CHANGE;
const bool isVersionChange = mMode == Mode::VersionChange;
const bool isInvalidated = mDatabase->IsInvalidated();
bool needToSendAbort = mReadyState == INITIAL;
bool needToSendAbort = mReadyState == ReadyState::Initial;
mAbortCode = aAbortCode;
mReadyState = DONE;
mReadyState = ReadyState::Done;
mError = error.forget();
if (isVersionChange) {
@ -775,7 +775,7 @@ IDBTransaction::FireCompleteOrAbortEvents(nsresult aResult)
AssertIsOnOwningThread();
MOZ_ASSERT(!mFiredCompleteOrAbort);
mReadyState = DONE;
mReadyState = ReadyState::Done;
#ifdef DEBUG
mFiredCompleteOrAbort = true;
@ -834,7 +834,7 @@ int64_t
IDBTransaction::NextObjectStoreId()
{
AssertIsOnOwningThread();
MOZ_ASSERT(VERSION_CHANGE == mMode);
MOZ_ASSERT(Mode::VersionChange == mMode);
return mNextObjectStoreId++;
}
@ -843,7 +843,7 @@ int64_t
IDBTransaction::NextIndexId()
{
AssertIsOnOwningThread();
MOZ_ASSERT(VERSION_CHANGE == mMode);
MOZ_ASSERT(Mode::VersionChange == mMode);
return mNextIndexId++;
}
@ -862,22 +862,22 @@ IDBTransaction::GetMode(ErrorResult& aRv) const
AssertIsOnOwningThread();
switch (mMode) {
case READ_ONLY:
case Mode::ReadOnly:
return IDBTransactionMode::Readonly;
case READ_WRITE:
case Mode::ReadWrite:
return IDBTransactionMode::Readwrite;
case READ_WRITE_FLUSH:
case Mode::ReadWriteFlush:
return IDBTransactionMode::Readwriteflush;
case CLEANUP:
case Mode::Cleanup:
return IDBTransactionMode::Cleanup;
case VERSION_CHANGE:
case Mode::VersionChange:
return IDBTransactionMode::Versionchange;
case MODE_INVALID:
case Mode::Invalid:
default:
MOZ_CRASH("Bad mode!");
}
@ -896,7 +896,7 @@ IDBTransaction::ObjectStoreNames() const
{
AssertIsOnOwningThread();
if (mMode == IDBTransaction::VERSION_CHANGE) {
if (mMode == IDBTransaction::Mode::VersionChange) {
return mDatabase->ObjectStoreNames();
}
@ -917,7 +917,7 @@ IDBTransaction::ObjectStore(const nsAString& aName, ErrorResult& aRv)
const ObjectStoreSpec* spec = nullptr;
if (IDBTransaction::VERSION_CHANGE == mMode ||
if (IDBTransaction::Mode::VersionChange == mMode ||
mObjectStoreNames.Contains(aName)) {
const nsTArray<ObjectStoreSpec>& objectStores =
mDatabase->Spec()->objectStores();
@ -1014,8 +1014,8 @@ IDBTransaction::Run()
mCreating = false;
// Maybe commit if there were no requests generated.
if (mReadyState == IDBTransaction::INITIAL) {
mReadyState = DONE;
if (mReadyState == IDBTransaction::ReadyState::Initial) {
mReadyState = ReadyState::Done;
SendCommit();
}

View file

@ -53,25 +53,18 @@ class IDBTransaction final
friend class WorkerHolder;
public:
enum Mode
{
READ_ONLY = 0,
READ_WRITE,
READ_WRITE_FLUSH,
CLEANUP,
VERSION_CHANGE,
enum struct Mode {
ReadOnly = 0,
ReadWrite,
ReadWriteFlush,
Cleanup,
VersionChange,
// Only needed for IPC serialization helper, should never be used in code.
MODE_INVALID
Invalid
};
enum ReadyState
{
INITIAL = 0,
LOADING,
COMMITTING,
DONE
};
enum struct ReadyState { Initial = 0, Loading, Inactive, Committing, Done };
private:
RefPtr<IDBDatabase> mDatabase;
@ -81,7 +74,7 @@ private:
nsTArray<RefPtr<IDBObjectStore>> mDeletedObjectStores;
nsAutoPtr<WorkerHolder> mWorkerHolder;
// Tagged with mMode. If mMode is VERSION_CHANGE then mBackgroundActor will be
// Tagged with mMode. If mMode is Mode::VersionChange then mBackgroundActor will be
// a BackgroundVersionChangeTransactionChild. Otherwise it will be a
// BackgroundTransactionChild.
union {
@ -91,7 +84,7 @@ private:
const int64_t mLoggingSerialNumber;
// Only used for VERSION_CHANGE transactions.
// Only used for Mode::VersionChange transactions.
int64_t mNextObjectStoreId;
int64_t mNextIndexId;
@ -146,7 +139,7 @@ public:
{
AssertIsOnOwningThread();
if (mMode == VERSION_CHANGE) {
if (mMode == Mode::VersionChange) {
mBackgroundActor.mVersionChangeBackgroundActor = nullptr;
} else {
mBackgroundActor.mNormalBackgroundActor = nullptr;
@ -171,7 +164,7 @@ public:
{
AssertIsOnOwningThread();
return mReadyState == COMMITTING || mReadyState == DONE;
return mReadyState == ReadyState::Committing || mReadyState == ReadyState::Done;
}
bool
@ -179,17 +172,17 @@ public:
{
AssertIsOnOwningThread();
return mReadyState == DONE;
return mReadyState == ReadyState::Done;
}
bool
IsWriteAllowed() const
{
AssertIsOnOwningThread();
return mMode == READ_WRITE ||
mMode == READ_WRITE_FLUSH ||
mMode == CLEANUP ||
mMode == VERSION_CHANGE;
return mMode == Mode::ReadWrite ||
mMode == Mode::ReadWriteFlush ||
mMode == Mode::Cleanup ||
mMode == Mode::VersionChange;
}
bool
@ -295,11 +288,11 @@ public:
void
FireCompleteOrAbortEvents(nsresult aResult);
// Only for VERSION_CHANGE transactions.
// Only for Mode::VersionChange transactions.
int64_t
NextObjectStoreId();
// Only for VERSION_CHANGE transactions.
// Only for Mode::VersionChange transactions.
int64_t
NextIndexId();

View file

@ -119,19 +119,19 @@ public:
Append(kCommaSpace);
switch (aTransaction->GetMode()) {
case IDBTransaction::READ_ONLY:
case IDBTransaction::Mode::ReadOnly:
AppendLiteral("\"readonly\"");
break;
case IDBTransaction::READ_WRITE:
case IDBTransaction::Mode::ReadWrite:
AppendLiteral("\"readwrite\"");
break;
case IDBTransaction::READ_WRITE_FLUSH:
case IDBTransaction::Mode::ReadWriteFlush:
AppendLiteral("\"readwriteflush\"");
break;
case IDBTransaction::CLEANUP:
case IDBTransaction::Mode::Cleanup:
AppendLiteral("\"cleanup\"");
break;
case IDBTransaction::VERSION_CHANGE:
case IDBTransaction::Mode::VersionChange:
AppendLiteral("\"versionchange\"");
break;
default:

View file

@ -86,8 +86,8 @@ template <>
struct ParamTraits<mozilla::dom::IDBTransaction::Mode> :
public ContiguousEnumSerializer<
mozilla::dom::IDBTransaction::Mode,
mozilla::dom::IDBTransaction::READ_ONLY,
mozilla::dom::IDBTransaction::MODE_INVALID>
mozilla::dom::IDBTransaction::Mode::ReadOnly,
mozilla::dom::IDBTransaction::Mode::Invalid>
{ };
} // namespace IPC

View file

@ -12,7 +12,7 @@
<script type="text/javascript;version=1.7">
function testSteps()
{
const READ_WRITE = "readwrite";
const Mode::ReadWrite = "readwrite";
const databaseInfo = [
{ name: window.location.pathname + "1" },
@ -72,7 +72,7 @@
for (let i = 1; i < databases.length; i++) {
let db = databases[i];
let objectStore = db.transaction([objectStoreName], READ_WRITE)
let objectStore = db.transaction([objectStoreName], Mode::ReadWrite)
.objectStore(objectStoreName);
request = objectStore.add(refResult, 2);

View file

@ -12,7 +12,7 @@
<script type="text/javascript;version=1.7">
function testSteps()
{
const READ_WRITE = "readwrite";
const Mode::ReadWrite = "readwrite";
const name = window.location.pathname;
@ -48,7 +48,7 @@
is(event.type, "success", "Got correct event type");
let trans = db.transaction([objectStoreName], READ_WRITE);
let trans = db.transaction([objectStoreName], Mode::ReadWrite);
trans.objectStore(objectStoreName).delete(fileData1.key);
trans.oncomplete = grabEventAndContinueHandler;
event = yield undefined;
@ -80,7 +80,7 @@
let db = event.target.result;
db.onerror = errorHandler;
trans = db.transaction([objectStoreName], READ_WRITE);
trans = db.transaction([objectStoreName], Mode::ReadWrite);
objectStore = trans.objectStore(objectStoreName);
request = objectStore.get(fileData2.key);
@ -100,7 +100,7 @@
is(getFileDBRefCount(name, 2), 0, "Correct db ref count");
trans = db.transaction([objectStoreName], READ_WRITE);
trans = db.transaction([objectStoreName], Mode::ReadWrite);
objectStore = trans.objectStore(objectStoreName);
objectStore.delete(fileData3.key);

View file

@ -12,7 +12,7 @@
<script type="text/javascript;version=1.7">
function testSteps()
{
const READ_WRITE = "readwrite";
const Mode::ReadWrite = "readwrite";
const name = window.location.pathname;
@ -67,7 +67,7 @@
is(usage, startUsage + fileData1.obj.file.size + fileData2.obj.file.size,
"Correct file usage");
let trans = db.transaction([objectStoreName], READ_WRITE);
let trans = db.transaction([objectStoreName], Mode::ReadWrite);
trans.objectStore(objectStoreName).delete(fileData1.key);
trans.oncomplete = grabEventAndContinueHandler;
event = yield undefined;

View file

@ -21,7 +21,7 @@
*/
function testSteps()
{
const READ_WRITE = "readwrite";
const Mode::ReadWrite = "readwrite";
const databaseInfo = [
{ name: window.location.pathname + "1", source: true },
@ -106,7 +106,7 @@
for (let i = 1; i < databases.length; i++) {
let db = databases[i];
let trans = db.transaction([objectStoreName], READ_WRITE);
let trans = db.transaction([objectStoreName], Mode::ReadWrite);
let objectStore = trans.objectStore(objectStoreName);
request = objectStore.add(fileBackedFile, 2);

View file

@ -12,7 +12,7 @@
<script type="text/javascript;version=1.7">
function testSteps()
{
const READ_WRITE = "readwrite";
const Mode::ReadWrite = "readwrite";
const name = window.location.pathname;

View file

@ -12,7 +12,7 @@
<script type="text/javascript;version=1.7">
function testSteps()
{
const READ_WRITE = "readwrite";
const Mode::ReadWrite = "readwrite";
const name = window.location.pathname;
@ -42,7 +42,7 @@
is(event.type, "success", "Got correct event type");
let objectStore = db.transaction([objectStoreName], READ_WRITE)
let objectStore = db.transaction([objectStoreName], Mode::ReadWrite)
.objectStore(objectStoreName);
request = objectStore.add(blobData.blob, blobData.key);
request.onsuccess = grabEventAndContinueHandler;
@ -65,7 +65,7 @@
verifyBlob(event.target.result, blobData.blob, 1);
yield undefined;
objectStore = db.transaction([objectStoreName], READ_WRITE)
objectStore = db.transaction([objectStoreName], Mode::ReadWrite)
.objectStore(objectStoreName);
request = objectStore.add(fileData.file, fileData.key);
request.onsuccess = grabEventAndContinueHandler;

View file

@ -12,7 +12,7 @@
<script type="text/javascript;version=1.7">
function testSteps()
{
const READ_WRITE = "readwrite";
const Mode::ReadWrite = "readwrite";
const name = window.location.pathname;
@ -40,7 +40,7 @@
is(event.type, "success", "Got correct event type");
let trans = db.transaction([objectStoreName], READ_WRITE);
let trans = db.transaction([objectStoreName], Mode::ReadWrite);
objectStore = trans.objectStore(objectStoreName);
objectStore.delete(fileData.key);
@ -50,7 +50,7 @@
is(getFileDBRefCount(name, 1), 0, "Correct db ref count");
trans = db.transaction([objectStoreName], READ_WRITE);
trans = db.transaction([objectStoreName], Mode::ReadWrite);
objectStore = trans.objectStore(objectStoreName);
request = objectStore.add(fileData.file, fileData.key);
@ -81,7 +81,7 @@
let db = event.target.result;
db.onerror = errorHandler;
let trans = db.transaction([objectStoreName], READ_WRITE);
let trans = db.transaction([objectStoreName], Mode::ReadWrite);
objectStore = trans.objectStore(objectStoreName);
request = objectStore.get(fileData.key);
@ -98,7 +98,7 @@
is(getFileDBRefCount(name, 1), 0, "Correct db ref count");
trans = db.transaction([objectStoreName], READ_WRITE);
trans = db.transaction([objectStoreName], Mode::ReadWrite);
objectStore = trans.objectStore(objectStoreName);
request = objectStore.add(result, fileData.key);

View file

@ -12,7 +12,7 @@
<script type="text/javascript;version=1.7">
function testSteps()
{
const READ_WRITE = "readwrite";
const Mode::ReadWrite = "readwrite";
const name = window.location.pathname;
@ -38,7 +38,7 @@
is(event.type, "success", "Got correct event type");
let trans = db.transaction([objectStoreName], READ_WRITE);
let trans = db.transaction([objectStoreName], Mode::ReadWrite);
objectStore = trans.objectStore(objectStoreName);
request = objectStore.add(fileData.file, fileData.key);
@ -58,7 +58,7 @@
is(getFileDBRefCount(name, 1), 0, "Correct db ref count");
trans = db.transaction([objectStoreName], READ_WRITE);
trans = db.transaction([objectStoreName], Mode::ReadWrite);
objectStore = trans.objectStore(objectStoreName);
request = objectStore.add(result, fileData.key);

View file

@ -12,7 +12,7 @@
<script type="text/javascript;version=1.7">
function testSteps()
{
const READ_WRITE = "readwrite";
const Mode::ReadWrite = "readwrite";
const name = window.location.pathname;
@ -67,7 +67,7 @@
for (let i = 1; i < objectStoreInfo.length; i++) {
let info = objectStoreInfo[i];
let objectStore = db.transaction([info.name], READ_WRITE)
let objectStore = db.transaction([info.name], Mode::ReadWrite)
.objectStore(info.name);
request = objectStore.add(refResult, 2);

View file

@ -12,7 +12,7 @@
<script type="text/javascript;version=1.7">
function testSteps()
{
const READ_WRITE = "readwrite";
const Mode::ReadWrite = "readwrite";
const name = window.location.pathname;
@ -38,7 +38,7 @@
is(event.type, "success", "Got correct event type");
let trans = db.transaction([objectStoreName], READ_WRITE);
let trans = db.transaction([objectStoreName], Mode::ReadWrite);
objectStore = trans.objectStore(objectStoreName);
request = objectStore.add(fileData.file, fileData.key);

View file

@ -12,7 +12,7 @@
<script type="text/javascript;version=1.7">
function testSteps()
{
const READ_WRITE = "readwrite";
const Mode::ReadWrite = "readwrite";
const databaseInfo = [
{ name: window.location.pathname + "1" },
@ -56,7 +56,7 @@
is(mutableFile.name, "random.bin", "Correct name");
is(mutableFile.type, "binary/random", "Correct type");
let trans = db1.transaction([objectStoreName], READ_WRITE);
let trans = db1.transaction([objectStoreName], Mode::ReadWrite);
let objectStore = trans.objectStore(objectStoreName);
request = objectStore.add(mutableFile, 42);
@ -74,7 +74,7 @@
let db2 = databases[1];
trans = db2.transaction([objectStoreName], READ_WRITE);
trans = db2.transaction([objectStoreName], Mode::ReadWrite);
objectStore = trans.objectStore(objectStoreName);
try {

View file

@ -12,7 +12,7 @@
<script type="text/javascript;version=1.7">
function testSteps()
{
const READ_WRITE = "readwrite";
const Mode::ReadWrite = "readwrite";
const name = window.location.pathname;
@ -58,7 +58,7 @@
let file = event.target.result;
let trans = db.transaction([objectStoreName], READ_WRITE);
let trans = db.transaction([objectStoreName], Mode::ReadWrite);
objectStore = trans.objectStore(objectStoreName);
request = objectStore.add(file, 42);

View file

@ -55,10 +55,10 @@ function testSteps()
try {
db.transaction("foo").objectStore("foo").clear();
ok(false, "clear should throw on READ_ONLY transactions");
ok(false, "clear should throw on Mode::ReadOnly transactions");
}
catch (e) {
ok(true, "clear should throw on READ_ONLY transactions");
ok(true, "clear should throw on Mode::ReadOnly transactions");
}
request = db.transaction("foo", "readwriteflush")

View file

@ -28,7 +28,7 @@ function testSteps()
is(db.version, 1, "Database has correct version");
db.onupgradeneeded = function() {
ok(false, "our ongoing VERSION_CHANGE transaction should exclude any others!");
ok(false, "our ongoing Mode::VersionChange transaction should exclude any others!");
}
db.createObjectStore("foo");

View file

@ -258,7 +258,7 @@ function testSteps()
abortEventCount = 0;
let expectedAbortEventCount = 0;
// During INITIAL
// During ReadyState::Initial
transaction = db.transaction("foo");
transaction.abort();
try {
@ -269,7 +269,7 @@ function testSteps()
ok(true, "second abort should throw an error");
}
// During LOADING
// During ReadyState::Loading
transaction = db.transaction("foo");
transaction.objectStore("foo").get(1).onerror = abortErrorHandler;
expectedAbortEventCount++;
@ -282,7 +282,7 @@ function testSteps()
ok(true, "second abort should throw an error");
}
// During LOADING from callback
// During ReadyState::Loading from callback
transaction = db.transaction("foo");
transaction.objectStore("foo").get(1).onsuccess = grabEventAndContinueHandler;
event = yield undefined;
@ -297,7 +297,7 @@ function testSteps()
ok(true, "second abort should throw an error");
}
// During LOADING from error callback
// During ReadyState::Loading from error callback
transaction = db.transaction("foo", "readwrite");
transaction.objectStore("foo").add({}, 1).onerror = function(event) {
event.preventDefault();
@ -327,7 +327,7 @@ function testSteps()
};
yield undefined;
// During COMMITTING
// During ReadyState::Committing
transaction = db.transaction("foo", "readwrite");
transaction.objectStore("foo").put({hello: "world"}, 1).onsuccess = function(event) {
continueToNextStep();

View file

@ -259,14 +259,13 @@ SVGUseElement::CreateAnonymousContent()
}
}
nsCOMPtr<nsINode> newnode;
nsCOMArray<nsINode> unused;
nsNodeInfoManager* nodeInfoManager =
targetContent->OwnerDoc() == OwnerDoc() ?
nullptr : OwnerDoc()->NodeInfoManager();
nsNodeUtils::Clone(targetContent, true, nodeInfoManager, unused,
getter_AddRefs(newnode));
IgnoredErrorResult rv;
nsCOMPtr<nsINode> newnode =
nsNodeUtils::Clone(targetContent, true, nodeInfoManager, &unused, rv);
nsCOMPtr<nsIContent> newcontent = do_QueryInterface(newnode);
if (!newcontent)

View file

@ -0,0 +1,110 @@
<!DOCTYPE HTML>
<html>
<!--https://bugzilla.mozilla.org/show_bug.cgi?id=633602-->
<head>
<title>Bug 633602</title>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js">
</script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js">
</script>
<script type="application/javascript" src="pointerlock_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=633602">
Mozilla Bug 633602
</a>
<div id="host"></div>
<pre id="test">
<script type="text/javascript">
/*
* Test for Bug 1430303
* Make sure DOM API is correct.
*/
SimpleTest.waitForExplicitFinish();
var div,
host,
hasRequestPointerLock = false,
pointerLockChangeEventFired = false,
pointerUnlocked = false,
pointerLocked = false,
hasExitPointerLock = false,
pointerLockElement = false,
hasMovementX = false,
hasMovementY = false;
gotContextMenuEvent = false;
function runTests () {
ok(hasRequestPointerLock, "Element should have requestPointerLock.");
ok(pointerLockChangeEventFired, "pointerlockchange event should fire.");
ok(pointerUnlocked, "Should be able to unlock pointer locked element.");
ok(pointerLocked, "Requested element should be able to lock.");
ok(hasExitPointerLock, "Document should have exitPointerLock");
ok(pointerLockElement, "Document should keep track of correct pointer locked element");
ok(hasMovementX, "Mouse Event should have movementX.");
ok(hasMovementY, "Mouse Event should have movementY.");
ok(!gotContextMenuEvent, "Shouldn't have got a contextmenu event.");
}
function mouseMoveHandler(e) {
info("Got mousemove");
document.removeEventListener("mousemove", mouseMoveHandler);
hasMovementX = "movementX" in e;
hasMovementY = "movementY" in e;
hasExitPointerLock = "exitPointerLock" in document;
document.exitPointerLock();
}
document.addEventListener("pointerlockchange", function (e) {
pointerLockChangeEventFired = true;
if (document.pointerLockElement) {
info("Got pointerlockchange for entering");
pointerLocked = true;
pointerLockElement = document.pointerLockElement === host;
is(host.shadowRoot.firstChild, host.shadowRoot.pointerLockElement,
"ShadowRoot's pointerLockElement shouldn't be retargeted.");
window.addEventListener("contextmenu",
function() { gotContextMenuEvent = true; },
true);
synthesizeMouse(document.body, 4, 4,
{ type: "contextmenu", button: 2 },
window);
document.addEventListener("mousemove", mouseMoveHandler);
synthesizeMouseAtCenter(host, {type: "mousemove"}, window);
} else {
info("Got pointerlockchange for exiting");
pointerUnlocked = true;
addFullscreenChangeContinuation("exit", function() {
info("Got fullscreenchange for exiting");
runTests();
SimpleTest.finish();
});
document.exitFullscreen();
}
});
function start() {
host = document.getElementById("host");
var sr = host.attachShadow({mode: "open"});
sr.innerHTML = "<div><div>";
div = sr.firstChild;
info("Requesting fullscreen on parent");
addFullscreenChangeContinuation("enter", function() {
info("Got fullscreenchange for entering");
hasRequestPointerLock = "requestPointerLock" in div;
div.requestPointerLock();
});
host.requestFullscreen();
}
</script>
</pre>
</body>
</html>

View file

@ -8,6 +8,7 @@ tags = fullscreen
support-files =
pointerlock_utils.js
file_pointerlock-api.html
file_pointerlock-api-with-shadow.html
file_pointerlockerror.html
file_escapeKey.html
file_withoutDOM.html

View file

@ -33,7 +33,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
["full-screen-api.unprefix.enabled", true],
["full-screen-api.allow-trusted-requests-only", false],
["full-screen-api.transition-duration.enter", "0 0"],
["full-screen-api.transition-duration.leave", "0 0"]
["full-screen-api.transition-duration.leave", "0 0"],
["dom.webcomponents.enabled", true]
]}, nextTest);
// Run the tests which go full-screen in new window, as Mochitests
@ -49,6 +50,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
"file_movementXY.html",
"file_nestedFullScreen.html",
"file_pointerlock-api.html",
"file_pointerlock-api-with-shadow.html",
"file_pointerlockerror.html",
"file_pointerLockPref.html",
"file_removedFromDOM.html",

View file

@ -1,173 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=887538
-->
<head>
<title>Test for HTMLShadowElement</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<div id="grabme"></div>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=887538">Bug 887538</a>
<script>
var host = document.createElement("span");
// Create three shadow roots on a single host and make sure that shadow elements
// are associated with the correct shadow root.
var firstShadow = host.createShadowRoot();
firstShadow.innerHTML = '<shadow id="shadowone"></shadow>';
var secondShadow = host.createShadowRoot();
secondShadow.innerHTML = '<shadow id="shadowtwo"></shadow>';
var thirdShadow = host.createShadowRoot();
thirdShadow.innerHTML = '<shadow id="shadowthree"></shadow>';
is(firstShadow.getElementById("shadowone").olderShadowRoot, null, "Shadow element in oldest ShadowRoot should not be associated with a ShadowRoot.");
is(secondShadow.getElementById("shadowtwo").olderShadowRoot, firstShadow, "Shadow element should be associated with older ShadowRoot.");
is(thirdShadow.getElementById("shadowthree").olderShadowRoot, secondShadow, "Shadow element should be associated with older ShadowRoot.");
// Only the first ShadowRoot in tree order is an insertion point.
host = document.createElement("span");
firstShadow = host.createShadowRoot();
secondShadow = host.createShadowRoot();
secondShadow.innerHTML = '<shadow id="shadowone"></shadow><shadow id="shadowtwo"></shadow>';
var shadowElemOne = secondShadow.getElementById("shadowone");
var shadowElemTwo = secondShadow.getElementById("shadowtwo");
is(shadowElemOne.olderShadowRoot, firstShadow, "First <shadow> in tree order should be an insertion point.");
is(shadowElemTwo.olderShadowRoot, null, "Second <shadow> in tree order should not be an insertion point.");
// Remove the first <shadow> element and make sure the second <shadow> element becomes an insertion point.
secondShadow.removeChild(shadowElemOne);
is(shadowElemOne.olderShadowRoot, null, "<shadow> element not in a ShadowRoot is not an insertion point.");
is(shadowElemTwo.olderShadowRoot, firstShadow, "Second <shadow> element should become insertion point after first is removed.");
// Insert a <shadow> element before the current shadow insertion point and make sure that it becomes an insertion point.
secondShadow.insertBefore(shadowElemOne, shadowElemTwo);
is(shadowElemOne.olderShadowRoot, firstShadow, "<shadow> element inserted as first in tree order should become an insertion point.");
is(shadowElemTwo.olderShadowRoot, null, "<shadow> element should no longer be an insertion point it another is inserted before.");
// <shadow> element in fallback content is not an insertion point.
host = document.createElement("span");
firstShadow = host.createShadowRoot();
secondShadow = host.createShadowRoot();
secondShadow.innerHTML = '<content><shadow id="shadowone"></shadow></content><shadow id="shadowtwo"></shadow>';
shadowElemOne = secondShadow.getElementById("shadowone");
shadowElemTwo = secondShadow.getElementById("shadowtwo");
is(shadowElemOne.olderShadowRoot, null, "<shadow> element in fallback content is not an insertion point.");
is(shadowElemTwo.olderShadowRoot, null, "<shadow> element preceeded by another <shadow> element is not an insertion point.");
// <shadow> element that is descendant of shadow element is not an insertion point.
host = document.createElement("span");
firstShadow = host.createShadowRoot();
secondShadow = host.createShadowRoot();
secondShadow.innerHTML = '<shadow><shadow id="shadowone"></shadow></shadow>';
shadowElemOne = secondShadow.getElementById("shadowone");
is(shadowElemOne.olderShadowRoot, null, "<shadow> element that is descendant of shadow element is not an insertion point.");
// Check projection of <content> elements through <shadow> elements.
host = document.createElement("span");
firstShadow = host.createShadowRoot();
secondShadow = host.createShadowRoot();
firstShadow.innerHTML = '<content id="firstcontent"></content>';
secondShadow.innerHTML = '<shadow><span id="one"></span><content id="secondcontent"></content><span id="four"></span></shadow>';
host.innerHTML = '<span id="two"></span><span id="three"></span>';
var firstContent = firstShadow.getElementById("firstcontent");
var secondContent = secondShadow.getElementById("secondcontent");
var firstDistNodes = firstContent.getDistributedNodes();
var secondDistNodes = secondContent.getDistributedNodes();
is(secondDistNodes.length, 2, "There should be two distributed nodes from the host.");
ok(secondDistNodes[0].id == "two" &&
secondDistNodes[1].id == "three", "Nodes projected from host should preserve order.");
is(firstDistNodes.length, 4, "There should be four distributed nodes, two from the first shadow, two from the second shadow.");
ok(firstDistNodes[0].id == "one" &&
firstDistNodes[1].id == "two" &&
firstDistNodes[2].id == "three" &&
firstDistNodes[3].id == "four", "Reprojection through shadow should preserve node order.");
// Remove a node from the host and make sure that it is removed from all insertion points.
host.removeChild(host.firstChild);
firstDistNodes = firstContent.getDistributedNodes();
secondDistNodes = secondContent.getDistributedNodes();
is(secondDistNodes.length, 1, "There should be one distriubted node remaining after removing node from host.");
ok(secondDistNodes[0].id == "three", "Span with id=two should have been removed from content element.");
is(firstDistNodes.length, 3, "There should be three distributed nodes remaining after removing node from host.");
ok(firstDistNodes[0].id == "one" &&
firstDistNodes[1].id == "three" &&
firstDistNodes[2].id == "four", "Reprojection through shadow should preserve node order.");
// Check projection of <shadow> elements to <content> elements.
host = document.createElement("span");
firstShadow = host.createShadowRoot();
secondShadow = host.createShadowRoot();
secondShadow.innerHTML = '<span id="firstspan"><shadow></shadow></span>';
thirdShadow = secondShadow.getElementById("firstspan").createShadowRoot();
thirdShadow.innerHTML = '<content id="firstcontent"></content>';
firstContent = thirdShadow.getElementById("firstcontent");
var shadowChild = document.createElement("span");
firstShadow.appendChild(shadowChild);
is(firstContent.getDistributedNodes()[0], shadowChild, "Elements in shadow insertioin point should be projected into content insertion points.");
// Remove child of ShadowRoot and check that projected node is removed from insertion point.
firstShadow.removeChild(firstShadow.firstChild);
is(firstContent.getDistributedNodes().length, 0, "Reprojected element was removed from ShadowRoot, thus it should be removed from content insertion point.");
// Check deeply nested projection of <shadow> elements.
host = document.createElement("span");
firstShadow = host.createShadowRoot();
firstShadow.innerHTML = '<content></content>';
secondShadow = host.createShadowRoot();
secondShadow.innerHTML = '<shadow><content></content></shadow>';
thirdShadow = host.createShadowRoot();
thirdShadow.innerHTML = '<span id="firstspan"><shadow><content></content></shadow></span>';
var fourthShadow = thirdShadow.getElementById("firstspan").createShadowRoot();
fourthShadow.innerHTML = '<content id="firstcontent"></content>';
firstContent = fourthShadow.getElementById("firstcontent");
host.innerHTML = '<span></span>';
is(firstContent.getDistributedNodes()[0], host.firstChild, "Child of host should be projected to insertion point.");
// Remove node and make sure that it is removed from distributed nodes.
host.removeChild(host.firstChild);
is(firstContent.getDistributedNodes().length, 0, "Node removed from host should be removed from insertion point.");
// Check projection of fallback content through <shadow> elements.
host = document.createElement("span");
firstShadow = host.createShadowRoot();
firstShadow.innerHTML = '<content><span id="firstspan"></span></content>';
secondShadow = host.createShadowRoot();
secondShadow.innerHTML = '<span id="secondspan"><shadow id="firstshadow"></shadow></span>';
firstShadowElem = secondShadow.getElementById("firstshadow");
thirdShadow = secondShadow.getElementById("secondspan").createShadowRoot();
thirdShadow.innerHTML = '<content id="firstcontent"></content>';
firstContent = thirdShadow.getElementById("firstcontent");
is(firstContent.getDistributedNodes().length, 1, "There should be one node distributed from fallback content.");
is(firstContent.getDistributedNodes()[0], firstShadow.getElementById("firstspan"), "Fallback content should be distributed.");
// Add some content to the host (causing the fallback content to be dropped) and make sure distribution nodes are updated.
var newElem = document.createElement("div");
firstShadowElem.appendChild(newElem);
is(firstContent.getDistributedNodes().length, 1, "There should be one node distributed from the host.");
is(firstContent.getDistributedNodes()[0], newElem, "Distributed node should be from host, not fallback content.");
// Remove the distribution node and check that fallback content is used.
firstShadowElem.removeChild(newElem);
is(firstContent.getDistributedNodes().length, 1, "There should be one node distributed from fallback content.");
is(firstContent.getDistributedNodes()[0], firstShadow.getElementById("firstspan"), "Fallback content should be distributed after removing node from host.");
</script>
</body>
</html>

View file

@ -22,22 +22,7 @@ isnot(document.baseURI, "http://www.example.org/", "Base element should be inert
SimpleTest.waitForExplicitFinish();
// Check that <link> is inert.
var numStyleBeforeLoad = document.styleSheets.length;
shadow.innerHTML = '<link id="shadowlink" rel="stylesheet" type="text/css" href="inert_style.css" /><span id="shadowspan"></span>';
shadow.applyAuthorStyles = true;
var shadowSpan = shadow.getElementById("shadowspan");
var shadowStyle = shadow.getElementById("shadowlink");
function runChecks() {
isnot(getComputedStyle(shadowSpan, null).getPropertyValue("padding-top"), "10px", "Link element should be inert.");
is(document.styleSheets.length, numStyleBeforeLoad, "Document style count should remain the same because the style should not be in the doucment.");
is(shadow.styleSheets.length, 0, "Inert link should not add style to ShadowRoot.");
// Remove link to make sure we don't get assertions.
shadow.removeChild(shadowStyle);
SimpleTest.finish();
};
SimpleTest.finish();
</script>
</body>

View file

@ -48,19 +48,12 @@ isnot(getComputedStyle(document.getElementById("bodydiv"), null).getPropertyValu
// Make sure that elements in the ShadowRoot are styled according to the ShadowRoot style.
is(getComputedStyle(divToStyle, null).getPropertyValue("height"), "100px", "ShadowRoot style sheets should apply to elements in ShadowRoot.");
// Tests for applyAuthorStyles.
// Tests for author styles not applying in a ShadowRoot.
var authorStyle = document.createElement("style");
authorStyle.innerHTML = ".fat { padding-right: 20px; padding-left: 30px; }";
document.body.appendChild(authorStyle);
is(root.applyAuthorStyles, false, "applyAuthorStyles defaults to false.");
isnot(getComputedStyle(divToStyle, null).getPropertyValue("padding-right"), "20px", "Author styles should not apply to ShadowRoot when ShadowRoot.applyAuthorStyles is false.");
root.applyAuthorStyles = true;
is(root.applyAuthorStyles, true, "applyAuthorStyles was set to true.");
is(getComputedStyle(divToStyle, null).getPropertyValue("padding-right"), "20px", "Author styles should apply to ShadowRoot when ShadowRoot.applyAuthorStyles is true.");
root.applyAuthorStyles = false;
is(root.applyAuthorStyles, false, "applyAuthorStyles was set to false.");
isnot(getComputedStyle(divToStyle, null).getPropertyValue("padding-right"), "20px", "Author styles should not apply to ShadowRoot when ShadowRoot.applyAuthorStyles is false.");
isnot(iframeWin.getComputedStyle(divToStyle, null).getPropertyValue("padding-right"), "20px", "Author styles should not apply to ShadowRoot.");
// Test dynamic changes to style in ShadowRoot.
root.innerHTML = '<div id="divtostyle" class="dummy"></div>';

View file

@ -238,10 +238,6 @@ partial interface Document {
readonly attribute boolean fullscreenEnabled;
[BinaryName="fullscreenEnabled"]
readonly attribute boolean mozFullScreenEnabled;
[LenientSetter, Func="nsDocument::IsUnprefixedFullscreenEnabled"]
readonly attribute Element? fullscreenElement;
[BinaryName="fullscreenElement"]
readonly attribute Element? mozFullScreenElement;
[Func="nsDocument::IsUnprefixedFullscreenEnabled"]
void exitFullscreen();
@ -258,7 +254,6 @@ partial interface Document {
// https://w3c.github.io/pointerlock/#extensions-to-the-document-interface
// https://w3c.github.io/pointerlock/#extensions-to-the-documentorshadowroot-mixin
partial interface Document {
readonly attribute Element? pointerLockElement;
[BinaryName="pointerLockElement", Pref="pointer-lock-api.prefixed.enabled"]
readonly attribute Element? mozPointerLockElement;
void exitPointerLock();
@ -292,8 +287,6 @@ partial interface Document {
// http://dev.w3.org/csswg/cssom-view/#extensions-to-the-document-interface
partial interface Document {
Element? elementFromPoint (float x, float y);
sequence<Element> elementsFromPoint (float x, float y);
CaretPosition? caretPositionFromPoint (float x, float y);
readonly attribute Element? scrollingElement;

View file

@ -12,18 +12,17 @@
interface DocumentOrShadowRoot {
// Not implemented yet: bug 1430308.
// Selection? getSelection();
// Not implemented yet: bug 1430301.
// Element? elementFromPoint (float x, float y);
// Not implemented yet: bug 1430301.
// sequence<Element> elementsFromPoint (float x, float y);
Element? elementFromPoint (float x, float y);
sequence<Element> elementsFromPoint (float x, float y);
// Not implemented yet: bug 1430307.
// CaretPosition? caretPositionFromPoint (float x, float y);
readonly attribute Element? activeElement;
readonly attribute StyleSheetList styleSheets;
// Not implemented yet: bug 1430303.
// readonly attribute Element? pointerLockElement;
// Not implemented yet: bug 1430305.
// readonly attribute Element? fullscreenElement;
readonly attribute Element? pointerLockElement;
[LenientSetter, Func="nsDocument::IsUnprefixedFullscreenEnabled"]
readonly attribute Element? fullscreenElement;
[BinaryName="fullscreenElement"]
readonly attribute Element? mozFullScreenElement;
};

View file

@ -244,10 +244,6 @@ partial interface Element {
readonly attribute HTMLSlotElement? assignedSlot;
[CEReactions, Unscopable, SetterThrows, Func="nsDocument::IsWebComponentsEnabled"]
attribute DOMString slot;
// [deprecated] Shadow DOM v0
[Throws, Func="nsDocument::IsWebComponentsEnabled"]
ShadowRoot createShadowRoot();
};
Element implements ChildNode;

View file

@ -31,6 +31,5 @@ interface ShadowRoot : DocumentFragment
HTMLCollection getElementsByClassName(DOMString classNames);
[CEReactions, SetterThrows, TreatNullAs=EmptyString]
attribute DOMString innerHTML;
attribute boolean applyAuthorStyles;
};

View file

@ -321,10 +321,11 @@ nsXBLBinding::GenerateAnonymousContent()
if (hasContent) {
nsIDocument* doc = mBoundElement->OwnerDoc();
nsCOMPtr<nsINode> clonedNode;
nsCOMArray<nsINode> nodesWithProperties;
nsNodeUtils::Clone(content, true, doc->NodeInfoManager(),
nodesWithProperties, getter_AddRefs(clonedNode));
IgnoredErrorResult rv;
nsCOMPtr<nsINode> clonedNode =
nsNodeUtils::Clone(content, true, doc->NodeInfoManager(), nullptr, rv);
// FIXME: Bug 1399558, Why is this code OK assuming that nsNodeUtils::Clone
// never fails?
mContent = clonedNode->AsElement();
// Search for <xbl:children> elements in the XBL content. In the presence

View file

@ -155,6 +155,8 @@ NS_NewDOMDocument(nsIDOMDocument** aInstancePtrResult,
if (aDoctype) {
nsCOMPtr<nsIDOMNode> tmpNode;
rv = doc->AppendChild(aDoctype, getter_AddRefs(tmpNode));
// TODO: if we choose to land bug 1318479, make sure to call
// result.WouldReportJSException() before stealing the NSResult.
NS_ENSURE_SUCCESS(rv, rv);
}
@ -167,6 +169,8 @@ NS_NewDOMDocument(nsIDOMDocument** aInstancePtrResult,
nsCOMPtr<nsIDOMNode> tmpNode;
rv = doc->AppendChild(root, getter_AddRefs(tmpNode));
// TODO: if we choose to land bug 1318479, make sure to call
// result.WouldReportJSException() before stealing the NSResult.
NS_ENSURE_SUCCESS(rv, rv);
}

View file

@ -682,7 +682,7 @@ nsEditorSpellCheck::UpdateCurrentDictionary(nsIEditorSpellCheckCallback* aCallba
RefPtr<DictionaryFetcher> fetcher =
new DictionaryFetcher(this, aCallback, mDictionaryFetcherGroup);
rootContent->GetLang(fetcher->mRootContentLang);
nsCOMPtr<nsIDocument> doc = rootContent->GetUncomposedDoc();
nsCOMPtr<nsIDocument> doc = rootContent->GetComposedDoc();
NS_ENSURE_STATE(doc);
doc->GetContentLanguage(fetcher->mRootDocContentLang);

View file

@ -1114,7 +1114,7 @@ EditorEventListener::Focus(WidgetEvent* aFocusEvent)
return NS_OK;
}
nsCOMPtr<nsIDOMEventTarget> target = aFocusEvent->GetDOMEventTarget();
nsCOMPtr<nsIDOMEventTarget> target = aFocusEvent->GetOriginalDOMEventTarget();
nsCOMPtr<nsINode> node = do_QueryInterface(target);
NS_ENSURE_TRUE(node, NS_ERROR_UNEXPECTED);
@ -1126,13 +1126,15 @@ EditorEventListener::Focus(WidgetEvent* aFocusEvent)
}
if (node->IsNodeOfType(nsINode::eCONTENT)) {
nsIContent* content =
node->AsContent()->FindFirstNonChromeOnlyAccessContent();
// XXX If the focus event target is a form control in contenteditable
// element, perhaps, the parent HTML editor should do nothing by this
// handler. However, FindSelectionRoot() returns the root element of the
// contenteditable editor. So, the editableRoot value is invalid for
// the plain text editor, and it will be set to the wrong limiter of
// the selection. However, fortunately, actual bugs are not found yet.
nsCOMPtr<nsIContent> editableRoot = editorBase->FindSelectionRoot(node);
nsCOMPtr<nsIContent> editableRoot = editorBase->FindSelectionRoot(content);
// make sure that the element is really focused in case an earlier
// listener in the chain changed the focus.

View file

@ -7986,16 +7986,20 @@ HTMLEditRules::ConfirmSelectionInBody()
{
// get the body
NS_ENSURE_STATE(mHTMLEditor);
nsCOMPtr<nsIDOMElement> rootElement = do_QueryInterface(mHTMLEditor->GetRoot());
NS_ENSURE_TRUE(rootElement, NS_ERROR_UNEXPECTED);
RefPtr<Element> rootElement = mHTMLEditor->GetRoot();
if (NS_WARN_IF(!rootElement)) {
return NS_ERROR_UNEXPECTED;
}
// get the selection
NS_ENSURE_STATE(mHTMLEditor);
RefPtr<Selection> selection = mHTMLEditor->GetSelection();
NS_ENSURE_STATE(selection);
if (NS_WARN_IF(!selection)) {
return NS_ERROR_UNEXPECTED;
}
// get the selection start location
nsCOMPtr<nsIDOMNode> selNode, temp, parent;
nsCOMPtr<nsINode> selNode;
int32_t selOffset;
NS_ENSURE_STATE(mHTMLEditor);
nsresult rv =
@ -8005,12 +8009,11 @@ HTMLEditRules::ConfirmSelectionInBody()
return rv;
}
temp = selNode;
nsINode* temp = selNode;
// check that selNode is inside body
while (temp && !TextEditUtils::IsBody(temp)) {
temp->GetParentNode(getter_AddRefs(parent));
temp = parent;
while (temp && !temp->IsHTMLElement(nsGkAtoms::body)) {
temp = temp->GetParentOrHostNode();
}
// if we aren't in the body, force the issue
@ -8018,6 +8021,7 @@ HTMLEditRules::ConfirmSelectionInBody()
// uncomment this to see when we get bad selections
// NS_NOTREACHED("selection not in body");
selection->Collapse(rootElement, 0);
return NS_OK;
}
// get the selection end location
@ -8028,9 +8032,8 @@ HTMLEditRules::ConfirmSelectionInBody()
temp = selNode;
// check that selNode is inside body
while (temp && !TextEditUtils::IsBody(temp)) {
rv = temp->GetParentNode(getter_AddRefs(parent));
temp = parent;
while (temp && !temp->IsHTMLElement(nsGkAtoms::body)) {
temp = temp->GetParentOrHostNode();
}
// if we aren't in the body, force the issue
@ -8040,9 +8043,7 @@ HTMLEditRules::ConfirmSelectionInBody()
selection->Collapse(rootElement, 0);
}
// XXX This is the result of the last call of GetParentNode(), it doesn't
// make sense...
return rv;
return NS_OK;
}
nsresult

View file

@ -381,13 +381,14 @@ HTMLEditor::FindSelectionRoot(nsINode* aNode)
aNode->IsNodeOfType(nsINode::eCONTENT),
"aNode must be content or document node");
nsCOMPtr<nsIDocument> doc = aNode->GetUncomposedDoc();
nsCOMPtr<nsIDocument> doc = aNode->GetComposedDoc();
if (!doc) {
return nullptr;
}
nsCOMPtr<nsIContent> content;
if (doc->HasFlag(NODE_IS_EDITABLE) || !aNode->IsContent()) {
if (aNode->IsInUncomposedDoc() &&
doc->HasFlag(NODE_IS_EDITABLE) || !aNode->IsContent()) {
content = doc->GetRootElement();
return content.forget();
}
@ -5135,7 +5136,11 @@ HTMLEditor::IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent)
return true;
}
nsCOMPtr<nsIDOMEventTarget> target = aGUIEvent->GetDOMEventTarget();
nsCOMPtr<nsIDOMEventTarget> target = aGUIEvent->GetOriginalDOMEventTarget();
nsCOMPtr<nsIContent> content = do_QueryInterface(target);
if (content) {
target = content->FindFirstNonChromeOnlyAccessContent();
}
NS_ENSURE_TRUE(target, false);
nsCOMPtr<nsIDocument> document = GetDocument();

View file

@ -1483,8 +1483,9 @@ nsresult mozInlineSpellChecker::DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
// Now check that we're still looking at a range that's under
// aWordUtil.GetRootNode()
nsINode* rootNode = aWordUtil.GetRootNode();
if (!nsContentUtils::ContentIsDescendantOf(beginNode, rootNode) ||
!nsContentUtils::ContentIsDescendantOf(endNode, rootNode)) {
if (!beginNode->IsInComposedDoc() || !endNode->IsInComposedDoc() ||
!nsContentUtils::ContentIsShadowIncludingDescendantOf(beginNode, rootNode) ||
!nsContentUtils::ContentIsShadowIncludingDescendantOf(endNode, rootNode)) {
// Just bail out and don't try to spell-check this
return NS_OK;
}

View file

@ -214,7 +214,7 @@ bool
js::intl_FormatRelativeTime(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 3);
MOZ_ASSERT(args.length() == 4);
RootedObject relativeTimeFormat(cx, &args[0].toObject());

View file

@ -10,7 +10,7 @@
}
connectedCallback() {
let shadow = this.createShadowRoot();
let shadow = this.attachShadow({ mode: "open" });
if (this.template) {
let te = document.createElement('template');
te.innerHTML = this.template;

View file

@ -1,16 +0,0 @@
<!doctype html>
<script>
// Test for content redistribution outside of the document.
// Passes if it doesn't assert.
let host = document.createElement('div');
host.innerHTML = "<div id='foo'></div>";
let shadowRoot = host.createShadowRoot();
shadowRoot.innerHTML = "<content select='#foo'></content>";
host.firstElementChild.removeAttribute('id');
// Move to the document, do the same.
document.documentElement.appendChild(host);
host.firstElementChild.setAttribute('id', 'foo');
</script>

View file

@ -484,6 +484,5 @@ load 1308793.svg
load 1308848-1.html
load 1308848-2.html
asserts(0-1) load 1343606.html # bug 1343948
load 1404789-1.html
load 1404789-2.html
load 1419762.html

View file

@ -796,21 +796,23 @@ public:
nsCOMArray<nsIContent> mGeneratedTextNodesWithInitializer;
TreeMatchContext mTreeMatchContext;
TreeMatchContext& mTreeMatchContext;
// Constructor
// Use the passed-in history state.
nsFrameConstructorState(
nsIPresShell* aPresShell,
TreeMatchContext& aTreeMatchContext,
nsContainerFrame* aFixedContainingBlock,
nsContainerFrame* aAbsoluteContainingBlock,
nsContainerFrame* aFloatContainingBlock,
already_AddRefed<nsILayoutHistoryState> aHistoryState);
// Get the history state from the pres context's pres shell.
nsFrameConstructorState(nsIPresShell* aPresShell,
nsContainerFrame* aFixedContainingBlock,
nsContainerFrame* aAbsoluteContainingBlock,
nsContainerFrame* aFloatContainingBlock);
nsFrameConstructorState(nsIPresShell* aPresShell,
TreeMatchContext& aTreeMatchContext,
nsContainerFrame* aFixedContainingBlock,
nsContainerFrame* aAbsoluteContainingBlock,
nsContainerFrame* aFloatContainingBlock);
~nsFrameConstructorState();
@ -963,6 +965,7 @@ protected:
nsFrameConstructorState::nsFrameConstructorState(
nsIPresShell* aPresShell,
TreeMatchContext& aTreeMatchContext,
nsContainerFrame* aFixedContainingBlock,
nsContainerFrame* aAbsoluteContainingBlock,
nsContainerFrame* aFloatContainingBlock,
@ -989,8 +992,7 @@ nsFrameConstructorState::nsFrameConstructorState(
mFixedPosIsAbsPos(aFixedContainingBlock == aAbsoluteContainingBlock),
mHavePendingPopupgroup(false),
mCreatingExtraFrames(false),
mTreeMatchContext(true, nsRuleWalker::eRelevantLinkUnvisited,
aPresShell->GetDocument()),
mTreeMatchContext(aTreeMatchContext),
mCurrentPendingBindingInsertionPoint(nullptr)
{
#ifdef MOZ_XUL
@ -1003,10 +1005,13 @@ nsFrameConstructorState::nsFrameConstructorState(
}
nsFrameConstructorState::nsFrameConstructorState(nsIPresShell* aPresShell,
TreeMatchContext& aTreeMatchContext,
nsContainerFrame* aFixedContainingBlock,
nsContainerFrame* aAbsoluteContainingBlock,
nsContainerFrame* aFloatContainingBlock)
: nsFrameConstructorState(aPresShell, aFixedContainingBlock,
: nsFrameConstructorState(aPresShell,
aTreeMatchContext,
aFixedContainingBlock,
aAbsoluteContainingBlock,
aFloatContainingBlock,
aPresShell->GetDocument()->GetLayoutHistoryState())
@ -2417,13 +2422,15 @@ nsCSSFrameConstructor::ConstructDocElementFrame(Element* aDocEle
NS_ASSERTION(mDocElementContainingBlock, "Should have parent by now");
TreeMatchContext matchContext(mDocument, TreeMatchContext::ForFrameConstruction);
// Initialize the ancestor filter with null for now; we'll push
// aDocElement once we finish resolving style for it.
matchContext.InitAncestors(nullptr);
nsFrameConstructorState state(mPresShell,
matchContext,
GetAbsoluteContainingBlock(mDocElementContainingBlock, FIXED_POS),
nullptr,
nullptr, do_AddRef(aFrameState));
// Initialize the ancestor filter with null for now; we'll push
// aDocElement once we finish resolving style for it.
state.mTreeMatchContext.InitAncestors(nullptr);
// XXXbz why, exactly?
if (!mTempFrameTreeState)
@ -2856,7 +2863,8 @@ nsCSSFrameConstructor::SetUpDocElementContainingBlock(nsIContent* aDocElement)
RefPtr<nsStyleContext> rootPseudoStyle;
// we must create a state because if the scrollbars are GFX it needs the
// state to build the scrollbar frames.
nsFrameConstructorState state(mPresShell, nullptr, nullptr, nullptr);
TreeMatchContext matchContext(mDocument, TreeMatchContext::ForFrameConstruction);
nsFrameConstructorState state(mPresShell, matchContext, nullptr, nullptr, nullptr);
// Start off with the viewport as parent; we'll adjust it as needed.
nsContainerFrame* parentFrame = viewportFrame;
@ -7023,12 +7031,16 @@ nsCSSFrameConstructor::MaybeConstructLazily(Operation aOperation,
}
void
nsCSSFrameConstructor::CreateNeededFrames(nsIContent* aContent)
nsCSSFrameConstructor::CreateNeededFrames(nsIContent* aContent,
TreeMatchContext& aTreeMatchContext)
{
NS_ASSERTION(!aContent->HasFlag(NODE_NEEDS_FRAME),
"shouldn't get here with a content node that has needs frame bit set");
NS_ASSERTION(aContent->HasFlag(NODE_DESCENDANTS_NEED_FRAMES),
"should only get here with a content node that has descendants needing frames");
NS_ASSERTION(aTreeMatchContext.mAncestorFilter.HasFilter(),
"The whole point of having the tree match context is optimizing "
"the ancestor filter usage!");
aContent->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES);
@ -7064,19 +7076,31 @@ nsCSSFrameConstructor::CreateNeededFrames(nsIContent* aContent)
inRun = false;
// generate a ContentRangeInserted for [startOfRun,i)
ContentRangeInserted(aContent, firstChildInRun, child, nullptr,
false);
false, &aTreeMatchContext);
}
}
}
if (inRun) {
ContentAppended(aContent, firstChildInRun, false);
ContentAppended(aContent, firstChildInRun, false, &aTreeMatchContext);
}
// Now descend.
FlattenedChildIterator iter(aContent);
for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
if (child->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
CreateNeededFrames(child);
TreeMatchContext::AutoAncestorPusher insertionPointPusher(
aTreeMatchContext);
// Handle stuff like xbl:children.
if (child->GetParent() != aContent && child->GetParent()->IsElement()) {
insertionPointPusher.PushAncestorAndStyleScope(
child->GetParent()->AsElement());
}
TreeMatchContext::AutoAncestorPusher pusher(aTreeMatchContext);
pusher.PushAncestorAndStyleScope(child);
CreateNeededFrames(child, aTreeMatchContext);
}
}
}
@ -7091,7 +7115,9 @@ void nsCSSFrameConstructor::CreateNeededFrames()
"root element should not have frame created lazily");
if (rootElement && rootElement->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
BeginUpdate();
CreateNeededFrames(rootElement);
TreeMatchContext treeMatchContext(mDocument, TreeMatchContext::ForFrameConstruction);
treeMatchContext.InitAncestors(rootElement);
CreateNeededFrames(rootElement, treeMatchContext);
EndUpdate();
}
}
@ -7208,10 +7234,13 @@ nsCSSFrameConstructor::MaybeRecreateForFrameset(nsIFrame* aParentFrame,
}
void
nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
nsIContent* aFirstNewContent,
bool aAllowLazyConstruction)
nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
nsIContent* aFirstNewContent,
bool aAllowLazyConstruction,
TreeMatchContext* aProvidedTreeMatchContext)
{
MOZ_ASSERT_IF(aProvidedTreeMatchContext, !aAllowLazyConstruction);
AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
NS_PRECONDITION(mUpdateCount != 0,
"Should be in an update while creating frames");
@ -7391,13 +7420,23 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
}
// Create some new frames
//
// We use the provided tree match context, or create a new one on the fly
// otherwise.
Maybe<TreeMatchContext> matchContext;
if (!aProvidedTreeMatchContext) {
matchContext.emplace(mDocument, TreeMatchContext::ForFrameConstruction);
// We use GetParentElementCrossingShadowRoot to handle the case where
// aContainer is a ShadowRoot.
matchContext->InitAncestors(aFirstNewContent->GetParentElementCrossingShadowRoot());
}
nsFrameConstructorState state(mPresShell,
aProvidedTreeMatchContext
? *aProvidedTreeMatchContext
: *matchContext,
GetAbsoluteContainingBlock(parentFrame, FIXED_POS),
GetAbsoluteContainingBlock(parentFrame, ABS_POS),
containingBlock);
// We use GetParentElementCrossingShadowRoot to handle the case where
// aContainer is a ShadowRoot.
state.mTreeMatchContext.InitAncestors(aFirstNewContent->GetParentElementCrossingShadowRoot());
nsIAtom* frameType = parentFrame->GetType();
@ -7606,7 +7645,8 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer,
nsIContent* aStartChild,
nsIContent* aEndChild,
nsILayoutHistoryState* aFrameState,
bool aAllowLazyConstruction)
bool aAllowLazyConstruction,
TreeMatchContext* aProvidedTreeMatchContext)
{
AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
NS_PRECONDITION(mUpdateCount != 0,
@ -7839,14 +7879,21 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer,
return;
}
Maybe<TreeMatchContext> matchContext;
if (!aProvidedTreeMatchContext) {
matchContext.emplace(mDocument, TreeMatchContext::ForFrameConstruction);
// We use GetParentElementCrossingShadowRoot to handle the case where
// aContainer is a ShadowRoot.
matchContext->InitAncestors(aStartChild->GetParentElementCrossingShadowRoot());
}
nsFrameConstructorState state(mPresShell,
aProvidedTreeMatchContext
? *aProvidedTreeMatchContext
: *matchContext,
GetAbsoluteContainingBlock(insertion.mParentFrame, FIXED_POS),
GetAbsoluteContainingBlock(insertion.mParentFrame, ABS_POS),
GetFloatContainingBlock(insertion.mParentFrame),
do_AddRef(aFrameState));
// We use GetParentElementCrossingShadowRoot to handle the case where
// aContainer is a ShadowRoot.
state.mTreeMatchContext.InitAncestors(aStartChild->GetParentElementCrossingShadowRoot());
// Recover state for the containing block - we need to know if
// it has :first-letter or :first-line style applied to it. The
@ -8695,7 +8742,9 @@ nsCSSFrameConstructor::CreateContinuingTableFrame(nsIPresShell* aPresShell,
// Replicate the header/footer frame.
nsTableRowGroupFrame* headerFooterFrame;
nsFrameItems childItems;
TreeMatchContext matchContext(mDocument, TreeMatchContext::ForFrameConstruction);
nsFrameConstructorState state(mPresShell,
matchContext,
GetAbsoluteContainingBlock(newFrame, FIXED_POS),
GetAbsoluteContainingBlock(newFrame, ABS_POS),
nullptr);
@ -8969,7 +9018,10 @@ nsCSSFrameConstructor::ReplicateFixedFrames(nsPageContentFrame* aParentFrame)
// This should not normally be possible (because fixed-pos elements should
// be absolute containers) but fixed-pos tables currently aren't abs-pos
// containers.
nsFrameConstructorState state(mPresShell, aParentFrame,
TreeMatchContext matchContext(mDocument, TreeMatchContext::ForFrameConstruction);
nsFrameConstructorState state(mPresShell,
matchContext,
aParentFrame,
nullptr,
mRootElementFrame);
state.mCreatingExtraFrames = true;
@ -11109,7 +11161,10 @@ nsCSSFrameConstructor::CreateLetterFrame(nsContainerFrame* aBlockFrame,
NS_ASSERTION(aBlockContinuation == GetFloatContainingBlock(aParentFrame),
"Containing block is confused");
TreeMatchContext matchContext(mDocument,
TreeMatchContext::ForFrameConstruction);
nsFrameConstructorState state(mPresShell,
matchContext,
GetAbsoluteContainingBlock(aParentFrame, FIXED_POS),
GetAbsoluteContainingBlock(aParentFrame, ABS_POS),
aBlockContinuation);
@ -11485,7 +11540,10 @@ nsCSSFrameConstructor::CreateListBoxContent(nsContainerFrame* aParentFrame,
// Construct a new frame
if (nullptr != aParentFrame) {
nsFrameItems frameItems;
nsFrameConstructorState state(mPresShell, GetAbsoluteContainingBlock(aParentFrame, FIXED_POS),
TreeMatchContext matchContext(mDocument, TreeMatchContext::ForFrameConstruction);
nsFrameConstructorState state(mPresShell,
matchContext,
GetAbsoluteContainingBlock(aParentFrame, FIXED_POS),
GetAbsoluteContainingBlock(aParentFrame, ABS_POS),
GetFloatContainingBlock(aParentFrame),
do_AddRef(mTempFrameTreeState.get()));
@ -12376,7 +12434,8 @@ nsCSSFrameConstructor::GenerateChildFrames(nsContainerFrame* aFrame)
BeginUpdate();
nsFrameItems childItems;
nsFrameConstructorState state(mPresShell, nullptr, nullptr, nullptr);
TreeMatchContext matchContext(mDocument, TreeMatchContext::ForFrameConstruction);
nsFrameConstructorState state(mPresShell, matchContext, nullptr, nullptr, nullptr);
// We don't have a parent frame with a pending binding constructor here,
// so no need to worry about ordering of the kids' constructors with it.
// Pass null for the PendingBinding.

Some files were not shown because too many files have changed in this diff Show more