diff options
Diffstat (limited to 'dom/base')
98 files changed, 2591 insertions, 2375 deletions
diff --git a/dom/base/AnonymousContent.cpp b/dom/base/AnonymousContent.cpp index 1df36b048..aea923f2b 100644 --- a/dom/base/AnonymousContent.cpp +++ b/dom/base/AnonymousContent.cpp @@ -7,6 +7,7 @@ #include "AnonymousContent.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/AnonymousContentBinding.h" +#include "nsComputedDOMStyle.h" #include "nsCycleCollectionParticipant.h" #include "nsIDocument.h" #include "nsIDOMHTMLCollection.h" @@ -208,5 +209,31 @@ AnonymousContent::WrapObject(JSContext* aCx, return AnonymousContentBinding::Wrap(aCx, this, aGivenProto, aReflector); } +void +AnonymousContent::GetComputedStylePropertyValue(const nsAString& aElementId, + const nsAString& aPropertyName, + DOMString& aResult, + ErrorResult& aRv) +{ + Element* element = GetElementById(aElementId); + if (!element) { + aRv.Throw(NS_ERROR_NOT_AVAILABLE); + return; + } + + nsIPresShell* shell = element->OwnerDoc()->GetShell(); + if (!shell) { + aRv.Throw(NS_ERROR_NOT_AVAILABLE); + return; + } + + RefPtr<nsComputedDOMStyle> cs = + new nsComputedDOMStyle(element, + NS_LITERAL_STRING(""), + element->OwnerDoc(), + nsComputedDOMStyle::eAll); + aRv = cs->GetPropertyValue(aPropertyName, aResult); +} + } // namespace dom } // namespace mozilla diff --git a/dom/base/AnonymousContent.h b/dom/base/AnonymousContent.h index fd3b59c44..b56c14595 100644 --- a/dom/base/AnonymousContent.h +++ b/dom/base/AnonymousContent.h @@ -68,6 +68,11 @@ public: const Sequence<OwningNonNull<DOMRect>>& aRects, ErrorResult& aError); + void GetComputedStylePropertyValue(const nsAString& aElementId, + const nsAString& aPropertyName, + DOMString& aResult, + ErrorResult& aRv); + private: ~AnonymousContent(); nsCOMPtr<Element> mContentNode; diff --git a/dom/base/Attr.cpp b/dom/base/Attr.cpp index 71b559392..df05ef5cb 100644 --- a/dom/base/Attr.cpp +++ b/dom/base/Attr.cpp @@ -344,7 +344,7 @@ Attr::RemoveChildAt(uint32_t aIndex, bool aNotify) } nsresult -Attr::PreHandleEvent(EventChainPreVisitor& aVisitor) +Attr::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.mCanHandle = true; return NS_OK; diff --git a/dom/base/Attr.h b/dom/base/Attr.h index 3f80030d2..39ac8b195 100644 --- a/dom/base/Attr.h +++ b/dom/base/Attr.h @@ -54,7 +54,7 @@ public: // nsIDOMAttr interface NS_DECL_NSIDOMATTR - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent(EventChainPreVisitor& aVisitor) override; // nsIAttribute interface void SetMap(nsDOMAttributeMap *aMap) override; diff --git a/dom/base/BlobSet.h b/dom/base/BlobSet.h index 3e22955dd..bf98cb023 100644 --- a/dom/base/BlobSet.h +++ b/dom/base/BlobSet.h @@ -8,6 +8,9 @@ #define mozilla_dom_BlobSet_h #include "mozilla/RefPtr.h" +#include "jsfriendapi.h" +#include "nsString.h" +#include "nsTArray.h" namespace mozilla { namespace dom { diff --git a/dom/base/CORSMode.h b/dom/base/CORSMode.h index 82f2f570d..7a2b4c579 100644 --- a/dom/base/CORSMode.h +++ b/dom/base/CORSMode.h @@ -4,6 +4,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include <stdint.h> + #ifndef CORSMode_h_ #define CORSMode_h_ diff --git a/dom/base/ChildIterator.cpp b/dom/base/ChildIterator.cpp index d8c454ae8..19156aff7 100644 --- a/dom/base/ChildIterator.cpp +++ b/dom/base/ChildIterator.cpp @@ -6,61 +6,27 @@ #include "ChildIterator.h" #include "nsContentUtils.h" +#include "mozilla/dom/HTMLSlotElement.h" #include "mozilla/dom/XBLChildrenElement.h" -#include "mozilla/dom/HTMLContentElement.h" -#include "mozilla/dom/HTMLShadowElement.h" #include "mozilla/dom/ShadowRoot.h" #include "nsIAnonymousContentCreator.h" #include "nsIFrame.h" #include "nsCSSAnonBoxes.h" +#include "nsDocument.h" namespace mozilla { namespace dom { -class MatchedNodes { -public: - explicit MatchedNodes(HTMLContentElement* aInsertionPoint) - : mIsContentElement(true), mContentElement(aInsertionPoint) {} - - explicit MatchedNodes(XBLChildrenElement* aInsertionPoint) - : mIsContentElement(false), mChildrenElement(aInsertionPoint) {} - - uint32_t Length() const - { - return mIsContentElement ? mContentElement->MatchedNodes().Length() - : mChildrenElement->InsertedChildrenLength(); - } - - nsIContent* operator[](int32_t aIndex) const - { - return mIsContentElement ? mContentElement->MatchedNodes()[aIndex] - : mChildrenElement->InsertedChild(aIndex); - } - - bool IsEmpty() const - { - return mIsContentElement ? mContentElement->MatchedNodes().IsEmpty() - : !mChildrenElement->HasInsertedChildren(); - } -protected: - bool mIsContentElement; - union { - HTMLContentElement* mContentElement; - XBLChildrenElement* mChildrenElement; - }; -}; - -static inline MatchedNodes -GetMatchedNodesForPoint(nsIContent* aContent) +ExplicitChildIterator::ExplicitChildIterator(const nsIContent* aParent, + bool aStartAtBeginning) + : mParent(aParent), + mChild(nullptr), + mDefaultChild(nullptr), + mIsFirst(aStartAtBeginning), + mIndexInInserted(0) { - if (aContent->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) { - // XBL case - return MatchedNodes(static_cast<XBLChildrenElement*>(aContent)); - } - - // Web components case - MOZ_ASSERT(aContent->IsHTMLElement(nsGkAtoms::content)); - return MatchedNodes(HTMLContentElement::FromContent(aContent)); + mParentAsSlot = nsDocument::IsWebComponentsEnabled(mParent) ? + HTMLSlotElement::FromContent(mParent) : nullptr; } nsIContent* @@ -69,30 +35,29 @@ ExplicitChildIterator::GetNextChild() // If we're already in the inserted-children array, look there first if (mIndexInInserted) { MOZ_ASSERT(mChild); - MOZ_ASSERT(nsContentUtils::IsContentInsertionPoint(mChild)); MOZ_ASSERT(!mDefaultChild); - MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild); - if (mIndexInInserted < assignedChildren.Length()) { - return assignedChildren[mIndexInInserted++]; - } - mIndexInInserted = 0; - mChild = mChild->GetNextSibling(); - } else if (mShadowIterator) { - // If we're inside of a <shadow> element, look through the - // explicit children of the projected ShadowRoot via - // the mShadowIterator. - nsIContent* nextChild = mShadowIterator->GetNextChild(); - if (nextChild) { - return nextChild; + if (mParentAsSlot) { + const nsTArray<RefPtr<nsINode>>& assignedNodes = + mParentAsSlot->AssignedNodes(); + + mChild = (mIndexInInserted < assignedNodes.Length()) ? + assignedNodes[mIndexInInserted++]->AsContent() : nullptr; + return mChild; } - mShadowIterator = nullptr; + MOZ_ASSERT(mChild->IsActiveChildrenElement()); + auto* childrenElement = + static_cast<XBLChildrenElement*>(mChild); + if (mIndexInInserted < childrenElement->InsertedChildrenLength()) { + return childrenElement->InsertedChild(mIndexInInserted++); + } + mIndexInInserted = 0; mChild = mChild->GetNextSibling(); } else if (mDefaultChild) { // If we're already in default content, check if there are more nodes there MOZ_ASSERT(mChild); - MOZ_ASSERT(nsContentUtils::IsContentInsertionPoint(mChild)); + MOZ_ASSERT(mChild->IsActiveChildrenElement()); mDefaultChild = mDefaultChild->GetNextSibling(); if (mDefaultChild) { @@ -101,6 +66,19 @@ ExplicitChildIterator::GetNextChild() mChild = mChild->GetNextSibling(); } else if (mIsFirst) { // at the beginning of the child list + // For slot parent, iterate over assigned nodes if not empty, otherwise + // fall through and iterate over direct children (fallback content). + if (mParentAsSlot) { + const nsTArray<RefPtr<nsINode>>& assignedNodes = + mParentAsSlot->AssignedNodes(); + if (!assignedNodes.IsEmpty()) { + mIndexInInserted = 1; + mChild = assignedNodes[0]->AsContent(); + mIsFirst = false; + return mChild; + } + } + mChild = mParent->GetFirstChild(); mIsFirst = false; } else if (mChild) { // in the middle of the child list @@ -110,31 +88,16 @@ ExplicitChildIterator::GetNextChild() // Iterate until we find a non-insertion point, or an insertion point with // content. while (mChild) { - // If the current child being iterated is a shadow insertion point then - // the iterator needs to go into the projected ShadowRoot. - if (ShadowRoot::IsShadowInsertionPoint(mChild)) { - // Look for the next child in the projected ShadowRoot for the <shadow> - // element. - HTMLShadowElement* shadowElem = HTMLShadowElement::FromContent(mChild); - ShadowRoot* projectedShadow = shadowElem->GetOlderShadowRoot(); - if (projectedShadow) { - mShadowIterator = new ExplicitChildIterator(projectedShadow); - nsIContent* nextChild = mShadowIterator->GetNextChild(); - if (nextChild) { - return nextChild; - } - mShadowIterator = nullptr; - } - mChild = mChild->GetNextSibling(); - } else if (nsContentUtils::IsContentInsertionPoint(mChild)) { + if (mChild->IsActiveChildrenElement()) { // If the current child being iterated is a content insertion point // then the iterator needs to return the nodes distributed into // the content insertion point. - MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild); - if (!assignedChildren.IsEmpty()) { + auto* childrenElement = + static_cast<XBLChildrenElement*>(mChild); + if (childrenElement->HasInsertedChildren()) { // Iterate through elements projected on insertion point. mIndexInInserted = 1; - return assignedChildren[0]; + return childrenElement->InsertedChild(0); } // Insertion points inside fallback/default content @@ -192,19 +155,17 @@ FlattenedChildIterator::Init(bool aIgnoreXBL) } bool -ExplicitChildIterator::Seek(nsIContent* aChildToFind) +ExplicitChildIterator::Seek(const nsIContent* aChildToFind) { if (aChildToFind->GetParent() == mParent && !aChildToFind->IsRootOfAnonymousSubtree()) { // Fast path: just point ourselves to aChildToFind, which is a // normal DOM child of ours. - MOZ_ASSERT(!ShadowRoot::IsShadowInsertionPoint(aChildToFind)); - MOZ_ASSERT(!nsContentUtils::IsContentInsertionPoint(aChildToFind)); - mChild = aChildToFind; + mChild = const_cast<nsIContent*>(aChildToFind); mIndexInInserted = 0; - mShadowIterator = nullptr; mDefaultChild = nullptr; mIsFirst = false; + MOZ_ASSERT(!mChild->IsActiveChildrenElement()); return true; } @@ -220,12 +181,18 @@ ExplicitChildIterator::Get() const { MOZ_ASSERT(!mIsFirst); + // When mParentAsSlot is set, mChild is always set to the current child. It + // does not matter whether mChild is an assigned node or a fallback content. + if (mParentAsSlot) { + return mChild; + } + if (mIndexInInserted) { - MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild); - return assignedChildren[mIndexInInserted - 1]; - } else if (mShadowIterator) { - return mShadowIterator->Get(); + MOZ_ASSERT(mChild->IsActiveChildrenElement()); + auto* childrenElement = static_cast<XBLChildrenElement*>(mChild); + return childrenElement->InsertedChild(mIndexInInserted - 1); } + return mDefaultChild ? mDefaultChild : mChild; } @@ -234,20 +201,28 @@ ExplicitChildIterator::GetPreviousChild() { // If we're already in the inserted-children array, look there first if (mIndexInInserted) { + + if (mParentAsSlot) { + const nsTArray<RefPtr<nsINode>>& assignedNodes = + mParentAsSlot->AssignedNodes(); + + mChild = (--mIndexInInserted) ? + assignedNodes[mIndexInInserted - 1]->AsContent() : nullptr; + + if (!mChild) { + mIsFirst = true; + } + return mChild; + } + // NB: mIndexInInserted points one past the last returned child so we need // to look *two* indices back in order to return the previous child. - MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild); + MOZ_ASSERT(mChild->IsActiveChildrenElement()); + auto* childrenElement = static_cast<XBLChildrenElement*>(mChild); if (--mIndexInInserted) { - return assignedChildren[mIndexInInserted - 1]; + return childrenElement->InsertedChild(mIndexInInserted - 1); } mChild = mChild->GetPreviousSibling(); - } else if (mShadowIterator) { - nsIContent* previousChild = mShadowIterator->GetPreviousChild(); - if (previousChild) { - return previousChild; - } - mShadowIterator = nullptr; - mChild = mChild->GetPreviousSibling(); } else if (mDefaultChild) { // If we're already in default content, check if there are more nodes there mDefaultChild = mDefaultChild->GetPreviousSibling(); @@ -261,35 +236,32 @@ ExplicitChildIterator::GetPreviousChild() } else if (mChild) { // in the middle of the child list mChild = mChild->GetPreviousSibling(); } else { // at the end of the child list + // For slot parent, iterate over assigned nodes if not empty, otherwise + // fall through and iterate over direct children (fallback content). + if (mParentAsSlot) { + const nsTArray<RefPtr<nsINode>>& assignedNodes = + mParentAsSlot->AssignedNodes(); + if (!assignedNodes.IsEmpty()) { + mIndexInInserted = assignedNodes.Length(); + mChild = assignedNodes[mIndexInInserted - 1]->AsContent(); + return mChild; + } + } + mChild = mParent->GetLastChild(); } // Iterate until we find a non-insertion point, or an insertion point with // content. while (mChild) { - if (ShadowRoot::IsShadowInsertionPoint(mChild)) { - // If the current child being iterated is a shadow insertion point then - // the iterator needs to go into the projected ShadowRoot. - HTMLShadowElement* shadowElem = HTMLShadowElement::FromContent(mChild); - ShadowRoot* projectedShadow = shadowElem->GetOlderShadowRoot(); - if (projectedShadow) { - // Create a ExplicitChildIterator that begins iterating from the end. - mShadowIterator = new ExplicitChildIterator(projectedShadow, false); - nsIContent* previousChild = mShadowIterator->GetPreviousChild(); - if (previousChild) { - return previousChild; - } - mShadowIterator = nullptr; - } - mChild = mChild->GetPreviousSibling(); - } else if (nsContentUtils::IsContentInsertionPoint(mChild)) { + if (mChild->IsActiveChildrenElement()) { // If the current child being iterated is a content insertion point // then the iterator needs to return the nodes distributed into // the content insertion point. - MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild); - if (!assignedChildren.IsEmpty()) { - mIndexInInserted = assignedChildren.Length(); - return assignedChildren[mIndexInInserted - 1]; + auto* childrenElement = static_cast<XBLChildrenElement*>(mChild); + if (childrenElement->HasInsertedChildren()) { + mIndexInInserted = childrenElement->InsertedChildrenLength(); + return childrenElement->InsertedChild(mIndexInInserted - 1); } mDefaultChild = mChild->GetLastChild(); @@ -317,11 +289,9 @@ AllChildrenIterator::Get() const { switch (mPhase) { case eAtBeforeKid: { - nsIFrame* frame = mOriginalContent->GetPrimaryFrame(); - MOZ_ASSERT(frame, "No frame at eAtBeforeKid phase"); - nsIFrame* beforeFrame = nsLayoutUtils::GetBeforeFrame(frame); - MOZ_ASSERT(beforeFrame, "No content before frame at eAtBeforeKid phase"); - return beforeFrame->GetContent(); + Element* before = nsLayoutUtils::GetBeforePseudo(mOriginalContent); + MOZ_ASSERT(before, "No content before frame at eAtBeforeKid phase"); + return before; } case eAtExplicitKids: @@ -331,11 +301,9 @@ AllChildrenIterator::Get() const return mAnonKids[mAnonKidsIdx]; case eAtAfterKid: { - nsIFrame* frame = mOriginalContent->GetPrimaryFrame(); - MOZ_ASSERT(frame, "No frame at eAtAfterKid phase"); - nsIFrame* afterFrame = nsLayoutUtils::GetAfterFrame(frame); - MOZ_ASSERT(afterFrame, "No content before frame at eAtBeforeKid phase"); - return afterFrame->GetContent(); + Element* after = nsLayoutUtils::GetAfterPseudo(mOriginalContent); + MOZ_ASSERT(after, "No content after frame at eAtAfterKid phase"); + return after; } default: @@ -345,19 +313,14 @@ AllChildrenIterator::Get() const bool -AllChildrenIterator::Seek(nsIContent* aChildToFind) +AllChildrenIterator::Seek(const nsIContent* aChildToFind) { if (mPhase == eAtBegin || mPhase == eAtBeforeKid) { mPhase = eAtExplicitKids; - nsIFrame* frame = mOriginalContent->GetPrimaryFrame(); - if (frame) { - nsIFrame* beforeFrame = nsLayoutUtils::GetBeforeFrame(frame); - if (beforeFrame) { - if (beforeFrame->GetContent() == aChildToFind) { - mPhase = eAtBeforeKid; - return true; - } - } + Element* beforePseudo = nsLayoutUtils::GetBeforePseudo(mOriginalContent); + if (beforePseudo && beforePseudo == aChildToFind) { + mPhase = eAtBeforeKid; + return true; } } @@ -383,12 +346,10 @@ AllChildrenIterator::AppendNativeAnonymousChildren() // The root scroll frame is not the primary frame of the root element. // Detect and handle this case. - if (mOriginalContent == mOriginalContent->OwnerDoc()->GetRootElement()) { - nsIPresShell* presShell = mOriginalContent->OwnerDoc()->GetShell(); - nsIFrame* scrollFrame = presShell ? presShell->GetRootScrollFrame() : nullptr; - if (scrollFrame) { - AppendNativeAnonymousChildrenFromFrame(scrollFrame); - } + if (!(mFlags & nsIContent::eSkipDocumentLevelNativeAnonymousContent) && + mOriginalContent == mOriginalContent->OwnerDoc()->GetRootElement()) { + nsContentUtils::AppendDocumentLevelNativeAnonymousContentTo( + mOriginalContent->OwnerDoc(), mAnonKids); } } @@ -406,13 +367,10 @@ AllChildrenIterator::GetNextChild() { if (mPhase == eAtBegin) { mPhase = eAtExplicitKids; - nsIFrame* frame = mOriginalContent->GetPrimaryFrame(); - if (frame) { - nsIFrame* beforeFrame = nsLayoutUtils::GetBeforeFrame(frame); - if (beforeFrame) { - mPhase = eAtBeforeKid; - return beforeFrame->GetContent(); - } + Element* beforeContent = nsLayoutUtils::GetBeforePseudo(mOriginalContent); + if (beforeContent) { + mPhase = eAtBeforeKid; + return beforeContent; } } @@ -448,13 +406,10 @@ AllChildrenIterator::GetNextChild() return mAnonKids[mAnonKidsIdx]; } - nsIFrame* frame = mOriginalContent->GetPrimaryFrame(); - if (frame) { - nsIFrame* afterFrame = nsLayoutUtils::GetAfterFrame(frame); - if (afterFrame) { - mPhase = eAtAfterKid; - return afterFrame->GetContent(); - } + Element* afterContent = nsLayoutUtils::GetAfterPseudo(mOriginalContent); + if (afterContent) { + mPhase = eAtAfterKid; + return afterContent; } } @@ -468,13 +423,10 @@ AllChildrenIterator::GetPreviousChild() if (mPhase == eAtEnd) { MOZ_ASSERT(mAnonKidsIdx == mAnonKids.Length()); mPhase = eAtAnonKids; - nsIFrame* frame = mOriginalContent->GetPrimaryFrame(); - if (frame) { - nsIFrame* afterFrame = nsLayoutUtils::GetAfterFrame(frame); - if (afterFrame) { - mPhase = eAtAfterKid; - return afterFrame->GetContent(); - } + Element* afterContent = nsLayoutUtils::GetAfterPseudo(mOriginalContent); + if (afterContent) { + mPhase = eAtAfterKid; + return afterContent; } } @@ -503,13 +455,10 @@ AllChildrenIterator::GetPreviousChild() return kid; } - nsIFrame* frame = mOriginalContent->GetPrimaryFrame(); - if (frame) { - nsIFrame* beforeFrame = nsLayoutUtils::GetBeforeFrame(frame); - if (beforeFrame) { - mPhase = eAtBeforeKid; - return beforeFrame->GetContent(); - } + Element* beforeContent = nsLayoutUtils::GetBeforePseudo(mOriginalContent); + if (beforeContent) { + mPhase = eAtBeforeKid; + return beforeContent; } } @@ -585,12 +534,6 @@ StyleChildrenIterator::IsNeeded(const Element* aElement) return true; } - // The root element has a scroll frame that is not the primary frame, so we - // need to do special checking for that case. - if (aElement == aElement->OwnerDoc()->GetRootElement()) { - return true; - } - return false; } diff --git a/dom/base/ChildIterator.h b/dom/base/ChildIterator.h index ffff8dce5..78acdc146 100644 --- a/dom/base/ChildIterator.h +++ b/dom/base/ChildIterator.h @@ -36,27 +36,20 @@ class ExplicitChildIterator { public: explicit ExplicitChildIterator(const nsIContent* aParent, - bool aStartAtBeginning = true) - : mParent(aParent), - mChild(nullptr), - mDefaultChild(nullptr), - mIndexInInserted(0), - mIsFirst(aStartAtBeginning) - { - } + bool aStartAtBeginning = true); ExplicitChildIterator(const ExplicitChildIterator& aOther) - : mParent(aOther.mParent), mChild(aOther.mChild), + : mParent(aOther.mParent), + mParentAsSlot(aOther.mParentAsSlot), + mChild(aOther.mChild), mDefaultChild(aOther.mDefaultChild), - mShadowIterator(aOther.mShadowIterator ? - new ExplicitChildIterator(*aOther.mShadowIterator) : - nullptr), mIndexInInserted(aOther.mIndexInInserted), mIsFirst(aOther.mIsFirst) {} ExplicitChildIterator(ExplicitChildIterator&& aOther) - : mParent(aOther.mParent), mChild(aOther.mChild), + : mParent(aOther.mParent), + mParentAsSlot(aOther.mParentAsSlot), + mChild(aOther.mChild), mDefaultChild(aOther.mDefaultChild), - mShadowIterator(Move(aOther.mShadowIterator)), mIndexInInserted(aOther.mIndexInInserted), mIsFirst(aOther.mIsFirst) {} nsIContent* GetNextChild(); @@ -65,13 +58,13 @@ public: // found. This version can take shortcuts that the two-argument version // can't, so can be faster (and in fact can be O(1) instead of O(N) in many // cases). - bool Seek(nsIContent* aChildToFind); + bool Seek(const nsIContent* aChildToFind); // Looks for aChildToFind respecting insertion points until aChildToFind is found. // or aBound is found. If aBound is nullptr then the seek is unbounded. Returns // whether aChildToFind was found as an explicit child prior to encountering // aBound. - bool Seek(nsIContent* aChildToFind, nsIContent* aBound) + bool Seek(const nsIContent* aChildToFind, nsIContent* aBound) { // It would be nice to assert that we find aChildToFind, but bz thinks that // we might not find aChildToFind when called from ContentInserted @@ -102,6 +95,10 @@ protected: // the <xbl:content> element for the binding. const nsIContent* mParent; + // If parent is a slot element, this points to the parent as HTMLSlotElement, + // otherwise, it's null. + const HTMLSlotElement* mParentAsSlot; + // The current child. When we encounter an insertion point, // mChild remains as the insertion point whose content we're iterating (and // our state is controled by mDefaultChild or mIndexInInserted depending on @@ -114,11 +111,6 @@ protected: // to null, we continue iterating at mChild's next sibling. nsIContent* mDefaultChild; - // If non-null, this points to an iterator of the explicit children of - // the ShadowRoot projected by the current shadow element that we're - // iterating. - nsAutoPtr<ExplicitChildIterator> mShadowIterator; - // If not zero, we're iterating inserted children for an insertion point. This // is an index into mChild's inserted children array (mChild must be an // nsXBLChildrenElement). The index is one past the "current" child (as @@ -139,19 +131,29 @@ class FlattenedChildIterator : public ExplicitChildIterator public: explicit FlattenedChildIterator(const nsIContent* aParent, bool aStartAtBeginning = true) - : ExplicitChildIterator(aParent, aStartAtBeginning), mXBLInvolved(false) + : ExplicitChildIterator(aParent, aStartAtBeginning) + , mXBLInvolved(false) + , mOriginalContent(aParent) { Init(false); } FlattenedChildIterator(FlattenedChildIterator&& aOther) - : ExplicitChildIterator(Move(aOther)), mXBLInvolved(aOther.mXBLInvolved) {} + : ExplicitChildIterator(Move(aOther)) + , mXBLInvolved(aOther.mXBLInvolved) + , mOriginalContent(aOther.mOriginalContent) + {} FlattenedChildIterator(const FlattenedChildIterator& aOther) - : ExplicitChildIterator(aOther), mXBLInvolved(aOther.mXBLInvolved) {} + : ExplicitChildIterator(aOther) + , mXBLInvolved(aOther.mXBLInvolved) + , mOriginalContent(aOther.mOriginalContent) + {} bool XBLInvolved() { return mXBLInvolved; } + const nsIContent* Parent() const { return mOriginalContent; } + protected: /** * This constructor is a hack to help AllChildrenIterator which sometimes @@ -159,7 +161,9 @@ protected: */ FlattenedChildIterator(const nsIContent* aParent, uint32_t aFlags, bool aStartAtBeginning = true) - : ExplicitChildIterator(aParent, aStartAtBeginning), mXBLInvolved(false) + : ExplicitChildIterator(aParent, aStartAtBeginning) + , mXBLInvolved(false) + , mOriginalContent(aParent) { bool ignoreXBL = aFlags & nsIContent::eAllButXBL; Init(ignoreXBL); @@ -170,6 +174,8 @@ protected: // For certain optimizations, nsCSSFrameConstructor needs to know if the // child list of the element that we're iterating matches its .childNodes. bool mXBLInvolved; + + const nsIContent* mOriginalContent; }; /** @@ -187,12 +193,11 @@ public: AllChildrenIterator(const nsIContent* aNode, uint32_t aFlags, bool aStartAtBeginning = true) : FlattenedChildIterator(aNode, aFlags, aStartAtBeginning), - mOriginalContent(aNode), mAnonKidsIdx(aStartAtBeginning ? UINT32_MAX : 0), + mAnonKidsIdx(aStartAtBeginning ? UINT32_MAX : 0), mFlags(aFlags), mPhase(aStartAtBeginning ? eAtBegin : eAtEnd) { } AllChildrenIterator(AllChildrenIterator&& aOther) : FlattenedChildIterator(Move(aOther)), - mOriginalContent(aOther.mOriginalContent), mAnonKids(Move(aOther.mAnonKids)), mAnonKidsIdx(aOther.mAnonKidsIdx), mFlags(aOther.mFlags), mPhase(aOther.mPhase) #ifdef DEBUG @@ -211,11 +216,10 @@ public: // Seeks the given node in children of a parent element, starting from // the current iterator's position, and sets the iterator at the given child // node if it was found. - bool Seek(nsIContent* aChildToFind); + bool Seek(const nsIContent* aChildToFind); nsIContent* GetNextChild(); nsIContent* GetPreviousChild(); - const nsIContent* Parent() const { return mOriginalContent; } enum IteratorPhase { @@ -233,8 +237,6 @@ private: void AppendNativeAnonymousChildren(); void AppendNativeAnonymousChildrenFromFrame(nsIFrame* aFrame); - const nsIContent* mOriginalContent; - // mAnonKids is an array of native anonymous children, mAnonKidsIdx is index // in the array. If mAnonKidsIdx < mAnonKids.Length() and mPhase is // eAtAnonKids then the iterator points at a child at mAnonKidsIdx index. If @@ -256,10 +258,11 @@ private: /** * StyleChildrenIterator traverses the children of the element from the * perspective of the style system, particularly the children we need to traverse - * during restyle. This is identical to AllChildrenIterator with eAllChildren, - * _except_ that we detect and skip any native anonymous children that are used - * to implement pseudo-elements (since the style system needs to cascade those - * using different algorithms). + * during restyle. This is identical to AllChildrenIterator with + * (eAllChildren | eSkipDocumentLevelNativeAnonymousContent), _except_ that we + * detect and skip any native anonymous children that are used to implement + * pseudo-elements (since the style system needs to cascade those using + * different algorithms). * * Note: it assumes that no mutation of the DOM or frame tree takes place during * iteration, and will break horribly if that is not true. @@ -267,7 +270,9 @@ private: class StyleChildrenIterator : private AllChildrenIterator { public: explicit StyleChildrenIterator(const nsIContent* aContent) - : AllChildrenIterator(aContent, nsIContent::eAllChildren) + : AllChildrenIterator(aContent, + nsIContent::eAllChildren | + nsIContent::eSkipDocumentLevelNativeAnonymousContent) { MOZ_COUNT_CTOR(StyleChildrenIterator); } diff --git a/dom/base/ChromeUtils.cpp b/dom/base/ChromeUtils.cpp index 3cde261f0..b25f0b76b 100644 --- a/dom/base/ChromeUtils.cpp +++ b/dom/base/ChromeUtils.cpp @@ -7,6 +7,7 @@ #include "mozilla/Base64.h" #include "mozilla/BasePrincipal.h" +#include "jsfriendapi.h" namespace mozilla { namespace dom { diff --git a/dom/base/ChromeUtils.h b/dom/base/ChromeUtils.h index 051217c84..58a67b4f2 100644 --- a/dom/base/ChromeUtils.h +++ b/dom/base/ChromeUtils.h @@ -10,6 +10,7 @@ #include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/ChromeUtilsBinding.h" #include "mozilla/dom/ThreadSafeChromeUtilsBinding.h" +#include "mozilla/dom/UnionTypes.h" #include "mozilla/ErrorResult.h" namespace mozilla { diff --git a/dom/base/CustomElementRegistry.cpp b/dom/base/CustomElementRegistry.cpp index 99452df65..2bf969d38 100644 --- a/dom/base/CustomElementRegistry.cpp +++ b/dom/base/CustomElementRegistry.cpp @@ -11,7 +11,9 @@ #include "mozilla/dom/HTMLElementBinding.h" #include "mozilla/dom/WebComponentsBinding.h" #include "mozilla/dom/DocGroup.h" -#include "nsIParserService.h" +#include "mozilla/dom/Promise.h" +#include "nsContentUtils.h" +#include "nsHTMLTags.h" #include "jsapi.h" namespace mozilla { @@ -291,7 +293,6 @@ CustomElementRegistry::RegisterUnresolvedElement(Element* aElement, nsIAtom* aTy nsTArray<nsWeakPtr>* unresolved = mCandidatesMap.LookupOrAdd(typeName); nsWeakPtr* elem = unresolved->AppendElement(); *elem = do_GetWeakReference(aElement); - aElement->AddStates(NS_EVENT_STATE_UNRESOLVED); return; } @@ -415,19 +416,6 @@ CustomElementRegistry::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType } void -CustomElementRegistry::GetCustomPrototype(nsIAtom* aAtom, - JS::MutableHandle<JSObject*> aPrototype) -{ - mozilla::dom::CustomElementDefinition* definition = - mCustomDefinitions.GetWeak(aAtom); - if (definition) { - aPrototype.set(definition->mPrototype); - } else { - aPrototype.set(nullptr); - } -} - -void CustomElementRegistry::UpgradeCandidates(nsIAtom* aKey, CustomElementDefinition* aDefinition, ErrorResult& aRv) @@ -466,6 +454,12 @@ nsISupports* CustomElementRegistry::GetParentObject() const return mWindow; } +DocGroup* +CustomElementRegistry::GetDocGroup() const +{ + return mWindow ? mWindow->GetDocGroup() : nullptr; +} + static const char* kLifeCycleCallbackNames[] = { "connectedCallback", "disconnectedCallback", @@ -588,14 +582,8 @@ CustomElementRegistry::Define(const nsAString& aName, return; } - nsIParserService* ps = nsContentUtils::GetParserService(); - if (!ps) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return; - } - // bgsound and multicol are unknown html element. - int32_t tag = ps->HTMLCaseSensitiveAtomTagToId(extendsAtom); + int32_t tag = nsHTMLTags::CaseSensitiveAtomTagToId(extendsAtom); if (tag == eHTMLTag_userdefined || tag == eHTMLTag_bgsound || tag == eHTMLTag_multicol) { @@ -630,9 +618,9 @@ CustomElementRegistry::Define(const nsAString& aName, */ JSAutoCompartment ac(cx, constructor); JS::Rooted<JS::Value> prototypev(cx); - // The .prototype on the constructor passed from document.registerElement - // is the "expando" of a wrapper. So we should get it from wrapper instead - // instead of underlying object. + // The .prototype on the constructor passed could be an "expando" of a + // wrapper. So we should get it from wrapper instead of the underlying + // object. if (!JS_GetProperty(cx, constructor, "prototype", &prototypev)) { aRv.StealExceptionFromJSContext(cx); return; @@ -878,8 +866,6 @@ CustomElementRegistry::Upgrade(Element* aElement, CustomElementDefinition* aDefinition, ErrorResult& aRv) { - aElement->RemoveStates(NS_EVENT_STATE_UNRESOLVED); - RefPtr<CustomElementData> data = aElement->GetCustomElementData(); MOZ_ASSERT(data, "CustomElementData should exist"); diff --git a/dom/base/CustomElementRegistry.h b/dom/base/CustomElementRegistry.h index c416e5043..eacf568c9 100644 --- a/dom/base/CustomElementRegistry.h +++ b/dom/base/CustomElementRegistry.h @@ -27,6 +27,7 @@ struct CustomElementData; struct ElementDefinitionOptions; class CallbackFunction; class CustomElementReaction; +class DocGroup; class Function; class Promise; @@ -423,9 +424,6 @@ public: LifecycleAdoptedCallbackArgs* aAdoptedCallbackArgs, CustomElementDefinition* aDefinition); - void GetCustomPrototype(nsIAtom* aAtom, - JS::MutableHandle<JSObject*> aPrototype); - /** * Upgrade an element. * https://html.spec.whatwg.org/multipage/scripting.html#upgrades @@ -434,8 +432,7 @@ public: /** * Registers an unresolved custom element that is a candidate for - * upgrade when the definition is registered via registerElement. - * |aTypeName| is the name of the custom element type, if it is not + * upgrade. |aTypeName| is the name of the custom element type, if it is not * provided, then element name is used. |aTypeName| should be provided * when registering a custom element that extends an existing * element. e.g. <button is="x-button">. @@ -472,8 +469,8 @@ private: js::SystemAllocPolicy> ConstructorMap; // Hashtable for custom element definitions in web components. - // Custom prototypes are stored in the compartment where - // registerElement was called. + // Custom prototypes are stored in the compartment where definition was + // defined. DefinitionMap mCustomDefinitions; // Hashtable for looking up definitions by using constructor as key. @@ -517,6 +514,8 @@ private: public: nsISupports* GetParentObject() const; + DocGroup* GetDocGroup() const; + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; void Define(const nsAString& aName, Function& aFunctionConstructor, diff --git a/dom/base/DirectionalityUtils.cpp b/dom/base/DirectionalityUtils.cpp index d9a6c4524..632abb2c8 100644 --- a/dom/base/DirectionalityUtils.cpp +++ b/dom/base/DirectionalityUtils.cpp @@ -728,7 +728,7 @@ WalkDescendantsResetAutoDirection(Element* aElement) { nsIContent* child = aElement->GetFirstChild(); while (child) { - if (child->HasDirAuto()) { + if (child->IsElement() && child->AsElement()->HasDirAuto()) { child = child->GetNextNonChildNode(aElement); continue; } @@ -791,7 +791,7 @@ WalkDescendantsClearAncestorDirAuto(Element* aElement) { nsIContent* child = aElement->GetFirstChild(); while (child) { - if (child->HasDirAuto()) { + if (child->IsElement() && child->AsElement()->HasDirAuto()) { child = child->GetNextNonChildNode(aElement); continue; } diff --git a/dom/base/DocGroup.cpp b/dom/base/DocGroup.cpp index 30c058f0c..5d7db1fb6 100644 --- a/dom/base/DocGroup.cpp +++ b/dom/base/DocGroup.cpp @@ -1,3 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + #include "mozilla/dom/DocGroup.h" #include "mozilla/dom/TabGroup.h" #include "mozilla/Telemetry.h" @@ -6,10 +10,14 @@ #include "mozilla/StaticPtr.h" #include "mozilla/ClearOnShutdown.h" #include "nsIDocShell.h" +#include "nsDOMMutationObserver.h" +#include "nsNetCID.h" namespace mozilla { namespace dom { +AutoTArray<RefPtr<DocGroup>, 2>* DocGroup::sPendingDocGroups = nullptr; + /* static */ void DocGroup::GetKey(nsIPrincipal* aPrincipal, nsACString& aKey) { @@ -54,5 +62,23 @@ DocGroup::~DocGroup() NS_IMPL_ISUPPORTS(DocGroup, nsISupports) +void +DocGroup::SignalSlotChange(const HTMLSlotElement* aSlot) +{ + if (mSignalSlotList.Contains(aSlot)) { + return; + } + + mSignalSlotList.AppendElement(const_cast<HTMLSlotElement*>(aSlot)); + + if (!sPendingDocGroups) { + // Queue a mutation observer compound microtask. + nsDOMMutationObserver::QueueMutationObserverMicroTask(); + sPendingDocGroups = new AutoTArray<RefPtr<DocGroup>, 2>; + } + + sPendingDocGroups->AppendElement(this); +} + } } diff --git a/dom/base/DocGroup.h b/dom/base/DocGroup.h index 5b8f627cc..7a5a99dce 100644 --- a/dom/base/DocGroup.h +++ b/dom/base/DocGroup.h @@ -15,6 +15,7 @@ #include "mozilla/RefPtr.h" #include "mozilla/dom/CustomElementRegistry.h" +#include "mozilla/dom/HTMLSlotElement.h" namespace mozilla { namespace dom { @@ -73,6 +74,23 @@ public: return mDocuments.end(); } + // Append aSlot to the list of signal slot list, if it's not in it already + // list, and queue a mutation observer microtask. + void SignalSlotChange(const mozilla::dom::HTMLSlotElement* aSlot); + + const nsTArray<RefPtr<HTMLSlotElement>>& SignalSlotList() const + { + return mSignalSlotList; + } + + void ClearSignalSlotList() + { + mSignalSlotList.Clear(); + } + + // List of DocGroups that has non-empty signal slot list. + static AutoTArray<RefPtr<DocGroup>, 2>* sPendingDocGroups; + private: DocGroup(TabGroup* aTabGroup, const nsACString& aKey); ~DocGroup(); @@ -81,6 +99,7 @@ private: RefPtr<TabGroup> mTabGroup; nsTArray<nsIDocument*> mDocuments; RefPtr<mozilla::dom::CustomElementReactionsStack> mReactionsStack; + nsTArray<RefPtr<HTMLSlotElement>> mSignalSlotList; }; } // namespace dom diff --git a/dom/base/DocumentFragment.cpp b/dom/base/DocumentFragment.cpp index 3eb2a0790..3e1e2c81b 100644 --- a/dom/base/DocumentFragment.cpp +++ b/dom/base/DocumentFragment.cpp @@ -125,6 +125,8 @@ DocumentFragment::Constructor(const GlobalObject& aGlobal, return window->GetDoc()->CreateDocumentFragment(); } +NS_IMPL_CYCLE_COLLECTION_INHERITED(DocumentFragment, FragmentOrElement, mHost) + // QueryInterface implementation for DocumentFragment NS_INTERFACE_MAP_BEGIN(DocumentFragment) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY diff --git a/dom/base/DocumentFragment.h b/dom/base/DocumentFragment.h index 68a7f0ff4..ccc233ff1 100644 --- a/dom/base/DocumentFragment.h +++ b/dom/base/DocumentFragment.h @@ -43,6 +43,7 @@ public: // nsISupports NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DocumentFragment, FragmentOrElement) // interface nsIDOMNode NS_FORWARD_NSIDOMNODE_TO_NSINODE @@ -121,15 +122,9 @@ public: return nullptr; } - nsIContent* GetHost() const - { - return mHost; - } + Element* GetHost() const { return mHost; } - void SetHost(nsIContent* aHost) - { - mHost = aHost; - } + void SetHost(Element* aHost) { mHost = aHost; } static already_AddRefed<DocumentFragment> Constructor(const GlobalObject& aGlobal, ErrorResult& aRv); @@ -145,7 +140,7 @@ protected: } nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; - nsIContent* mHost; // Weak + nsCOMPtr<Element> mHost; }; } // namespace dom diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index c8467e036..8e9225a64 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -166,8 +166,7 @@ nsIContent::DoGetID() const const nsAttrValue* Element::DoGetClasses() const { - MOZ_ASSERT(HasFlag(NODE_MAY_HAVE_CLASS), "Unexpected call"); - + MOZ_ASSERT(MayHaveClass(), "Unexpected call"); if (IsSVGElement()) { const nsAttrValue* animClass = static_cast<const nsSVGElement*>(this)->GetAnimatedClassName(); @@ -470,50 +469,11 @@ Element::GetBindingURL(nsIDocument *aDocument, css::URLValue **aResult) JSObject* Element::WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) { - JS::Rooted<JSObject*> givenProto(aCx, aGivenProto); - JS::Rooted<JSObject*> customProto(aCx); - - if (!givenProto) { - // Custom element prototype swizzling. - CustomElementData* data = GetCustomElementData(); - if (data) { - // If this is a registered custom element then fix the prototype. - nsContentUtils::GetCustomPrototype(OwnerDoc(), NodeInfo()->NamespaceID(), - data->GetCustomElementType(), &customProto); - if (customProto && - NodePrincipal()->SubsumesConsideringDomain(nsContentUtils::ObjectPrincipal(customProto))) { - // The custom element prototype could be in different compartment. - if (!JS_WrapObject(aCx, &customProto)) { - return nullptr; - } - // Just go ahead and create with the right proto up front. Set - // customProto to null to flag that we don't need to do any post-facto - // proto fixups here. - givenProto = customProto; - customProto = nullptr; - } - } - } - - JS::Rooted<JSObject*> obj(aCx, nsINode::WrapObject(aCx, givenProto)); + JS::Rooted<JSObject*> obj(aCx, nsINode::WrapObject(aCx, aGivenProto)); if (!obj) { return nullptr; } - if (customProto) { - // We want to set the custom prototype in the compartment where it was - // registered. In the case that |obj| and |prototype| are in different - // compartments, this will set the prototype on the |obj|'s wrapper and - // thus only visible in the wrapper's compartment, since we know obj's - // principal does not subsume customProto's in this case. - JSAutoCompartment ac(aCx, customProto); - JS::Rooted<JSObject*> wrappedObj(aCx, obj); - if (!JS_WrapObject(aCx, &wrappedObj) || - !JS_SetPrototype(aCx, wrappedObj, customProto)) { - return nullptr; - } - } - nsIDocument* doc; if (HasFlag(NODE_FORCE_XBL_BINDINGS)) { doc = OwnerDoc(); @@ -1093,9 +1053,102 @@ Element::RemoveFromIdTable() } } +void +Element::SetSlot(const nsAString& aName, ErrorResult& aError) +{ + aError = SetAttr(kNameSpaceID_None, nsGkAtoms::slot, aName, true); +} + +void +Element::GetSlot(nsAString& aName) +{ + GetAttr(kNameSpaceID_None, nsGkAtoms::slot, aName); +} + +// https://dom.spec.whatwg.org/#dom-element-shadowroot +ShadowRoot* +Element::GetShadowRootByMode() const +{ + /** + * 1. Let shadow be context object’s shadow root. + * 2. If shadow is null or its mode is "closed", then return null. + */ + ShadowRoot* shadowRoot = GetShadowRoot(); + if (!shadowRoot || shadowRoot->IsClosed()) { + return nullptr; + } + + /** + * 3. Return shadow. + */ + return shadowRoot; +} + +// https://dom.spec.whatwg.org/#dom-element-attachshadow +already_AddRefed<ShadowRoot> +Element::AttachShadow(const ShadowRootInit& aInit, ErrorResult& aError) +{ + /** + * 1. If context object’s namespace is not the HTML namespace, + * then throw a "NotSupportedError" DOMException. + */ + if (!IsHTMLElement()) { + aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return nullptr; + } + + /** + * 2. If context object’s local name is not + * a valid custom element name, "article", "aside", "blockquote", + * "body", "div", "footer", "h1", "h2", "h3", "h4", "h5", "h6", + * "header", "main" "nav", "p", "section", or "span", + * then throw a "NotSupportedError" DOMException. + */ + nsIAtom* nameAtom = NodeInfo()->NameAtom(); + if (!(nsContentUtils::IsCustomElementName(nameAtom) || + nameAtom == nsGkAtoms::article || + nameAtom == nsGkAtoms::aside || + nameAtom == nsGkAtoms::blockquote || + nameAtom == nsGkAtoms::body || + nameAtom == nsGkAtoms::div || + nameAtom == nsGkAtoms::footer || + nameAtom == nsGkAtoms::h1 || + nameAtom == nsGkAtoms::h2 || + nameAtom == nsGkAtoms::h3 || + nameAtom == nsGkAtoms::h4 || + nameAtom == nsGkAtoms::h5 || + nameAtom == nsGkAtoms::h6 || + nameAtom == nsGkAtoms::header || + nameAtom == nsGkAtoms::main || + nameAtom == nsGkAtoms::nav || + nameAtom == nsGkAtoms::p || + nameAtom == nsGkAtoms::section || + nameAtom == nsGkAtoms::span)) { + aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + 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. + */ + if (GetShadowRoot()) { + aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return nullptr; + } + nsAutoScriptBlocker scriptBlocker; RefPtr<mozilla::dom::NodeInfo> nodeInfo; @@ -1113,12 +1166,9 @@ Element::CreateShadowRoot(ErrorResult& aError) return nullptr; } - nsIDocument* doc = GetComposedDoc(); - nsIContent* destroyedFramesFor = nullptr; - if (doc) { - nsIPresShell* shell = doc->GetShell(); - if (shell) { - shell->DestroyFramesFor(this, &destroyedFramesFor); + if (nsIDocument* doc = GetComposedDoc()) { + if (nsIPresShell* shell = doc->GetShell()) { + shell->DestroyFramesForAndRestyle(this); MOZ_ASSERT(!shell->FrameManager()->GetDisplayContentsStyleFor(this)); } } @@ -1130,29 +1180,20 @@ Element::CreateShadowRoot(ErrorResult& aError) // Calling SetPrototypeBinding takes ownership of protoBinding. docInfo->SetPrototypeBinding(NS_LITERAL_CSTRING("shadowroot"), protoBinding); - RefPtr<ShadowRoot> shadowRoot = new ShadowRoot(this, nodeInfo.forget(), - protoBinding); + /** + * 4. Let shadow be a new shadow root whose node document is + * context object’s node document, host is context object, + * and mode is init’s mode. + */ + RefPtr<ShadowRoot> shadowRoot = + new ShadowRoot(this, aClosed, nodeInfo.forget(), protoBinding); shadowRoot->SetIsComposedDocParticipant(IsInComposedDoc()); - // Replace the old ShadowRoot with the new one and let the old - // ShadowRoot know about the younger ShadowRoot because the old - // ShadowRoot is projected into the younger ShadowRoot's shadow - // insertion point (if it exists). - ShadowRoot* olderShadow = GetShadowRoot(); + /** + * 5. Set context object’s shadow root to shadow. + */ SetShadowRoot(shadowRoot); - if (olderShadow) { - olderShadow->SetYoungerShadow(shadowRoot); - - // Unbind children of older shadow root because they - // are no longer in the composed tree. - for (nsIContent* child = olderShadow->GetFirstChild(); child; - child = child->GetNextSibling()) { - child->UnbindFromTree(true, false); - } - - olderShadow->SetIsComposedDocParticipant(false); - } // xblBinding takes ownership of docInfo. RefPtr<nsXBLBinding> xblBinding = new nsXBLBinding(shadowRoot, protoBinding); @@ -1161,96 +1202,12 @@ Element::CreateShadowRoot(ErrorResult& aError) SetXBLBinding(xblBinding); - // Recreate the frame for the bound content because binding a ShadowRoot - // changes how things are rendered. - if (doc) { - MOZ_ASSERT(doc == GetComposedDoc()); - nsIPresShell* shell = doc->GetShell(); - if (shell) { - shell->CreateFramesFor(destroyedFramesFor); - } - } - + /** + * 6. Return shadow. + */ return shadowRoot.forget(); } -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DestinationInsertionPointList, mParent, - mDestinationPoints) - -NS_INTERFACE_TABLE_HEAD(DestinationInsertionPointList) - NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY - NS_INTERFACE_TABLE(DestinationInsertionPointList, nsINodeList, nsIDOMNodeList) - NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(DestinationInsertionPointList) -NS_INTERFACE_MAP_END - -NS_IMPL_CYCLE_COLLECTING_ADDREF(DestinationInsertionPointList) -NS_IMPL_CYCLE_COLLECTING_RELEASE(DestinationInsertionPointList) - -DestinationInsertionPointList::DestinationInsertionPointList(Element* aElement) - : mParent(aElement) -{ - nsTArray<nsIContent*>* destPoints = aElement->GetExistingDestInsertionPoints(); - if (destPoints) { - for (uint32_t i = 0; i < destPoints->Length(); i++) { - mDestinationPoints.AppendElement(destPoints->ElementAt(i)); - } - } -} - -DestinationInsertionPointList::~DestinationInsertionPointList() -{ -} - -nsIContent* -DestinationInsertionPointList::Item(uint32_t aIndex) -{ - return mDestinationPoints.SafeElementAt(aIndex); -} - -NS_IMETHODIMP -DestinationInsertionPointList::Item(uint32_t aIndex, nsIDOMNode** aReturn) -{ - nsIContent* item = Item(aIndex); - if (!item) { - return NS_ERROR_FAILURE; - } - - return CallQueryInterface(item, aReturn); -} - -uint32_t -DestinationInsertionPointList::Length() const -{ - return mDestinationPoints.Length(); -} - -NS_IMETHODIMP -DestinationInsertionPointList::GetLength(uint32_t* aLength) -{ - *aLength = Length(); - return NS_OK; -} - -int32_t -DestinationInsertionPointList::IndexOf(nsIContent* aContent) -{ - return mDestinationPoints.IndexOf(aContent); -} - -JSObject* -DestinationInsertionPointList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) -{ - return NodeListBinding::Wrap(aCx, this, aGivenProto); -} - -already_AddRefed<DestinationInsertionPointList> -Element::GetDestinationInsertionPoints() -{ - RefPtr<DestinationInsertionPointList> list = - new DestinationInsertionPointList(this); - return list.forget(); -} - void Element::GetAttribute(const nsAString& aName, DOMString& aReturn) { @@ -2383,7 +2340,8 @@ Element::MaybeCheckSameAttrVal(int32_t aNamespaceID, bool aNotify, nsAttrValue& aOldValue, uint8_t* aModType, - bool* aHasListeners) + bool* aHasListeners, + bool* aOldValueSet) { bool modification = false; *aHasListeners = aNotify && @@ -2391,6 +2349,8 @@ Element::MaybeCheckSameAttrVal(int32_t aNamespaceID, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this); + *aOldValueSet = false; + // If we have no listeners and aNotify is false, we are almost certainly // coming from the content sink and will almost certainly have no previous // value. Even if we do, setting the value is cheap when we have no @@ -2414,6 +2374,7 @@ Element::MaybeCheckSameAttrVal(int32_t aNamespaceID, // We have to serialize the value anyway in order to create the // mutation event so there's no cost in doing it now. aOldValue.SetToSerialized(*info.mValue); + *aOldValueSet = true; } bool valueMatches = aValue.EqualsAsStrings(*info.mValue); if (valueMatches && aPrefix == info.mName->GetPrefix()) { @@ -2433,10 +2394,12 @@ Element::OnlyNotifySameValueSet(int32_t aNamespaceID, nsIAtom* aName, nsIAtom* aPrefix, const nsAttrValueOrString& aValue, bool aNotify, nsAttrValue& aOldValue, - uint8_t* aModType, bool* aHasListeners) + uint8_t* aModType, bool* aHasListeners, + bool* aOldValueSet) { if (!MaybeCheckSameAttrVal(aNamespaceID, aName, aPrefix, aValue, aNotify, - aOldValue, aModType, aHasListeners)) { + aOldValue, aModType, aHasListeners, + aOldValueSet)) { return false; } @@ -2446,11 +2409,44 @@ Element::OnlyNotifySameValueSet(int32_t aNamespaceID, nsIAtom* aName, } nsresult +Element::SetSingleClassFromParser(nsIAtom* aSingleClassName) +{ + // Keep this in sync with SetAttr and SetParsedAttr below. + + if (!mAttrsAndChildren.CanFitMoreAttrs()) { + return NS_ERROR_FAILURE; + } + + nsAttrValue value(aSingleClassName); + + nsIDocument* document = GetComposedDoc(); + mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, false); + + // In principle, BeforeSetAttr should be called here if a node type + // existed that wanted to do something special for class, but there + // is no such node type, so calling SetMayHaveClass() directly. + SetMayHaveClass(); + + return SetAttrAndNotify(kNameSpaceID_None, + nsGkAtoms::_class, + nullptr, // prefix + nullptr, // old value + value, + static_cast<uint8_t>(nsIDOMMutationEvent::ADDITION), + false, // hasListeners + false, // notify + kCallAfterSetAttr, + document, + updateBatch); +} + +nsresult Element::SetAttr(int32_t aNamespaceID, nsIAtom* aName, nsIAtom* aPrefix, const nsAString& aValue, bool aNotify) { - // Keep this in sync with SetParsedAttr below + // Keep this in sync with SetParsedAttr below and SetSingleClassFromParser + // above. NS_ENSURE_ARG_POINTER(aName); NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown, @@ -2462,17 +2458,27 @@ Element::SetAttr(int32_t aNamespaceID, nsIAtom* aName, uint8_t modType; bool hasListeners; + // We don't want to spend time preparsing class attributes if the value is not + // changing, so just init our nsAttrValueOrString with aValue for the + // OnlyNotifySameValueSet call. nsAttrValueOrString value(aValue); nsAttrValue oldValue; + bool oldValueSet; if (OnlyNotifySameValueSet(aNamespaceID, aName, aPrefix, value, aNotify, - oldValue, &modType, &hasListeners)) { - return NS_OK; + oldValue, &modType, &hasListeners, &oldValueSet)) { + return OnAttrSetButNotChanged(aNamespaceID, aName, value, aNotify); } - nsresult rv = BeforeSetAttr(aNamespaceID, aName, &value, aNotify); - NS_ENSURE_SUCCESS(rv, rv); - nsAttrValue* preparsedAttrValue = value.GetStoredAttrValue(); + nsAttrValue attrValue; + nsAttrValue* preparsedAttrValue; + if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::_class) { + attrValue.ParseAtomArray(aValue); + value.ResetToAttrValue(attrValue); + preparsedAttrValue = &attrValue; + } else { + preparsedAttrValue = nullptr; + } if (aNotify) { nsNodeUtils::AttributeWillChange(this, aNamespaceID, aName, modType, @@ -2481,21 +2487,23 @@ Element::SetAttr(int32_t aNamespaceID, nsIAtom* aName, // Hold a script blocker while calling ParseAttribute since that can call // out to id-observers - nsAutoScriptBlocker scriptBlocker; + nsIDocument* document = GetComposedDoc(); + mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify); - nsAttrValue attrValue; - if (preparsedAttrValue) { - attrValue.SwapValueWith(*preparsedAttrValue); - } - // Even the value was pre-parsed in BeforeSetAttr, we still need to call - // ParseAttribute because it can have side effects. - if (!ParseAttribute(aNamespaceID, aName, aValue, attrValue)) { + nsresult rv = BeforeSetAttr(aNamespaceID, aName, &value, aNotify); + NS_ENSURE_SUCCESS(rv, rv); + + if (!preparsedAttrValue && + !ParseAttribute(aNamespaceID, aName, aValue, attrValue)) { attrValue.SetTo(aValue); } - return SetAttrAndNotify(aNamespaceID, aName, aPrefix, oldValue, + PreIdMaybeChange(aNamespaceID, aName, &value); + + return SetAttrAndNotify(aNamespaceID, aName, aPrefix, + oldValueSet ? &oldValue : nullptr, attrValue, modType, hasListeners, aNotify, - kCallAfterSetAttr); + kCallAfterSetAttr, document, updateBatch); } nsresult @@ -2503,7 +2511,7 @@ Element::SetParsedAttr(int32_t aNamespaceID, nsIAtom* aName, nsIAtom* aPrefix, nsAttrValue& aParsedValue, bool aNotify) { - // Keep this in sync with SetAttr above + // Keep this in sync with SetAttr and SetSingleClassFromParser above NS_ENSURE_ARG_POINTER(aName); NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown, @@ -2518,41 +2526,46 @@ Element::SetParsedAttr(int32_t aNamespaceID, nsIAtom* aName, bool hasListeners; nsAttrValueOrString value(aParsedValue); nsAttrValue oldValue; + bool oldValueSet; if (OnlyNotifySameValueSet(aNamespaceID, aName, aPrefix, value, aNotify, - oldValue, &modType, &hasListeners)) { - return NS_OK; + oldValue, &modType, &hasListeners, &oldValueSet)) { + return OnAttrSetButNotChanged(aNamespaceID, aName, value, aNotify); } - nsresult rv = BeforeSetAttr(aNamespaceID, aName, &value, aNotify); - NS_ENSURE_SUCCESS(rv, rv); - if (aNotify) { nsNodeUtils::AttributeWillChange(this, aNamespaceID, aName, modType, &aParsedValue); } - return SetAttrAndNotify(aNamespaceID, aName, aPrefix, oldValue, + nsresult rv = BeforeSetAttr(aNamespaceID, aName, &value, aNotify); + NS_ENSURE_SUCCESS(rv, rv); + + PreIdMaybeChange(aNamespaceID, aName, &value); + + nsIDocument* document = GetComposedDoc(); + mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify); + return SetAttrAndNotify(aNamespaceID, aName, aPrefix, + oldValueSet ? &oldValue : nullptr, aParsedValue, modType, hasListeners, aNotify, - kCallAfterSetAttr); + kCallAfterSetAttr, document, updateBatch); } nsresult Element::SetAttrAndNotify(int32_t aNamespaceID, nsIAtom* aName, nsIAtom* aPrefix, - const nsAttrValue& aOldValue, + const nsAttrValue* aOldValue, nsAttrValue& aParsedValue, uint8_t aModType, bool aFireMutation, bool aNotify, - bool aCallAfterSetAttr) + bool aCallAfterSetAttr, + nsIDocument* aComposedDocument, + const mozAutoDocUpdate&) { nsresult rv; - nsIDocument* document = GetComposedDoc(); - mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify); - nsMutationGuard::DidMutate(); // Copy aParsedValue for later use since it will be lost when we call @@ -2564,6 +2577,7 @@ Element::SetAttrAndNotify(int32_t aNamespaceID, bool hadValidDir = false; bool hadDirAuto = false; + bool oldValueSet; if (aNamespaceID == kNameSpaceID_None) { if (aName == nsGkAtoms::dir) { @@ -2574,8 +2588,8 @@ Element::SetAttrAndNotify(int32_t aNamespaceID, // XXXbz Perhaps we should push up the attribute mapping function // stuff to Element? if (!IsAttributeMapped(aName) || - !SetMappedAttribute(document, aName, aParsedValue, &rv)) { - rv = mAttrsAndChildren.SetAndSwapAttr(aName, aParsedValue); + !SetAndSwapMappedAttribute(aComposedDocument, aName, aParsedValue, &oldValueSet, &rv)) { + rv = mAttrsAndChildren.SetAndSwapAttr(aName, aParsedValue, &oldValueSet); } } else { @@ -2584,24 +2598,34 @@ Element::SetAttrAndNotify(int32_t aNamespaceID, aNamespaceID, nsIDOMNode::ATTRIBUTE_NODE); - rv = mAttrsAndChildren.SetAndSwapAttr(ni, aParsedValue); + rv = mAttrsAndChildren.SetAndSwapAttr(ni, aParsedValue, &oldValueSet); } + NS_ENSURE_SUCCESS(rv, rv); - // If the old value owns its own data, we know it is OK to keep using it. - const nsAttrValue* oldValue = - aParsedValue.StoresOwnData() ? &aParsedValue : &aOldValue; + PostIdMaybeChange(aNamespaceID, aName, &valueForAfterSetAttr); - NS_ENSURE_SUCCESS(rv, rv); + // If the old value owns its own data, we know it is OK to keep using it. + // oldValue will be null if there was no previously set value + const nsAttrValue* oldValue; + if (aParsedValue.StoresOwnData()) { + if (oldValueSet) { + oldValue = &aParsedValue; + } else { + oldValue = nullptr; + } + } else { + // No need to conditionally assign null here. If there was no previously + // set value for the attribute, aOldValue will already be null. + oldValue = aOldValue; + } - if (document || HasFlag(NODE_FORCE_XBL_BINDINGS)) { + if (aComposedDocument || HasFlag(NODE_FORCE_XBL_BINDINGS)) { RefPtr<nsXBLBinding> binding = GetXBLBinding(); if (binding) { binding->AttributeChanged(aName, aNamespaceID, false, aNotify); } } - UpdateState(aNotify); - if (CustomElementRegistry::IsCustomElementEnabled()) { if (CustomElementData* data = GetCustomElementData()) { if (CustomElementDefinition* definition = @@ -2611,7 +2635,14 @@ Element::SetAttrAndNotify(int32_t aNamespaceID, MOZ_ASSERT(data->mState == CustomElementData::State::eCustom, "AttributeChanged callback should fire only if " "custom element state is custom"); - nsCOMPtr<nsIAtom> oldValueAtom = oldValue->GetAsAtom(); + nsCOMPtr<nsIAtom> oldValueAtom; + if (oldValue) { + oldValueAtom = oldValue->GetAsAtom(); + } else { + // If there is no old value, get the value of the uninitialized attribute + // that was swapped with aParsedValue. + oldValueAtom = aParsedValue.GetAsAtom(); + } nsCOMPtr<nsIAtom> newValueAtom = valueForAfterSetAttr.GetAsAtom(); nsAutoString ns; nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNamespaceID, ns); @@ -2631,7 +2662,8 @@ Element::SetAttrAndNotify(int32_t aNamespaceID, } if (aCallAfterSetAttr) { - rv = AfterSetAttr(aNamespaceID, aName, &valueForAfterSetAttr, aNotify); + rv = AfterSetAttr(aNamespaceID, aName, &valueForAfterSetAttr, oldValue, + aNotify); NS_ENSURE_SUCCESS(rv, rv); if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) { @@ -2640,12 +2672,14 @@ Element::SetAttrAndNotify(int32_t aNamespaceID, } } + UpdateState(aNotify); + if (aNotify) { // Don't pass aOldValue to AttributeChanged since it may not be reliable. // Callers only compute aOldValue under certain conditions which may not // be triggered by all nsIMutationObservers. nsNodeUtils::AttributeChanged(this, aNamespaceID, aName, aModType, - oldValue == &aParsedValue ? &aParsedValue : nullptr); + aParsedValue.StoresOwnData() ? &aParsedValue : nullptr); } if (aFireMutation) { @@ -2663,7 +2697,7 @@ Element::SetAttrAndNotify(int32_t aNamespaceID, if (!newValue.IsEmpty()) { mutation.mNewAttrValue = NS_Atomize(newValue); } - if (!oldValue->IsEmptyString()) { + if (oldValue && !oldValue->IsEmptyString()) { mutation.mPrevAttrValue = oldValue->GetAsAtom(); } mutation.mAttrChange = aModType; @@ -2675,25 +2709,6 @@ Element::SetAttrAndNotify(int32_t aNamespaceID, return NS_OK; } -nsresult -Element::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, - nsAttrValueOrString* aValue, bool aNotify) -{ - if (aNamespaceID == kNameSpaceID_None) { - if (aName == nsGkAtoms::_class) { - // aValue->GetAttrValue will only be non-null here when this is called - // via Element::SetParsedAttr. This shouldn't happen for "class", but - // this will handle it. - if (aValue && !aValue->GetAttrValue()) { - nsAttrValue attr; - attr.ParseAtomArray(aValue->String()); - aValue->TakeParsedValue(attr); - } - } - } - return NS_OK; -} - bool Element::ParseAttribute(int32_t aNamespaceID, nsIAtom* aAttribute, @@ -2701,22 +2716,16 @@ Element::ParseAttribute(int32_t aNamespaceID, nsAttrValue& aResult) { if (aNamespaceID == kNameSpaceID_None) { - if (aAttribute == nsGkAtoms::_class) { - SetFlags(NODE_MAY_HAVE_CLASS); - // Result should have been preparsed above. - return true; - } + MOZ_ASSERT(aAttribute != nsGkAtoms::_class, + "The class attribute should be preparsed and therefore should " + "never be passed to Element::ParseAttribute"); if (aAttribute == nsGkAtoms::id) { // Store id as an atom. id="" means that the element has no id, // not that it has an emptystring as the id. - RemoveFromIdTable(); if (aValue.IsEmpty()) { - ClearHasID(); return false; } aResult.ParseAtom(aValue); - SetHasID(); - AddToIdTable(aResult.GetAtomValue()); return true; } } @@ -2725,15 +2734,71 @@ Element::ParseAttribute(int32_t aNamespaceID, } bool -Element::SetMappedAttribute(nsIDocument* aDocument, - nsIAtom* aName, - nsAttrValue& aValue, - nsresult* aRetval) +Element::SetAndSwapMappedAttribute(nsIDocument* aDocument, + nsIAtom* aName, + nsAttrValue& aValue, + bool* aValueWasSet, + nsresult* aRetval) { *aRetval = NS_OK; return false; } +nsresult +Element::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString* aValue, bool aNotify) +{ + if (aNamespaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::_class) { + if (aValue) { + // Note: This flag is asymmetrical. It is never unset and isn't exact. + // If it is ever made to be exact, we probably need to handle this + // similarly to how ids are handled in PreIdMaybeChange and + // PostIdMaybeChange. + // Note that SetSingleClassFromParser inlines BeforeSetAttr and + // calls SetMayHaveClass directly. Making a subclass take action + // on the class attribute in a BeforeSetAttr override would + // require revising SetSingleClassFromParser. + SetMayHaveClass(); + } + } + } + + return NS_OK; +} + +void +Element::PreIdMaybeChange(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString* aValue) +{ + if (aNamespaceID != kNameSpaceID_None || aName != nsGkAtoms::id) { + return; + } + RemoveFromIdTable(); + + return; +} + +void +Element::PostIdMaybeChange(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue) +{ + if (aNamespaceID != kNameSpaceID_None || aName != nsGkAtoms::id) { + return; + } + + // id="" means that the element has no id, not that it has an empty + // string as the id. + if (aValue && !aValue->IsEmptyString()) { + SetHasID(); + AddToIdTable(aValue->GetAtomValue()); + } else { + ClearHasID(); + } + + return; +} + EventListenerManager* Element::GetEventListenerManagerForAttr(nsIAtom* aAttrName, bool* aDefer) @@ -2810,9 +2875,6 @@ Element::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName, return NS_OK; } - nsresult rv = BeforeSetAttr(aNameSpaceID, aName, nullptr, aNotify); - NS_ENSURE_SUCCESS(rv, rv); - nsIDocument *document = GetComposedDoc(); mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify); @@ -2822,11 +2884,16 @@ Element::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName, nullptr); } + nsresult rv = BeforeSetAttr(aNameSpaceID, aName, nullptr, aNotify); + NS_ENSURE_SUCCESS(rv, rv); + bool hasMutationListeners = aNotify && nsContentUtils::HasMutationListeners(this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this); + PreIdMaybeChange(aNameSpaceID, aName, nullptr); + // Grab the attr node if needed before we remove it from the attr map RefPtr<Attr> attrNode; if (hasMutationListeners) { @@ -2845,12 +2912,6 @@ Element::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName, // react to unexpected attribute changes. nsMutationGuard::DidMutate(); - if (aName == nsGkAtoms::id && aNameSpaceID == kNameSpaceID_None) { - // Have to do this before clearing flag. See RemoveFromIdTable - RemoveFromIdTable(); - ClearHasID(); - } - bool hadValidDir = false; bool hadDirAuto = false; @@ -2863,6 +2924,8 @@ Element::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName, rv = mAttrsAndChildren.RemoveAttrAt(index, oldValue); NS_ENSURE_SUCCESS(rv, rv); + PostIdMaybeChange(aNameSpaceID, aName, nullptr); + if (document || HasFlag(NODE_FORCE_XBL_BINDINGS)) { RefPtr<nsXBLBinding> binding = GetXBLBinding(); if (binding) { @@ -2870,8 +2933,6 @@ Element::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName, } } - UpdateState(aNotify); - if (CustomElementRegistry::IsCustomElementEnabled()) { if (CustomElementData* data = GetCustomElementData()) { if (CustomElementDefinition* definition = @@ -2898,6 +2959,11 @@ Element::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName, } } + rv = AfterSetAttr(aNameSpaceID, aName, nullptr, &oldValue, aNotify); + NS_ENSURE_SUCCESS(rv, rv); + + UpdateState(aNotify); + if (aNotify) { // We can always pass oldValue here since there is no new value which could // have corrupted it. @@ -2905,9 +2971,6 @@ Element::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName, nsIDOMMutationEvent::REMOVAL, &oldValue); } - rv = AfterSetAttr(aNameSpaceID, aName, nullptr, aNotify); - NS_ENSURE_SUCCESS(rv, rv); - if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) { OnSetDirAttr(this, nullptr, hadValidDir, hadDirAuto, aNotify); } @@ -3129,7 +3192,7 @@ Element::CheckHandleEventForLinksPrecondition(EventChainVisitor& aVisitor, } nsresult -Element::PreHandleEventForLinks(EventChainPreVisitor& aVisitor) +Element::GetEventTargetParentForLinks(EventChainPreVisitor& aVisitor) { // Optimisation: return early if this event doesn't interest us. // IMPORTANT: this switch and the switch below it must be kept in sync! @@ -3151,8 +3214,9 @@ Element::PreHandleEventForLinks(EventChainPreVisitor& aVisitor) nsresult rv = NS_OK; - // We do the status bar updates in PreHandleEvent so that the status bar gets - // updated even if the event is consumed before we have a chance to set it. + // We do the status bar updates in GetEventTargetParent so that the status bar + // gets updated even if the event is consumed before we have a chance to set + // it. switch (aVisitor.mEvent->mMessage) { // Set the status bar similarly for mouseover and focus case eMouseOver: diff --git a/dom/base/Element.h b/dom/base/Element.h index 782004703..3d9b80a98 100644 --- a/dom/base/Element.h +++ b/dom/base/Element.h @@ -41,6 +41,7 @@ #include "Units.h" #include "DOMIntersectionObserver.h" +class mozAutoDocUpdate; class nsIFrame; class nsIDOMMozNamedAttrMap; class nsIURI; @@ -143,7 +144,6 @@ class CustomElementRegistry; class Link; class DOMRect; class DOMRectList; -class DestinationInsertionPointList; class Grid; // IID for the dom::Element interface @@ -255,6 +255,23 @@ public: void ClearStyleStateLocks(); /** + * Accessors for the state of our dir attribute. + */ + bool HasDirAuto() const + { + return State().HasState(NS_EVENT_STATE_DIR_ATTR_LIKE_AUTO); + } + + /** + * Elements with dir="rtl" or dir="ltr". + */ + bool HasFixedDir() const + { + return State().HasAtLeastOneOfStates(NS_EVENT_STATE_DIR_ATTR_LTR | + NS_EVENT_STATE_DIR_ATTR_RTL); + } + + /** * Get the inline style declaration, if any, for this element. */ virtual DeclarationBlock* GetInlineStyleDeclaration(); @@ -379,17 +396,10 @@ public: bool GetBindingURL(nsIDocument *aDocument, css::URLValue **aResult); - // The bdi element defaults to dir=auto if it has no dir attribute set. - // Other elements will only have dir=auto if they have an explicit dir=auto, - // which will mean that HasValidDir() returns true but HasFixedDir() returns - // false - inline bool HasDirAuto() const { - return (!HasFixedDir() && - (HasValidDir() || IsHTMLElement(nsGkAtoms::bdi))); - } - Directionality GetComputedDirectionality() const; + inline Element* GetFlattenedTreeParentElementForStyle() const; + /** * Gets the custom element data used by web components custom element. * Custom element data is created at the first attempt to enqueue a callback. @@ -460,6 +470,9 @@ 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 @@ -498,6 +511,16 @@ protected: RemoveStatesSilently(aStates); NotifyStateChange(aStates); } + virtual void ToggleStates(EventStates aStates, bool aNotify) + { + NS_PRECONDITION(!aStates.HasAtLeastOneOfStates(INTRINSIC_STATES), + "Should only be removing externally-managed states here"); + mState ^= aStates; + if (aNotify) { + NotifyStateChange(aStates); + } + } + public: virtual void UpdateEditableState(bool aNotify) override; @@ -520,6 +543,7 @@ public: already_AddRefed<mozilla::dom::NodeInfo> GetExistingAttrNameFromQName(const nsAString& aStr) const; + MOZ_ALWAYS_INLINE // Avoid a crashy hook from Avast 10 Beta (Bug 1058131) nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, const nsAString& aValue, bool aNotify) { @@ -534,25 +558,57 @@ public: * values will not actually be compared if we aren't notifying and we don't * have mutation listeners (in which case it's cheap to just return false * and let the caller go ahead and set the value). - * @param aOldValue Set to the old value of the attribute, but only if there - * are event listeners. If set, the type of aOldValue will be either + * @param aOldValue [out] Set to the old value of the attribute, but only if + * there are event listeners. If set, the type of aOldValue will be either * nsAttrValue::eString or nsAttrValue::eAtom. - * @param aModType Set to nsIDOMMutationEvent::MODIFICATION or to + * @param aModType [out] Set to nsIDOMMutationEvent::MODIFICATION or to * nsIDOMMutationEvent::ADDITION, but only if this helper returns true - * @param aHasListeners Set to true if there are mutation event listeners - * listening for NS_EVENT_BITS_MUTATION_ATTRMODIFIED + * @param aHasListeners [out] Set to true if there are mutation event + * listeners listening for NS_EVENT_BITS_MUTATION_ATTRMODIFIED + * @param aOldValueSet [out] Indicates whether an old attribute value has been + * stored in aOldValue. The bool will be set to true if a value was stored. */ bool MaybeCheckSameAttrVal(int32_t aNamespaceID, nsIAtom* aName, nsIAtom* aPrefix, const nsAttrValueOrString& aValue, bool aNotify, nsAttrValue& aOldValue, - uint8_t* aModType, bool* aHasListeners); + uint8_t* aModType, bool* aHasListeners, + bool* aOldValueSet); + + /** + * Notifies mutation listeners if aNotify is true, there are mutation + * listeners, and the attribute value is changing. + * + * @param aNamespaceID The namespace of the attribute + * @param aName The local name of the attribute + * @param aPrefix The prefix of the attribute + * @param aValue The value that the attribute is being changed to + * @param aNotify If true, mutation listeners will be notified if they exist + * and the attribute value is changing + * @param aOldValue [out] Set to the old value of the attribute, but only if + * there are event listeners. If set, the type of aOldValue will be either + * nsAttrValue::eString or nsAttrValue::eAtom. + * @param aModType [out] Set to nsIDOMMutationEvent::MODIFICATION or to + * nsIDOMMutationEvent::ADDITION, but only if this helper returns true + * @param aHasListeners [out] Set to true if there are mutation event + * listeners listening for NS_EVENT_BITS_MUTATION_ATTRMODIFIED + * @param aOldValueSet [out] Indicates whether an old attribute value has been + * stored in aOldValue. The bool will be set to true if a value was stored. + */ bool OnlyNotifySameValueSet(int32_t aNamespaceID, nsIAtom* aName, nsIAtom* aPrefix, const nsAttrValueOrString& aValue, bool aNotify, nsAttrValue& aOldValue, - uint8_t* aModType, bool* aHasListeners); + uint8_t* aModType, bool* aHasListeners, + bool* aOldValueSet); + + /** + * Sets the class attribute to a value that contains no whitespace. + * Assumes that we are not notifying and that the attribute hasn't been + * set previously. + */ + nsresult SetSingleClassFromParser(nsIAtom* aSingleClassName); virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, nsIAtom* aPrefix, const nsAString& aValue, bool aNotify) override; @@ -589,7 +645,7 @@ public: * guaranteed (e.g. we could have class=""). */ const nsAttrValue* GetClasses() const { - if (HasFlag(NODE_MAY_HAVE_CLASS)) { + if (MayHaveClass()) { return DoGetClasses(); } return nullptr; @@ -761,6 +817,25 @@ public: already_AddRefed<nsIHTMLCollection> GetElementsByClassName(const nsAString& aClassNames); + CSSPseudoElementType GetPseudoElementType() const { + if (!HasProperties()) { + return CSSPseudoElementType::NotPseudo; + } + nsresult rv = NS_OK; + auto raw = GetProperty(nsGkAtoms::pseudoProperty, &rv); + if (rv == NS_PROPTABLE_PROP_NOT_THERE) { + return CSSPseudoElementType::NotPseudo; + } + return CSSPseudoElementType(reinterpret_cast<uintptr_t>(raw)); + } + + void SetPseudoElementType(CSSPseudoElementType aPseudo) { + static_assert(sizeof(CSSPseudoElementType) <= sizeof(uintptr_t), + "Need to be able to store this in a void*"); + MOZ_ASSERT(aPseudo != CSSPseudoElementType::NotPseudo); + SetProperty(nsGkAtoms::pseudoProperty, reinterpret_cast<void*>(aPseudo)); + } + private: /** * Implement the algorithm specified at @@ -848,8 +923,15 @@ public: already_AddRefed<DOMRectList> GetClientRects(); already_AddRefed<DOMRect> GetBoundingClientRect(); + // Shadow DOM v1 + already_AddRefed<ShadowRoot> AttachShadow(const ShadowRootInit& aInit, + ErrorResult& aError); + ShadowRoot* GetShadowRootByMode() const; + void SetSlot(const nsAString& aName, ErrorResult& aError); + void GetSlot(nsAString& aName); + + // [deprecated] Shadow DOM v0 already_AddRefed<ShadowRoot> CreateShadowRoot(ErrorResult& aError); - already_AddRefed<DestinationInsertionPointList> GetDestinationInsertionPoints(); ShadowRoot *FastGetShadowRoot() const { @@ -1239,7 +1321,10 @@ protected: * its current value) is !StoresOwnData() --- in which * case the current value is probably already useless. * If the current value is StoresOwnData() (or absent), - * aOldValue will not be used. + * aOldValue will not be used. aOldValue will only be set + * in certain circumstances (there are mutation + * listeners, element is a custom element, attribute was + * not previously unset). Otherwise it will be null. * @param aParsedValue parsed new value of attribute. Replaced by the * old value of the attribute. This old value is only * useful if either it or the new value is StoresOwnData. @@ -1248,16 +1333,19 @@ protected: * @param aFireMutation should mutation-events be fired? * @param aNotify should we notify document-observers? * @param aCallAfterSetAttr should we call AfterSetAttr? + * @param aComposedDocument The current composed document of the element. */ nsresult SetAttrAndNotify(int32_t aNamespaceID, nsIAtom* aName, nsIAtom* aPrefix, - const nsAttrValue& aOldValue, + const nsAttrValue* aOldValue, nsAttrValue& aParsedValue, uint8_t aModType, bool aFireMutation, bool aNotify, - bool aCallAfterSetAttr); + bool aCallAfterSetAttr, + nsIDocument* aComposedDocument, + const mozAutoDocUpdate& aGuard); /** * Scroll to a new position using behavior evaluated from CSS and @@ -1295,14 +1383,21 @@ protected: * * @param aDocument the current document of this node (an optimization) * @param aName the name of the attribute - * @param aValue the nsAttrValue to set + * @param aValue the nsAttrValue to set. Will be swapped with the existing + * value of the attribute if the attribute already exists. + * @param [out] aValueWasSet If the attribute was not set previously, + * aValue will be swapped with an empty attribute + * and aValueWasSet will be set to false. Otherwise, + * aValueWasSet will be set to true and aValue will + * contain the previous value set. * @param [out] aRetval the nsresult status of the operation, if any. * @return true if the setting was attempted, false otherwise. */ - virtual bool SetMappedAttribute(nsIDocument* aDocument, - nsIAtom* aName, - nsAttrValue& aValue, - nsresult* aRetval); + virtual bool SetAndSwapMappedAttribute(nsIDocument* aDocument, + nsIAtom* aName, + nsAttrValue& aValue, + bool* aValueWasSet, + nsresult* aRetval); /** * Hook that is called by Element::SetAttr to allow subclasses to @@ -1315,33 +1410,92 @@ protected: * @param aName the localname of the attribute being set * @param aValue the value it's being set to represented as either a string or * a parsed nsAttrValue. Alternatively, if the attr is being removed it - * will be null. BeforeSetAttr is allowed to modify aValue by parsing - * the string to an nsAttrValue (to avoid having to reparse it in - * ParseAttribute). + * will be null. * @param aNotify Whether we plan to notify document observers. */ - // Note that this is inlined so that when subclasses call it it gets - // inlined. Those calls don't go through a vtable. virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, - nsAttrValueOrString* aValue, + const nsAttrValueOrString* aValue, bool aNotify); /** * Hook that is called by Element::SetAttr to allow subclasses to * deal with attribute sets. This will only be called after we have called - * SetAndTakeAttr and AttributeChanged (that is, after we have actually set - * the attr). It will always be called under a scriptblocker. + * SetAndSwapAttr (that is, after we have actually set the attr). It will + * always be called under a scriptblocker. * * @param aNamespaceID the namespace of the attr being set * @param aName the localname of the attribute being set * @param aValue the value it's being set to. If null, the attr is being * removed. + * @param aOldValue the value that the attribute had previously. If null, + * the attr was not previously set. This argument may not have the + * correct value for SVG elements, or other cases in which the + * attribute value doesn't store its own data * @param aNotify Whether we plan to notify document observers. */ // Note that this is inlined so that when subclasses call it it gets // inlined. Those calls don't go through a vtable. virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) + { + return NS_OK; + } + + /** + * This function shall be called just before the id attribute changes. It will + * be called after BeforeSetAttr. If the attribute being changed is not the id + * attribute, this function does nothing. Otherwise, it will remove the old id + * from the document's id cache. + * + * This must happen after BeforeSetAttr (rather than during) because the + * the subclasses' calls to BeforeSetAttr may notify on state changes. If they + * incorrectly determine whether the element had an id, the element may not be + * restyled properly. + * + * @param aNamespaceID the namespace of the attr being set + * @param aName the localname of the attribute being set + * @param aValue the new id value. Will be null if the id is being unset. + */ + void PreIdMaybeChange(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString* aValue); + + /** + * This function shall be called just after the id attribute changes. It will + * be called before AfterSetAttr. If the attribute being changed is not the id + * attribute, this function does nothing. Otherwise, it will add the new id to + * the document's id cache and properly set the ElementHasID flag. + * + * This must happen before AfterSetAttr (rather than during) because the + * the subclasses' calls to AfterSetAttr may notify on state changes. If they + * incorrectly determine whether the element now has an id, the element may + * not be restyled properly. + * + * @param aNamespaceID the namespace of the attr being set + * @param aName the localname of the attribute being set + * @param aValue the new id value. Will be null if the id is being unset. + */ + void PostIdMaybeChange(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue); + + /** + * Usually, setting an attribute to the value that it already has results in + * no action. However, in some cases, setting an attribute to its current + * value should have the effect of, for example, forcing a reload of + * network data. To address that, this function will be called in this + * situation to allow the handling of such a case. + * + * @param aNamespaceID the namespace of the attr being set + * @param aName the localname of the attribute being set + * @param aValue the value it's being set to represented as either a string or + * a parsed nsAttrValue. + * @param aNotify Whether we plan to notify document observers. + */ + // Note that this is inlined so that when subclasses call it it gets + // inlined. Those calls don't go through a vtable. + virtual nsresult OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString& aValue, + bool aNotify) { return NS_OK; } @@ -1400,7 +1554,7 @@ protected: /** * Handle status bar updates before they can be cancelled. */ - nsresult PreHandleEventForLinks(EventChainPreVisitor& aVisitor); + nsresult GetEventTargetParentForLinks(EventChainPreVisitor& aVisitor); /** * Handle default actions for link event if the event isn't consumed yet. @@ -1460,30 +1614,6 @@ private: nsCOMPtr<nsIDocument> mDoc; }; -class DestinationInsertionPointList : public nsINodeList -{ -public: - explicit DestinationInsertionPointList(Element* aElement); - - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DestinationInsertionPointList) - - // nsIDOMNodeList - NS_DECL_NSIDOMNODELIST - - // nsINodeList - virtual nsIContent* Item(uint32_t aIndex) override; - virtual int32_t IndexOf(nsIContent* aContent) override; - virtual nsINode* GetParentObject() override { return mParent; } - virtual uint32_t Length() const; - virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; -protected: - virtual ~DestinationInsertionPointList(); - - RefPtr<Element> mParent; - nsCOMArray<nsIContent> mDestinationPoints; -}; - NS_DEFINE_STATIC_IID_ACCESSOR(Element, NS_ELEMENT_IID) inline bool diff --git a/dom/base/ElementInlines.h b/dom/base/ElementInlines.h index c68bd012e..df2cc2e24 100644 --- a/dom/base/ElementInlines.h +++ b/dom/base/ElementInlines.h @@ -25,6 +25,17 @@ Element::UnregisterActivityObserver() OwnerDoc()->UnregisterActivityObserver(this); } +inline Element* +Element::GetFlattenedTreeParentElementForStyle() const +{ + nsINode* parentNode = GetFlattenedTreeParentNodeForStyle(); + if MOZ_LIKELY(parentNode && parentNode->IsElement()) { + return parentNode->AsElement(); + } + + return nullptr; +} + } // namespace dom } // namespace mozilla diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp index 526c3c9d4..6fbe04d25 100644 --- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -123,6 +123,7 @@ #include "mozilla/CORSMode.h" #include "mozilla/dom/ShadowRoot.h" +#include "mozilla/dom/HTMLSlotElement.h" #include "mozilla/dom/HTMLTemplateElement.h" #include "nsStyledElement.h" @@ -151,8 +152,56 @@ nsIContent::FindFirstNonChromeOnlyAccessContent() const return nullptr; } +// https://dom.spec.whatwg.org/#dom-slotable-assignedslot +HTMLSlotElement* +nsIContent::GetAssignedSlotByMode() const +{ + /** + * Get slotable's assigned slot for the result of + * find a slot with open flag UNSET [1]. + * + * [1] https://dom.spec.whatwg.org/#assign-a-slot + */ + HTMLSlotElement* slot = GetAssignedSlot(); + if (!slot) { + return nullptr; + } + + MOZ_ASSERT(GetParent()); + MOZ_ASSERT(GetParent()->GetShadowRoot()); + + /** + * Additional check for open flag SET: + * If slotable’s parent’s shadow root's mode is not "open", + * then return null. + */ + if (GetParent()->GetShadowRoot()->IsClosed()) { + return nullptr; + } + + return slot; +} + +nsINode* +nsIContent::GetFlattenedTreeParentForMaybeAssignedNode() const +{ + if (HTMLSlotElement* assignedSlot = GetAssignedSlot()) { + return assignedSlot; + } + + HTMLSlotElement* parentSlot = HTMLSlotElement::FromContent(GetParent()); + if (!parentSlot) { + return nullptr; + } + + // If this is not an unassigned node, then it must be a fallback content. + MOZ_ASSERT(parentSlot->AssignedNodes().IsEmpty()); + + return parentSlot; +} + nsINode* -nsIContent::GetFlattenedTreeParentNodeInternal() const +nsIContent::GetFlattenedTreeParentNodeInternal(FlattenedParentType aType) const { nsINode* parentNode = GetParentNode(); if (!parentNode || !parentNode->IsContent()) { @@ -161,18 +210,51 @@ nsIContent::GetFlattenedTreeParentNodeInternal() const } nsIContent* parent = parentNode->AsContent(); + if (aType == eForStyle && + IsRootOfNativeAnonymousSubtree() && + OwnerDoc()->GetRootElement() == parent) { + // When getting the flattened tree parent for style, we return null + // for any "document level" native anonymous content subtree root. + // This is NAC generated by an ancestor frame of the document element's + // primary frame, and includes scrollbar elements created by the root + // scroll frame, and the "custom content container" and accessible caret + // generated by the nsCanvasFrame. We distinguish document level NAC + // from NAC generated by the root element's primary frame below. + nsIFrame* parentFrame = parent->GetPrimaryFrame(); + if (!parentFrame) { + // If the root element has no primary frame, it means it can't have + // generated any NAC itself. Thus any NAC we have here must have + // been generated by an ancestor frame. + // + // If we are in here, then either the root element is display:none, or + // we are in the middle of constructing the root of the frame tree and + // we are trying to eagerly restyle document level NAC in + // nsCSSFrameConstructor::GetAnonymousContent before the root + // element's frame has been constructed. + return nullptr; + } + nsIAnonymousContentCreator* creator = do_QueryFrame(parentFrame); + if (!creator) { + // If the root element does have a frame, but does not implement + // nsIAnonymousContentCreator, then this must be document level NAC. + return nullptr; + } + AutoTArray<nsIContent*, 8> elements; + creator->AppendAnonymousContentTo(elements, 0); + if (!elements.Contains(this)) { + // If the root element does have a frame, and also does implement + // nsIAnonymousContentCreator, but didn't create this node, then + // it must be document level NAC. + return nullptr; + } + } + if (parent && nsContentUtils::HasDistributedChildren(parent) && nsContentUtils::IsInSameAnonymousTree(parent, this)) { - // This node is distributed to insertion points, thus we - // need to consult the destination insertion points list to - // figure out where this node was inserted in the flattened tree. - // It may be the case that |parent| distributes its children - // but the child does not match any insertion points, thus - // the flattened tree parent is nullptr. - nsTArray<nsIContent*>* destInsertionPoints = GetExistingDestInsertionPoints(); - parent = destInsertionPoints && !destInsertionPoints->IsEmpty() ? - destInsertionPoints->LastElement()->GetParent() : nullptr; - } else if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) { + return GetFlattenedTreeParentForMaybeAssignedNode(); + } + + if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) { nsIContent* insertionParent = GetXBLInsertionParent(); if (insertionParent) { parent = insertionParent; @@ -575,6 +657,9 @@ FragmentOrElement::nsDOMSlots::Traverse(nsCycleCollectionTraversalCallback &cb) NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExtendedSlots->mContainingShadow"); cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mExtendedSlots->mContainingShadow)); + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExtendedSlots->mAssignedSlot"); + cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mExtendedSlots->mAssignedSlot.get())); + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExtendedSlots->mXBLBinding"); cb.NoteNativeChild(mExtendedSlots->mXBLBinding, NS_CYCLE_COLLECTION_PARTICIPANT(nsXBLBinding)); @@ -618,6 +703,7 @@ FragmentOrElement::nsDOMSlots::Unlink() mExtendedSlots->mLabelsList = nullptr; mExtendedSlots->mShadowRoot = nullptr; mExtendedSlots->mContainingShadow = nullptr; + mExtendedSlots->mAssignedSlot = nullptr; MOZ_ASSERT(!(mExtendedSlots->mXBLBinding)); mExtendedSlots->mXBLInsertionParent = nullptr; if (mExtendedSlots->mCustomElementData) { @@ -718,7 +804,7 @@ FindChromeAccessOnlySubtreeOwner(nsIContent* aContent) } nsresult -nsIContent::PreHandleEvent(EventChainPreVisitor& aVisitor) +nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor) { //FIXME! Document how this event retargeting works, Bug 329124. aVisitor.mCanHandle = true; @@ -727,6 +813,7 @@ nsIContent::PreHandleEvent(EventChainPreVisitor& aVisitor) // Don't propagate mouseover and mouseout events when mouse is moving // inside chrome access only content. bool isAnonForEvents = IsRootOfChromeAccessOnlySubtree(); + aVisitor.mRootOfClosedTree = isAnonForEvents; if ((aVisitor.mEvent->mMessage == eMouseOver || aVisitor.mEvent->mMessage == eMouseOut || aVisitor.mEvent->mMessage == ePointerOver || @@ -738,7 +825,7 @@ nsIContent::PreHandleEvent(EventChainPreVisitor& aVisitor) ((this == aVisitor.mEvent->mOriginalTarget && !ChromeOnlyAccess()) || isAnonForEvents || GetShadowRoot())) { nsCOMPtr<nsIContent> relatedTarget = - do_QueryInterface(aVisitor.mEvent->AsMouseEvent()->relatedTarget); + do_QueryInterface(aVisitor.mEvent->AsMouseEvent()->mRelatedTarget); if (relatedTarget && relatedTarget->OwnerDoc() == OwnerDoc()) { @@ -748,7 +835,7 @@ nsIContent::PreHandleEvent(EventChainPreVisitor& aVisitor) nsIContent* adjustedTarget = Event::GetShadowRelatedTarget(this, relatedTarget); if (this == adjustedTarget) { - aVisitor.mParentTarget = nullptr; + aVisitor.SetParentTarget(nullptr, false); aVisitor.mCanHandle = false; return NS_OK; } @@ -805,7 +892,7 @@ nsIContent::PreHandleEvent(EventChainPreVisitor& aVisitor) originalTarget->FindFirstNonChromeOnlyAccessContent()) ? "" : "Wrong event propagation!?!\n"); #endif - aVisitor.mParentTarget = nullptr; + aVisitor.SetParentTarget(nullptr, false); // Event should not propagate to non-anon content. aVisitor.mCanHandle = isAnonForEvents; return NS_OK; @@ -816,58 +903,10 @@ nsIContent::PreHandleEvent(EventChainPreVisitor& aVisitor) } } - nsIContent* parent = GetParent(); - - // Web components have a special event chain that need to account - // for destination insertion points where nodes have been distributed. - nsTArray<nsIContent*>* destPoints = GetExistingDestInsertionPoints(); - if (destPoints && !destPoints->IsEmpty()) { - // Push destination insertion points to aVisitor.mDestInsertionPoints - // excluding shadow insertion points. - bool didPushNonShadowInsertionPoint = false; - for (uint32_t i = 0; i < destPoints->Length(); i++) { - nsIContent* point = destPoints->ElementAt(i); - if (!ShadowRoot::IsShadowInsertionPoint(point)) { - aVisitor.mDestInsertionPoints.AppendElement(point); - didPushNonShadowInsertionPoint = true; - } - } - - // Next node in the event path is the final destination - // (non-shadow) insertion point that was pushed. - if (didPushNonShadowInsertionPoint) { - parent = aVisitor.mDestInsertionPoints.LastElement(); - aVisitor.mDestInsertionPoints.SetLength( - aVisitor.mDestInsertionPoints.Length() - 1); - } - } - - ShadowRoot* thisShadowRoot = ShadowRoot::FromNode(this); - if (thisShadowRoot) { - if (!aVisitor.mEvent->mFlags.mComposed) { - // If we do stop propagation, we still want to propagate - // the event to chrome (nsPIDOMWindow::GetParentTarget()). - // The load event is special in that we don't ever propagate it - // to chrome. - nsCOMPtr<nsPIDOMWindowOuter> win = OwnerDoc()->GetWindow(); - EventTarget* parentTarget = win && aVisitor.mEvent->mMessage != eLoad - ? win->GetParentTarget() : nullptr; - - aVisitor.mParentTarget = parentTarget; - return NS_OK; - } - - if (!aVisitor.mDestInsertionPoints.IsEmpty()) { - parent = aVisitor.mDestInsertionPoints.LastElement(); - aVisitor.mDestInsertionPoints.SetLength( - aVisitor.mDestInsertionPoints.Length() - 1); - } else { - // The pool host for the youngest shadow root is shadow DOM host, - // for older shadow roots, it is the shadow insertion point - // where the shadow root is projected, nullptr if none exists. - parent = thisShadowRoot->GetPoolHost(); - } - } + // Event parent is the assigned slot, if node is assigned, or node's parent + // otherwise. + HTMLSlotElement* slot = GetAssignedSlot(); + nsIContent* parent = slot ? slot : GetParent(); // Event may need to be retargeted if this is the root of a native // anonymous content subtree or event is dispatched somewhere inside XBL. @@ -905,11 +944,17 @@ nsIContent::PreHandleEvent(EventChainPreVisitor& aVisitor) if (!aVisitor.mEvent->mFlags.mComposedInNativeAnonymousContent && IsRootOfNativeAnonymousSubtree() && OwnerDoc() && OwnerDoc()->GetWindow()) { - aVisitor.mParentTarget = OwnerDoc()->GetWindow()->GetParentTarget(); + aVisitor.SetParentTarget(OwnerDoc()->GetWindow()->GetParentTarget(), true); } else if (parent) { - aVisitor.mParentTarget = parent; + aVisitor.SetParentTarget(parent, false); + if (slot) { + ShadowRoot* root = slot->GetContainingShadow(); + if (root && root->IsClosed()) { + aVisitor.mParentIsSlotInClosedTree = true; + } + } } else { - aVisitor.mParentTarget = GetComposedDoc(); + aVisitor.SetParentTarget(GetComposedDoc(), false); } return NS_OK; } @@ -1086,21 +1131,18 @@ FragmentOrElement::SetShadowRoot(ShadowRoot* aShadowRoot) slots->mShadowRoot = aShadowRoot; } -nsTArray<nsIContent*>& -FragmentOrElement::DestInsertionPoints() +HTMLSlotElement* +FragmentOrElement::GetAssignedSlot() const { - nsExtendedDOMSlots* slots = ExtendedDOMSlots(); - return slots->mDestInsertionPoints; + nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots(); + return slots ? slots->mAssignedSlot.get() : nullptr; } -nsTArray<nsIContent*>* -FragmentOrElement::GetExistingDestInsertionPoints() const +void +FragmentOrElement::SetAssignedSlot(HTMLSlotElement* aSlot) { - nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots(); - if (slots) { - return &slots->mDestInsertionPoints; - } - return nullptr; + nsExtendedDOMSlots* slots = ExtendedDOMSlots(); + slots->mAssignedSlot = aSlot; } void @@ -2368,9 +2410,8 @@ FragmentOrElement::SetIsElementInStyleScopeFlagOnShadowTree(bool aInStyleScope) NS_ASSERTION(IsElement(), "calling SetIsElementInStyleScopeFlagOnShadowTree " "on a non-Element is useless"); ShadowRoot* shadowRoot = GetShadowRoot(); - while (shadowRoot) { + if (shadowRoot) { shadowRoot->SetIsElementInStyleScopeFlagOnSubtree(aInStyleScope); - shadowRoot = shadowRoot->GetOlderShadowRoot(); } } diff --git a/dom/base/FragmentOrElement.h b/dom/base/FragmentOrElement.h index 4edd88908..f1fa046ee 100644 --- a/dom/base/FragmentOrElement.h +++ b/dom/base/FragmentOrElement.h @@ -62,6 +62,7 @@ public: // nsIWeakReference NS_DECL_NSIWEAKREFERENCE virtual size_t SizeOfOnlyThis(mozilla::MallocSizeOf aMallocSizeOf) const override; + virtual bool IsAlive() const override { return mNode != nullptr; } void NoticeNodeDestruction() { @@ -154,9 +155,9 @@ public: virtual void SetXBLBinding(nsXBLBinding* aBinding, nsBindingManager* aOldBindingManager = nullptr) override; virtual ShadowRoot *GetContainingShadow() const override; - virtual nsTArray<nsIContent*> &DestInsertionPoints() override; - virtual nsTArray<nsIContent*> *GetExistingDestInsertionPoints() const override; virtual void SetShadowRoot(ShadowRoot* aBinding) override; + virtual mozilla::dom::HTMLSlotElement* GetAssignedSlot() const override; + virtual void SetAssignedSlot(mozilla::dom::HTMLSlotElement* aSlot) override; virtual nsIContent *GetXBLInsertionParent() const override; virtual void SetXBLInsertionParent(nsIContent* aContent) override; virtual bool IsLink(nsIURI** aURI) const override; @@ -294,10 +295,9 @@ public: RefPtr<ShadowRoot> mContainingShadow; /** - * An array of web component insertion points to which this element - * is distributed. + * The assigned slot associated with this element. */ - nsTArray<nsIContent*> mDestInsertionPoints; + RefPtr<mozilla::dom::HTMLSlotElement> mAssignedSlot; /** * XBL binding installed on the element. diff --git a/dom/base/GroupedSHistory.h b/dom/base/GroupedSHistory.h index 8c88a0aaf..c5e75d639 100644 --- a/dom/base/GroupedSHistory.h +++ b/dom/base/GroupedSHistory.h @@ -7,6 +7,8 @@ #ifndef GroupedSHistory_h #define GroupedSHistory_h +#include "nsCOMArray.h" +#include "nsCycleCollectionParticipant.h" // NS_DECL_CYCLE_* #include "nsIFrameLoader.h" #include "nsIGroupedSHistory.h" #include "nsIPartialSHistory.h" diff --git a/dom/base/ImageTracker.cpp b/dom/base/ImageTracker.cpp index 9fef059bc..d0c231981 100644 --- a/dom/base/ImageTracker.cpp +++ b/dom/base/ImageTracker.cpp @@ -8,6 +8,8 @@ * animating */ #include "ImageTracker.h" +#include "mozilla/Preferences.h" +#include "nsAppRunner.h" // for XRE_IsContentProcess namespace mozilla { namespace dom { diff --git a/dom/base/ImageTracker.h b/dom/base/ImageTracker.h index d33dc55f8..f16461192 100644 --- a/dom/base/ImageTracker.h +++ b/dom/base/ImageTracker.h @@ -10,6 +10,7 @@ #ifndef mozilla_dom_ImageTracker #define mozilla_dom_ImageTracker +#include "imgIRequest.h" #include "nsDataHashtable.h" #include "nsHashKeys.h" diff --git a/dom/base/MutableBlobStorage.h b/dom/base/MutableBlobStorage.h index fad391b16..ed368e5e6 100644 --- a/dom/base/MutableBlobStorage.h +++ b/dom/base/MutableBlobStorage.h @@ -7,6 +7,9 @@ #ifndef mozilla_dom_MutableBlobStorage_h #define mozilla_dom_MutableBlobStorage_h +#include "nsCycleCollectionParticipant.h" +#include "nsThreadUtils.h" +#include "nsProxyRelease.h" #include "mozilla/RefPtr.h" #include "prio.h" diff --git a/dom/base/MutableBlobStreamListener.cpp b/dom/base/MutableBlobStreamListener.cpp index 6769ad3e6..afcc04d9f 100644 --- a/dom/base/MutableBlobStreamListener.cpp +++ b/dom/base/MutableBlobStreamListener.cpp @@ -5,6 +5,7 @@ #include "MutableBlobStreamListener.h" #include "MutableBlobStorage.h" +#include "nsIInputStream.h" namespace mozilla { namespace dom { diff --git a/dom/base/NodeInfo.cpp b/dom/base/NodeInfo.cpp index d32a00fd3..f055dfc75 100644 --- a/dom/base/NodeInfo.cpp +++ b/dom/base/NodeInfo.cpp @@ -15,6 +15,7 @@ #include "mozilla/ArrayUtils.h" #include "mozilla/Likely.h" +#include "mozilla/Unused.h" #include "nsNodeInfoManager.h" #include "nsCOMPtr.h" diff --git a/dom/base/Pose.h b/dom/base/Pose.h index c7ef1b381..0817c8c96 100644 --- a/dom/base/Pose.h +++ b/dom/base/Pose.h @@ -8,6 +8,7 @@ #define mozilla_dom_Pose_h #include "nsWrapperCache.h" +#include "mozilla/ErrorResult.h" namespace mozilla { namespace dom { diff --git a/dom/base/ShadowRoot.cpp b/dom/base/ShadowRoot.cpp index 831987a96..c4e56f3fb 100644 --- a/dom/base/ShadowRoot.cpp +++ b/dom/base/ShadowRoot.cpp @@ -14,9 +14,9 @@ #include "nsIDOMHTMLElement.h" #include "nsIStyleSheetLinkingElement.h" #include "mozilla/dom/Element.h" -#include "mozilla/dom/HTMLContentElement.h" -#include "mozilla/dom/HTMLShadowElement.h" +#include "mozilla/dom/HTMLSlotElement.h" #include "nsXBLPrototypeBinding.h" +#include "mozilla/EventDispatcher.h" #include "mozilla/StyleSheet.h" #include "mozilla/StyleSheetInlines.h" @@ -27,10 +27,7 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(ShadowRoot) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ShadowRoot, DocumentFragment) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPoolHost) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetList) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOlderShadow) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mYoungerShadow) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMStyleSheets) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAssociatedBinding) for (auto iter = tmp->mIdentifierMap.ConstIter(); !iter.Done(); iter.Next()) { @@ -38,18 +35,14 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ShadowRoot, } NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ShadowRoot, - DocumentFragment) - if (tmp->mPoolHost) { - tmp->mPoolHost->RemoveMutationObserver(tmp); +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ShadowRoot) + if (tmp->GetHost()) { + tmp->GetHost()->RemoveMutationObserver(tmp); } - NS_IMPL_CYCLE_COLLECTION_UNLINK(mPoolHost) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mStyleSheetList) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mOlderShadow) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mYoungerShadow) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMStyleSheets) NS_IMPL_CYCLE_COLLECTION_UNLINK(mAssociatedBinding) tmp->mIdentifierMap.Clear(); -NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(DocumentFragment) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ShadowRoot) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent) @@ -59,14 +52,16 @@ NS_INTERFACE_MAP_END_INHERITING(DocumentFragment) NS_IMPL_ADDREF_INHERITED(ShadowRoot, DocumentFragment) NS_IMPL_RELEASE_INHERITED(ShadowRoot, DocumentFragment) -ShadowRoot::ShadowRoot(nsIContent* aContent, +ShadowRoot::ShadowRoot(Element* aElement, bool aClosed, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, nsXBLPrototypeBinding* aProtoBinding) - : DocumentFragment(aNodeInfo), mPoolHost(aContent), - mProtoBinding(aProtoBinding), mShadowElement(nullptr), - mInsertionPointChanged(false), mIsComposedDocParticipant(false) + : DocumentFragment(aNodeInfo) + , mProtoBinding(aProtoBinding) + , mInsertionPointChanged(false) + , mIsComposedDocParticipant(false) { - SetHost(aContent); + SetHost(aElement); + mMode = aClosed ? ShadowRootMode::Closed : ShadowRootMode::Open; // Nodes in a shadow tree should never store a value // in the subtree root pointer, nodes in the shadow tree @@ -75,29 +70,27 @@ ShadowRoot::ShadowRoot(nsIContent* aContent, SetFlags(NODE_IS_IN_SHADOW_TREE); - ExtendedDOMSlots()->mBindingParent = aContent; + ExtendedDOMSlots()->mBindingParent = aElement; ExtendedDOMSlots()->mContainingShadow = this; // Add the ShadowRoot as a mutation observer on the host to watch // for mutations because the insertion points in this ShadowRoot // may need to be updated when the host children are modified. - mPoolHost->AddMutationObserver(this); + GetHost()->AddMutationObserver(this); } ShadowRoot::~ShadowRoot() { - if (mPoolHost) { - // mPoolHost may have been unlinked or a new ShadowRoot may have been - // creating, making this one obsolete. - mPoolHost->RemoveMutationObserver(this); + if (auto* host = GetHost()) { + // mHost may have been unlinked or a new ShadowRoot may have been + // created, making this one obsolete. + host->RemoveMutationObserver(this); } UnsetFlags(NODE_IS_IN_SHADOW_TREE); // nsINode destructor expects mSubtreeRoot == this. SetSubtreeRootPointer(this); - - SetHost(nullptr); } JSObject* @@ -119,12 +112,115 @@ ShadowRoot::FromNode(nsINode* aNode) } void +ShadowRoot::AddSlot(HTMLSlotElement* aSlot) +{ + MOZ_ASSERT(aSlot); + + // Note that if name attribute missing, the slot is a default slot. + nsAutoString name; + aSlot->GetName(name); + + nsTArray<HTMLSlotElement*>* currentSlots = mSlotMap.LookupOrAdd(name); + MOZ_ASSERT(currentSlots); + + HTMLSlotElement* oldSlot = currentSlots->IsEmpty() ? + nullptr : currentSlots->ElementAt(0); + + TreeOrderComparator comparator; + currentSlots->InsertElementSorted(aSlot, comparator); + + HTMLSlotElement* currentSlot = currentSlots->ElementAt(0); + if (currentSlot != aSlot) { + return; + } + + bool doEnqueueSlotChange = false; + if (oldSlot && oldSlot != currentSlot) { + // Move assigned nodes from old slot to new slot. + const nsTArray<RefPtr<nsINode>>& assignedNodes = oldSlot->AssignedNodes(); + while (assignedNodes.Length() > 0) { + nsINode* assignedNode = assignedNodes[0]; + + oldSlot->RemoveAssignedNode(assignedNode); + currentSlot->AppendAssignedNode(assignedNode); + doEnqueueSlotChange = true; + } + + if (doEnqueueSlotChange) { + oldSlot->EnqueueSlotChangeEvent(); + currentSlot->EnqueueSlotChangeEvent(); + } + } else { + // Otherwise add appropriate nodes to this slot from the host. + for (nsIContent* child = GetHost()->GetFirstChild(); + child; + child = child->GetNextSibling()) { + nsAutoString slotName; + child->GetAttr(kNameSpaceID_None, nsGkAtoms::slot, slotName); + if (child->IsSlotable() && slotName.Equals(name)) { + currentSlot->AppendAssignedNode(child); + doEnqueueSlotChange = true; + } + } + + if (doEnqueueSlotChange) { + currentSlot->EnqueueSlotChangeEvent(); + } + } +} + +void +ShadowRoot::RemoveSlot(HTMLSlotElement* aSlot) +{ + MOZ_ASSERT(aSlot); + + nsAutoString name; + aSlot->GetName(name); + + nsTArray<HTMLSlotElement*>* currentSlots = mSlotMap.Get(name); + + if (currentSlots) { + if (currentSlots->Length() == 1) { + MOZ_ASSERT(currentSlots->ElementAt(0) == aSlot); + mSlotMap.Remove(name); + + if (aSlot->AssignedNodes().Length() > 0) { + aSlot->ClearAssignedNodes(); + aSlot->EnqueueSlotChangeEvent(); + } + } else { + bool doEnqueueSlotChange = false; + bool doReplaceSlot = currentSlots->ElementAt(0) == aSlot; + currentSlots->RemoveElement(aSlot); + HTMLSlotElement* replacementSlot = currentSlots->ElementAt(0); + + // Move assigned nodes from removed slot to the next slot in + // tree order with the same name. + if (doReplaceSlot) { + const nsTArray<RefPtr<nsINode>>& assignedNodes = aSlot->AssignedNodes(); + while (assignedNodes.Length() > 0) { + nsINode* assignedNode = assignedNodes[0]; + + aSlot->RemoveAssignedNode(assignedNode); + replacementSlot->AppendAssignedNode(assignedNode); + doEnqueueSlotChange = true; + } + + if (doEnqueueSlotChange) { + aSlot->EnqueueSlotChangeEvent(); + replacementSlot->EnqueueSlotChangeEvent(); + } + } + } + } +} + +void ShadowRoot::StyleSheetChanged() { mProtoBinding->FlushSkinSheets(); - nsIPresShell* shell = OwnerDoc()->GetShell(); - if (shell) { + if (nsIPresShell* shell = OwnerDoc()->GetShell()) { OwnerDoc()->BeginUpdate(UPDATE_STYLE); shell->RecordShadowStyleChange(this); OwnerDoc()->EndUpdate(UPDATE_STYLE); @@ -142,16 +238,30 @@ ShadowRoot::InsertSheet(StyleSheet* aSheet, linkingElement->SetStyleSheet(aSheet); // This sets the ownerNode on the sheet + MOZ_DIAGNOSTIC_ASSERT(mProtoBinding->SheetCount() == StyleScope::SheetCount()); +#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED + // FIXME(emilio, bug 1425759): For now we keep them duplicated, the proto + // binding will disappear soon (tm). + { + size_t i = 0; + for (RefPtr<StyleSheet>& sheet : mStyleSheets) { + MOZ_DIAGNOSTIC_ASSERT(sheet.get() == mProtoBinding->StyleSheetAt(i++)); + } + } +#endif + // Find the correct position to insert into the style sheet list (must // be in tree order). - for (size_t i = 0; i <= mProtoBinding->SheetCount(); i++) { - if (i == mProtoBinding->SheetCount()) { + for (size_t i = 0; i <= SheetCount(); i++) { + if (i == SheetCount()) { + AppendStyleSheet(*aSheet); mProtoBinding->AppendStyleSheet(aSheet); break; } - nsINode* sheetOwningNode = mProtoBinding->StyleSheetAt(i)->GetOwnerNode(); + nsINode* sheetOwningNode = SheetAt(i)->GetOwnerNode(); if (nsContentUtils::PositionIsBefore(aLinkingContent, sheetOwningNode)) { + InsertSheetAt(i, *aSheet); mProtoBinding->InsertStyleSheetAt(i, aSheet); break; } @@ -166,6 +276,7 @@ void ShadowRoot::RemoveSheet(StyleSheet* aSheet) { mProtoBinding->RemoveStyleSheet(aSheet); + StyleScope::RemoveSheet(*aSheet); if (aSheet->IsApplicable()) { StyleSheetChanged(); @@ -232,238 +343,158 @@ ShadowRoot::GetElementsByClassName(const nsAString& aClasses) return nsContentUtils::GetElementsByClassName(this, aClasses); } -void -ShadowRoot::AddInsertionPoint(HTMLContentElement* aInsertionPoint) -{ - TreeOrderComparator comparator; - mInsertionPoints.InsertElementSorted(aInsertionPoint, comparator); -} - -void -ShadowRoot::RemoveInsertionPoint(HTMLContentElement* aInsertionPoint) -{ - mInsertionPoints.RemoveElement(aInsertionPoint); -} - -void -ShadowRoot::SetYoungerShadow(ShadowRoot* aYoungerShadow) -{ - mYoungerShadow = aYoungerShadow; - mYoungerShadow->mOlderShadow = this; +nsresult +ShadowRoot::GetEventTargetParent(EventChainPreVisitor& aVisitor) +{ + aVisitor.mCanHandle = true; + aVisitor.mRootOfClosedTree = IsClosed(); + + // https://dom.spec.whatwg.org/#ref-for-get-the-parent%E2%91%A6 + if (!aVisitor.mEvent->mFlags.mComposed) { + nsCOMPtr<nsIContent> originalTarget = + do_QueryInterface(aVisitor.mEvent->mOriginalTarget); + if (originalTarget->GetContainingShadow() == this) { + // If we do stop propagation, we still want to propagate + // the event to chrome (nsPIDOMWindow::GetParentTarget()). + // The load event is special in that we don't ever propagate it + // to chrome. + nsCOMPtr<nsPIDOMWindowOuter> win = OwnerDoc()->GetWindow(); + EventTarget* parentTarget = win && aVisitor.mEvent->mMessage != eLoad + ? win->GetParentTarget() : nullptr; + + aVisitor.SetParentTarget(parentTarget, true); + return NS_OK; + } + } - ChangePoolHost(mYoungerShadow->GetShadowElement()); -} + nsIContent* shadowHost = GetHost(); + aVisitor.SetParentTarget(shadowHost, false); -void -ShadowRoot::RemoveDestInsertionPoint(nsIContent* aInsertionPoint, - nsTArray<nsIContent*>& aDestInsertionPoints) -{ - // Remove the insertion point from the destination insertion points. - // Also remove all succeeding insertion points because it is no longer - // possible for the content to be distributed into deeper node trees. - int32_t index = aDestInsertionPoints.IndexOf(aInsertionPoint); + if (aVisitor.mOriginalTargetIsInAnon) { + nsCOMPtr<nsIContent> content(do_QueryInterface(aVisitor.mEvent->mTarget)); + if (content && content->GetBindingParent() == shadowHost) { + aVisitor.mEventTargetAtParent = shadowHost; + } + } - // It's possible that we already removed the insertion point while processing - // other insertion point removals. - if (index >= 0) { - aDestInsertionPoints.SetLength(index); - } + return NS_OK; } -void -ShadowRoot::DistributeSingleNode(nsIContent* aContent) +const HTMLSlotElement* +ShadowRoot::AssignSlotFor(nsIContent* aContent) { - // Find the insertion point to which the content belongs. - HTMLContentElement* insertionPoint = nullptr; - for (uint32_t i = 0; i < mInsertionPoints.Length(); i++) { - if (mInsertionPoints[i]->Match(aContent)) { - if (mInsertionPoints[i]->MatchedNodes().Contains(aContent)) { - // Node is already matched into the insertion point. We are done. - return; - } - - // Matching may cause the insertion point to drop fallback content. - if (mInsertionPoints[i]->MatchedNodes().IsEmpty() && - static_cast<nsINode*>(mInsertionPoints[i])->GetFirstChild()) { - // This match will cause the insertion point to drop all fallback - // content and used matched nodes instead. Give up on the optimization - // and just distribute all nodes. - DistributeAllNodes(); - return; - } - insertionPoint = mInsertionPoints[i]; - break; - } + nsAutoString slotName; + // Note that if slot attribute is missing, assign it to the first default + // slot, if exists. + aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::slot, slotName); + nsTArray<HTMLSlotElement*>* slots = mSlotMap.Get(slotName); + if (!slots) { + return nullptr; } - // Find the index into the insertion point. - if (insertionPoint) { - nsCOMArray<nsIContent>& matchedNodes = insertionPoint->MatchedNodes(); - // Find the appropriate position in the matched node list for the - // newly distributed content. - bool isIndexFound = false; - MOZ_ASSERT(mPoolHost, "Where did the content come from if there is no pool host?"); - ExplicitChildIterator childIterator(mPoolHost); - for (uint32_t i = 0; i < matchedNodes.Length(); i++) { - // Seek through the host's explicit children until the inserted content - // is found or when the current matched node is reached. - if (childIterator.Seek(aContent, matchedNodes[i])) { - // aContent was found before the current matched node. - insertionPoint->InsertMatchedNode(i, aContent); - isIndexFound = true; - break; + HTMLSlotElement* slot = slots->ElementAt(0); + MOZ_ASSERT(slot); + + // Find the appropriate position in the assigned node list for the + // newly assigned content. + const nsTArray<RefPtr<nsINode>>& assignedNodes = slot->AssignedNodes(); + nsIContent* currentContent = GetHost()->GetFirstChild(); + bool indexFound = false; + uint32_t insertionIndex; + for (uint32_t i = 0; i < assignedNodes.Length(); i++) { + // Seek through the host's explicit children until the + // assigned content is found. + while (currentContent && currentContent != assignedNodes[i]) { + if (currentContent == aContent) { + indexFound = true; + insertionIndex = i; } - } - - if (!isIndexFound) { - // We have still not found an index in the insertion point, - // thus it must be at the end. - MOZ_ASSERT(childIterator.Seek(aContent, nullptr), - "Trying to match a node that is not a candidate to be matched"); - insertionPoint->AppendMatchedNode(aContent); - } - // Handle the case where the parent of the insertion point is a ShadowRoot - // that is projected into the younger ShadowRoot's shadow insertion point. - // The node distributed into the insertion point must be reprojected - // to the shadow insertion point. - if (insertionPoint->GetParent() == this && - mYoungerShadow && mYoungerShadow->GetShadowElement()) { - mYoungerShadow->GetShadowElement()->DistributeSingleNode(aContent); + currentContent = currentContent->GetNextSibling(); } - // Handle the case where the parent of the insertion point has a ShadowRoot. - // The node distributed into the insertion point must be reprojected to the - // insertion points of the parent's ShadowRoot. - ShadowRoot* parentShadow = insertionPoint->GetParent()->GetShadowRoot(); - if (parentShadow) { - parentShadow->DistributeSingleNode(aContent); + if (indexFound) { + break; } + } - // Handle the case where the parent of the insertion point is the <shadow> - // element. The node distributed into the insertion point must be reprojected - // into the older ShadowRoot's insertion points. - if (mShadowElement && mShadowElement == insertionPoint->GetParent()) { - ShadowRoot* olderShadow = mShadowElement->GetOlderShadowRoot(); - if (olderShadow) { - olderShadow->DistributeSingleNode(aContent); - } - } + if (indexFound) { + slot->InsertAssignedNode(insertionIndex, aContent); + } else { + slot->AppendAssignedNode(aContent); } + + return slot; } -void -ShadowRoot::RemoveDistributedNode(nsIContent* aContent) +const HTMLSlotElement* +ShadowRoot::UnassignSlotFor(nsIContent* aNode, const nsAString& aSlotName) { - // Find insertion point containing the content and remove the node. - for (uint32_t i = 0; i < mInsertionPoints.Length(); i++) { - if (mInsertionPoints[i]->MatchedNodes().Contains(aContent)) { - // Removing the matched node may cause the insertion point to use - // fallback content. - if (mInsertionPoints[i]->MatchedNodes().Length() == 1 && - static_cast<nsINode*>(mInsertionPoints[i])->GetFirstChild()) { - // Removing the matched node will cause fallback content to be - // used instead. Give up optimization and distribute all nodes. - DistributeAllNodes(); - return; - } + // Find the insertion point to which the content belongs. Note that if slot + // attribute is missing, unassign it from the first default slot, if exists. + nsTArray<HTMLSlotElement*>* slots = mSlotMap.Get(aSlotName); + if (!slots) { + return nullptr; + } - mInsertionPoints[i]->RemoveMatchedNode(aContent); + HTMLSlotElement* slot = slots->ElementAt(0); + MOZ_ASSERT(slot); - // Handle the case where the parent of the insertion point is a ShadowRoot - // that is projected into the younger ShadowRoot's shadow insertion point. - // The removed node needs to be removed from the shadow insertion point. - if (mInsertionPoints[i]->GetParent() == this) { - if (mYoungerShadow && mYoungerShadow->GetShadowElement()) { - mYoungerShadow->GetShadowElement()->RemoveDistributedNode(aContent); - } - } + if (!slot->AssignedNodes().Contains(aNode)) { + return nullptr; + } - // Handle the case where the parent of the insertion point has a ShadowRoot. - // The removed node needs to be removed from the insertion points of the - // parent's ShadowRoot. - ShadowRoot* parentShadow = mInsertionPoints[i]->GetParent()->GetShadowRoot(); - if (parentShadow) { - parentShadow->RemoveDistributedNode(aContent); - } + slot->RemoveAssignedNode(aNode); + return slot; +} - // Handle the case where the parent of the insertion point is the <shadow> - // element. The removed node must be removed from the older ShadowRoot's - // insertion points. - if (mShadowElement && mShadowElement == mInsertionPoints[i]->GetParent()) { - ShadowRoot* olderShadow = mShadowElement->GetOlderShadowRoot(); - if (olderShadow) { - olderShadow->RemoveDistributedNode(aContent); - } +bool +ShadowRoot::MaybeReassignElement(Element* aElement, + const nsAttrValue* aOldValue) +{ + nsIContent* parent = aElement->GetParent(); + if (parent && parent == GetHost()) { + const HTMLSlotElement* oldSlot = UnassignSlotFor(aElement, + aOldValue ? aOldValue->GetStringValue() : EmptyString()); + const HTMLSlotElement* newSlot = AssignSlotFor(aElement); + + if (oldSlot != newSlot) { + if (oldSlot) { + oldSlot->EnqueueSlotChangeEvent(); } - - break; + if (newSlot) { + newSlot->EnqueueSlotChangeEvent(); + } + return true; } } + + return false; } void -ShadowRoot::DistributeAllNodes() +ShadowRoot::DistributionChanged() { - // Create node pool. - nsTArray<nsIContent*> nodePool; - - // Make sure there is a pool host, an older shadow may not have - // one if the younger shadow does not have a <shadow> element. - if (mPoolHost) { - ExplicitChildIterator childIterator(mPoolHost); - for (nsIContent* content = childIterator.GetNextChild(); - content; - content = childIterator.GetNextChild()) { - nodePool.AppendElement(content); - } + // FIXME(emilio): We could be more granular in a bunch of cases. + auto* host = GetHost(); + if (!host || !host->IsInComposedDoc()) { + return; } - nsTArray<ShadowRoot*> shadowsToUpdate; - - for (uint32_t i = 0; i < mInsertionPoints.Length(); i++) { - mInsertionPoints[i]->ClearMatchedNodes(); - // Assign matching nodes from node pool. - for (uint32_t j = 0; j < nodePool.Length(); j++) { - if (mInsertionPoints[i]->Match(nodePool[j])) { - mInsertionPoints[i]->AppendMatchedNode(nodePool[j]); - nodePool.RemoveElementAt(j--); - } - } - - // Keep track of instances where the content insertion point is distributed - // (parent of insertion point has a ShadowRoot). - nsIContent* insertionParent = mInsertionPoints[i]->GetParent(); - MOZ_ASSERT(insertionParent, "The only way for an insertion point to be in the" - "mInsertionPoints array is to be a descendant of a" - "ShadowRoot, in which case, it should have a parent"); - - // If the parent of the insertion point has a ShadowRoot, the nodes distributed - // to the insertion point must be reprojected to the insertion points of the - // parent's ShadowRoot. - ShadowRoot* parentShadow = insertionParent->GetShadowRoot(); - if (parentShadow && !shadowsToUpdate.Contains(parentShadow)) { - shadowsToUpdate.AppendElement(parentShadow); - } + auto* shell = OwnerDoc()->GetShell(); + if (!shell) { + return; } - // If there is a shadow insertion point in this ShadowRoot, the children - // of the shadow insertion point needs to be distributed into the insertion - // points of the older ShadowRoot. - if (mShadowElement && mOlderShadow) { - mOlderShadow->DistributeAllNodes(); - } + shell->DestroyFramesForAndRestyle(host); +} - // If there is a younger ShadowRoot with a shadow insertion point, - // then the children of this ShadowRoot needs to be distributed to - // the younger ShadowRoot's shadow insertion point. - if (mYoungerShadow && mYoungerShadow->GetShadowElement()) { - mYoungerShadow->GetShadowElement()->DistributeAllNodes(); - } +void +ShadowRoot::DistributeAllNodes() +{ + //XXX Handle <slot>. - for (uint32_t i = 0; i < shadowsToUpdate.Length(); i++) { - shadowsToUpdate[i]->DistributeAllNodes(); - } + DistributionChanged(); } void @@ -507,105 +538,6 @@ ShadowRoot::SetApplyAuthorStyles(bool aApplyAuthorStyles) } } -StyleSheetList* -ShadowRoot::StyleSheets() -{ - if (!mStyleSheetList) { - mStyleSheetList = new ShadowRootStyleSheetList(this); - } - - return mStyleSheetList; -} - -void -ShadowRoot::SetShadowElement(HTMLShadowElement* aShadowElement) -{ - // If there is already a shadow element point, remove - // the projected shadow because it is no longer an insertion - // point. - if (mShadowElement) { - mShadowElement->SetProjectedShadow(nullptr); - } - - if (mOlderShadow) { - // Nodes for distribution will come from the new shadow element. - mOlderShadow->ChangePoolHost(aShadowElement); - } - - // Set the new shadow element to project the older ShadowRoot because - // it is the current shadow insertion point. - mShadowElement = aShadowElement; - if (mShadowElement) { - mShadowElement->SetProjectedShadow(mOlderShadow); - } -} - -void -ShadowRoot::ChangePoolHost(nsIContent* aNewHost) -{ - if (mPoolHost) { - mPoolHost->RemoveMutationObserver(this); - } - - // Clear the nodes matched to content insertion points - // because it is no longer relevant. - for (uint32_t i = 0; i < mInsertionPoints.Length(); i++) { - mInsertionPoints[i]->ClearMatchedNodes(); - } - - mPoolHost = aNewHost; - if (mPoolHost) { - mPoolHost->AddMutationObserver(this); - } -} - -bool -ShadowRoot::IsShadowInsertionPoint(nsIContent* aContent) -{ - if (!aContent) { - return false; - } - - HTMLShadowElement* shadowElem = HTMLShadowElement::FromContent(aContent); - return shadowElem && shadowElem->IsInsertionPoint(); -} - -/** - * Returns whether the web components pool population algorithm - * on the host would contain |aContent|. This function ignores - * insertion points in the pool, thus should only be used to - * test nodes that have not yet been distributed. - */ -bool -ShadowRoot::IsPooledNode(nsIContent* aContent, nsIContent* aContainer, - nsIContent* aHost) -{ - if (nsContentUtils::IsContentInsertionPoint(aContent) || - IsShadowInsertionPoint(aContent)) { - // Insertion points never end up in the pool. - return false; - } - - if (aContainer == aHost && - nsContentUtils::IsInSameAnonymousTree(aContainer, aContent)) { - // Children of the host will end up in the pool. We check to ensure - // that the content is in the same anonymous tree as the container - // because anonymous content may report its container as the host - // but it may not be in the host's child list. - return true; - } - - if (aContainer) { - // Fallback content will end up in pool if its parent is a child of the host. - HTMLContentElement* content = HTMLContentElement::FromContent(aContainer); - return content && content->IsInsertionPoint() && - content->MatchedNodes().IsEmpty() && - aContainer->GetParentNode() == aHost; - } - - return false; -} - void ShadowRoot::AttributeChanged(nsIDocument* aDocument, Element* aElement, @@ -614,13 +546,26 @@ ShadowRoot::AttributeChanged(nsIDocument* aDocument, int32_t aModType, const nsAttrValue* aOldValue) { - if (!IsPooledNode(aElement, aElement->GetParent(), mPoolHost)) { + if (aNameSpaceID != kNameSpaceID_None || aAttribute != nsGkAtoms::slot) { return; } // Attributes may change insertion point matching, find its new distribution. - RemoveDistributedNode(aElement); - DistributeSingleNode(aElement); + if (!MaybeReassignElement(aElement, aOldValue)) { + return; + } + + if (!aElement->IsInComposedDoc()) { + return; + } + + auto* shell = OwnerDoc()->GetShell(); + if (!shell) { + return; + } + + //XXX optimize this! + shell->DestroyFramesForAndRestyle(aElement); } void @@ -629,29 +574,10 @@ ShadowRoot::ContentAppended(nsIDocument* aDocument, nsIContent* aFirstNewContent, int32_t aNewIndexInContainer) { - if (mInsertionPointChanged) { - DistributeAllNodes(); - mInsertionPointChanged = false; - return; - } - - // Watch for new nodes added to the pool because the node - // may need to be added to an insertion point. - nsIContent* currentChild = aFirstNewContent; - while (currentChild) { - // Add insertion point to destination insertion points of fallback content. - if (nsContentUtils::IsContentInsertionPoint(aContainer)) { - HTMLContentElement* content = HTMLContentElement::FromContent(aContainer); - if (content->MatchedNodes().IsEmpty()) { - currentChild->DestInsertionPoints().AppendElement(aContainer); - } - } - - if (IsPooledNode(currentChild, aContainer, mPoolHost)) { - DistributeSingleNode(currentChild); - } - - currentChild = currentChild->GetNextSibling(); + for (nsIContent* content = aFirstNewContent; + content; + content = content->GetNextSibling()) { + ContentInserted(aDocument, aContainer, aFirstNewContent, aNewIndexInContainer); } } @@ -661,24 +587,30 @@ ShadowRoot::ContentInserted(nsIDocument* aDocument, nsIContent* aChild, int32_t aIndexInContainer) { - if (mInsertionPointChanged) { - DistributeAllNodes(); - mInsertionPointChanged = false; + // Check to ensure that the content is in the same anonymous tree + // as the container because anonymous content may report its container + // as the host but it may not be in the host's child list. + if (!nsContentUtils::IsInSameAnonymousTree(aContainer, aChild)) { return; } - // Watch for new nodes added to the pool because the node - // may need to be added to an insertion point. - if (IsPooledNode(aChild, aContainer, mPoolHost)) { - // Add insertion point to destination insertion points of fallback content. - if (nsContentUtils::IsContentInsertionPoint(aContainer)) { - HTMLContentElement* content = HTMLContentElement::FromContent(aContainer); - if (content->MatchedNodes().IsEmpty()) { - aChild->DestInsertionPoints().AppendElement(aContainer); - } + if (!aChild->IsSlotable()) { + return; + } + + if (aContainer && aContainer == GetHost()) { + if (const HTMLSlotElement* slot = AssignSlotFor(aChild)) { + slot->EnqueueSlotChangeEvent(); } + return; + } - DistributeSingleNode(aChild); + // If parent's root is a shadow root, and parent is a slot whose assigned + // nodes is the empty list, then run signal a slot change for parent. + HTMLSlotElement* slot = HTMLSlotElement::FromContentOrNull(aContainer); + if (slot && slot->GetContainingShadow() == this && + slot->AssignedNodes().IsEmpty()) { + slot->EnqueueSlotChangeEvent(); } } @@ -689,25 +621,32 @@ ShadowRoot::ContentRemoved(nsIDocument* aDocument, int32_t aIndexInContainer, nsIContent* aPreviousSibling) { - if (mInsertionPointChanged) { - DistributeAllNodes(); - mInsertionPointChanged = false; + // Check to ensure that the content is in the same anonymous tree + // as the container because anonymous content may report its container + // as the host but it may not be in the host's child list. + if (!nsContentUtils::IsInSameAnonymousTree(aContainer, aChild)) { + return; + } + + if (!aChild->IsSlotable()) { return; } - // Clear destination insertion points for removed - // fallback content. - if (nsContentUtils::IsContentInsertionPoint(aContainer)) { - HTMLContentElement* content = HTMLContentElement::FromContent(aContainer); - if (content->MatchedNodes().IsEmpty()) { - aChild->DestInsertionPoints().Clear(); + if (aContainer && aContainer == GetHost()) { + nsAutoString slotName; + aChild->GetAttr(kNameSpaceID_None, nsGkAtoms::slot, slotName); + if (const HTMLSlotElement* slot = UnassignSlotFor(aChild, slotName)) { + slot->EnqueueSlotChangeEvent(); } + return; } - // Watch for node that is removed from the pool because - // it may need to be removed from an insertion point. - if (IsPooledNode(aChild, aContainer, mPoolHost)) { - RemoveDistributedNode(aChild); + // If parent's root is a shadow root, and parent is a slot whose assigned + // nodes is the empty list, then run signal a slot change for parent. + HTMLSlotElement* slot = HTMLSlotElement::FromContentOrNull(aContainer); + if (slot && slot->GetContainingShadow() == this && + slot->AssignedNodes().IsEmpty()) { + slot->EnqueueSlotChangeEvent(); } } @@ -717,49 +656,3 @@ ShadowRoot::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const *aResult = nullptr; return NS_ERROR_DOM_DATA_CLONE_ERR; } - -void -ShadowRoot::DestroyContent() -{ - if (mOlderShadow) { - mOlderShadow->DestroyContent(); - } - DocumentFragment::DestroyContent(); -} - -NS_IMPL_CYCLE_COLLECTION_INHERITED(ShadowRootStyleSheetList, StyleSheetList, - mShadowRoot) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ShadowRootStyleSheetList) -NS_INTERFACE_MAP_END_INHERITING(StyleSheetList) - -NS_IMPL_ADDREF_INHERITED(ShadowRootStyleSheetList, StyleSheetList) -NS_IMPL_RELEASE_INHERITED(ShadowRootStyleSheetList, StyleSheetList) - -ShadowRootStyleSheetList::ShadowRootStyleSheetList(ShadowRoot* aShadowRoot) - : mShadowRoot(aShadowRoot) -{ - MOZ_COUNT_CTOR(ShadowRootStyleSheetList); -} - -ShadowRootStyleSheetList::~ShadowRootStyleSheetList() -{ - MOZ_COUNT_DTOR(ShadowRootStyleSheetList); -} - -StyleSheet* -ShadowRootStyleSheetList::IndexedGetter(uint32_t aIndex, bool& aFound) -{ - aFound = aIndex < mShadowRoot->mProtoBinding->SheetCount(); - if (!aFound) { - return nullptr; - } - return mShadowRoot->mProtoBinding->StyleSheetAt(aIndex); -} - -uint32_t -ShadowRootStyleSheetList::Length() -{ - return mShadowRoot->mProtoBinding->SheetCount(); -} - diff --git a/dom/base/ShadowRoot.h b/dom/base/ShadowRoot.h index f84078134..a24c8138e 100644 --- a/dom/base/ShadowRoot.h +++ b/dom/base/ShadowRoot.h @@ -8,8 +8,7 @@ #define mozilla_dom_shadowroot_h__ #include "mozilla/dom/DocumentFragment.h" -#include "mozilla/dom/StyleSheetList.h" -#include "mozilla/StyleSheet.h" +#include "mozilla/dom/StyleScope.h" #include "nsCOMPtr.h" #include "nsCycleCollectionParticipant.h" #include "nsIContentInlines.h" @@ -21,17 +20,17 @@ class nsIContent; class nsXBLPrototypeBinding; namespace mozilla { + +class EventChainPreVisitor; + namespace dom { class Element; -class HTMLContentElement; -class HTMLShadowElement; -class ShadowRootStyleSheetList; class ShadowRoot final : public DocumentFragment, + public StyleScope, public nsStubMutationObserver { - friend class ShadowRootStyleSheetList; public: NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ShadowRoot, DocumentFragment) @@ -42,76 +41,86 @@ public: NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED - ShadowRoot(nsIContent* aContent, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, + ShadowRoot(Element* aElement, bool aClosed, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, nsXBLPrototypeBinding* aProtoBinding); + // Shadow DOM v1 + Element* Host(); + ShadowRootMode Mode() const + { + return mMode; + } + bool IsClosed() const + { + return mMode == ShadowRootMode::Closed; + } + + // StyleScope. + nsINode& AsNode() final + { + return *this; + } + + // [deprecated] Shadow DOM v0 void AddToIdTable(Element* aElement, nsIAtom* aId); void RemoveFromIdTable(Element* aElement, nsIAtom* aId); void InsertSheet(StyleSheet* aSheet, nsIContent* aLinkingContent); void RemoveSheet(StyleSheet* aSheet); bool ApplyAuthorStyles(); void SetApplyAuthorStyles(bool aApplyAuthorStyles); - StyleSheetList* StyleSheets(); - HTMLShadowElement* GetShadowElement() { return mShadowElement; } + StyleSheetList* StyleSheets() + { + return &StyleScope::EnsureDOMStyleSheets(); + } /** - * Sets the current shadow insertion point where the older - * ShadowRoot will be projected. + * Distributes all the explicit children of the pool host to the content + * insertion points in this ShadowRoot. */ - void SetShadowElement(HTMLShadowElement* aShadowElement); + void DistributeAllNodes(); +private: /** - * Change the node that populates the distribution pool with - * its children. This is distinct from the ShadowRoot host described - * in the specifications. The ShadowRoot host is the element - * which created this ShadowRoot and does not change. The pool host - * is the same as the ShadowRoot host if this is the youngest - * ShadowRoot. If this is an older ShadowRoot, the pool host is - * the <shadow> element in the younger ShadowRoot (if it exists). + * Try to reassign an element to a slot and returns whether the assignment + * changed. */ - void ChangePoolHost(nsIContent* aNewHost); + bool MaybeReassignElement(Element* aElement, const nsAttrValue* aOldValue); /** - * Distributes a single explicit child of the pool host to the content - * insertion points in this ShadowRoot. + * Try to assign aContent to a slot in the shadow tree, returns the assigned + * slot if found. */ - void DistributeSingleNode(nsIContent* aContent); + const HTMLSlotElement* AssignSlotFor(nsIContent* aContent); /** - * Removes a single explicit child of the pool host from the content - * insertion points in this ShadowRoot. + * Unassign aContent from the assigned slot in the shadow tree, returns the + * assigned slot if found. + * + * Note: slot attribute of aContent may have changed already, so pass slot + * name explicity here. */ - void RemoveDistributedNode(nsIContent* aContent); + const HTMLSlotElement* UnassignSlotFor(nsIContent* aContent, + const nsAString& aSlotName); /** - * Distributes all the explicit children of the pool host to the content - * insertion points in this ShadowRoot. + * Called when we redistribute content after insertion points have changed. */ - void DistributeAllNodes(); + void DistributionChanged(); - void AddInsertionPoint(HTMLContentElement* aInsertionPoint); - void RemoveInsertionPoint(HTMLContentElement* aInsertionPoint); + bool IsPooledNode(nsIContent* aChild) const; + +public: + void AddSlot(HTMLSlotElement* aSlot); + void RemoveSlot(HTMLSlotElement* aSlot); - void SetYoungerShadow(ShadowRoot* aYoungerShadow); - ShadowRoot* GetYoungerShadowRoot() { return mYoungerShadow; } void SetInsertionPointChanged() { mInsertionPointChanged = true; } void SetAssociatedBinding(nsXBLBinding* aBinding) { mAssociatedBinding = aBinding; } - nsISupports* GetParentObject() const { return mPoolHost; } - - nsIContent* GetPoolHost() { return mPoolHost; } - nsTArray<HTMLShadowElement*>& ShadowDescendants() { return mShadowDescendants; } - JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; - static bool IsPooledNode(nsIContent* aChild, nsIContent* aContainer, - nsIContent* aHost); static ShadowRoot* FromNode(nsINode* aNode); - static bool IsShadowInsertionPoint(nsIContent* aContent); - - static void RemoveDestInsertionPoint(nsIContent* aInsertionPoint, - nsTArray<nsIContent*>& aDestInsertionPoints); // WebIDL methods. Element* GetElementById(const nsAString& aElementId); @@ -124,8 +133,6 @@ public: GetElementsByClassName(const nsAString& aClasses); void GetInnerHTML(nsAString& aInnerHTML); void SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError); - Element* Host(); - ShadowRoot* GetOlderShadowRoot() { return mOlderShadow; } void StyleSheetChanged(); bool IsComposedDocParticipant() { return mIsComposedDocParticipant; } @@ -134,24 +141,17 @@ public: mIsComposedDocParticipant = aIsComposedDocParticipant; } - virtual void DestroyContent() override; + nsresult GetEventTargetParent(EventChainPreVisitor& aVisitor) override; + protected: virtual ~ShadowRoot(); - // The pool host is the parent of the nodes that will be distributed - // into the insertion points in this ShadowRoot. See |ChangeShadowRoot|. - nsCOMPtr<nsIContent> mPoolHost; - - // An array of content insertion points that are a descendant of the ShadowRoot - // sorted in tree order. Insertion points are responsible for notifying - // the ShadowRoot when they are removed or added as a descendant. The insertion - // points are kept alive by the parent node, thus weak references are held - // by the array. - nsTArray<HTMLContentElement*> mInsertionPoints; + ShadowRootMode mMode; - // An array of the <shadow> elements that are descendant of the ShadowRoot - // sorted in tree order. Only the first may be a shadow insertion point. - nsTArray<HTMLShadowElement*> mShadowDescendants; + // Map from name of slot to an array of all slots in the shadow DOM with with + // the given name. The slots are stored as a weak pointer because the elements + // are in the shadow tree and should be kept alive by its parent. + nsClassHashtable<nsStringHashKey, nsTArray<mozilla::dom::HTMLSlotElement*>> mSlotMap; nsTHashtable<nsIdentifierMapEntry> mIdentifierMap; nsXBLPrototypeBinding* mProtoBinding; @@ -161,19 +161,6 @@ protected: // owns |mProtoBinding|. RefPtr<nsXBLBinding> mAssociatedBinding; - RefPtr<ShadowRootStyleSheetList> mStyleSheetList; - - // The current shadow insertion point of this ShadowRoot. - HTMLShadowElement* mShadowElement; - - // The ShadowRoot that was created by the host element before - // this ShadowRoot was created. - RefPtr<ShadowRoot> mOlderShadow; - - // The ShadowRoot that was created by the host element after - // this ShadowRoot was created. - RefPtr<ShadowRoot> mYoungerShadow; - // A boolean that indicates that an insertion point was added or removed // from this ShadowRoot and that the nodes need to be redistributed into // the insertion points. After this flag is set, nodes will be distributed @@ -189,28 +176,6 @@ protected: nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; }; -class ShadowRootStyleSheetList : public StyleSheetList -{ -public: - explicit ShadowRootStyleSheetList(ShadowRoot* aShadowRoot); - - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ShadowRootStyleSheetList, StyleSheetList) - - virtual nsINode* GetParentObject() const override - { - return mShadowRoot; - } - - uint32_t Length() override; - StyleSheet* IndexedGetter(uint32_t aIndex, bool& aFound) override; - -protected: - virtual ~ShadowRootStyleSheetList(); - - RefPtr<ShadowRoot> mShadowRoot; -}; - } // namespace dom } // namespace mozilla diff --git a/dom/base/StyleScope.cpp b/dom/base/StyleScope.cpp new file mode 100644 index 000000000..6c708a8ba --- /dev/null +++ b/dom/base/StyleScope.cpp @@ -0,0 +1,27 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "StyleScope.h" +#include "mozilla/dom/StyleSheetList.h" + +namespace mozilla { +namespace dom { + +StyleScope::~StyleScope() +{ +} + +StyleSheetList& +StyleScope::EnsureDOMStyleSheets() +{ + if (!mDOMStyleSheets) { + mDOMStyleSheets = new StyleSheetList(*this); + } + return *mDOMStyleSheets; +} + +} +} diff --git a/dom/base/StyleScope.h b/dom/base/StyleScope.h new file mode 100644 index 000000000..c8957b069 --- /dev/null +++ b/dom/base/StyleScope.h @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_StyleScope_h__ +#define mozilla_dom_StyleScope_h__ + +#include "nsTArray.h" + +class nsINode; + +namespace mozilla { +class StyleSheet; + +namespace dom { + +class StyleSheetList; + +/** + * A class meant to be shared by ShadowRoot and Document, that holds a list of + * stylesheets. + * + * TODO(emilio, bug 1418159): In the future this should hold most of the + * relevant style state, this should allow us to fix bug 548397. + */ +class StyleScope +{ +public: + virtual nsINode& AsNode() = 0; + + const nsINode& AsNode() const + { + return const_cast<StyleScope&>(*this).AsNode(); + } + + StyleSheet* SheetAt(size_t aIndex) const + { + return mStyleSheets.SafeElementAt(aIndex); + } + + size_t SheetCount() const + { + return mStyleSheets.Length(); + } + + int32_t IndexOfSheet(const StyleSheet& aSheet) const + { + return mStyleSheets.IndexOf(&aSheet); + } + + void InsertSheetAt(size_t aIndex, StyleSheet& aSheet) + { + mStyleSheets.InsertElementAt(aIndex, &aSheet); + } + + void RemoveSheet(StyleSheet& aSheet) + { + mStyleSheets.RemoveElement(&aSheet); + } + + void AppendStyleSheet(StyleSheet& aSheet) + { + mStyleSheets.AppendElement(&aSheet); + } + + StyleSheetList& EnsureDOMStyleSheets(); + + ~StyleScope(); + +protected: + nsTArray<RefPtr<mozilla::StyleSheet>> mStyleSheets; + RefPtr<mozilla::dom::StyleSheetList> mDOMStyleSheets; +}; + +} + +} + +#endif diff --git a/dom/base/StyleSheetList.cpp b/dom/base/StyleSheetList.cpp index 7274b5ea5..42db3179d 100644 --- a/dom/base/StyleSheetList.cpp +++ b/dom/base/StyleSheetList.cpp @@ -8,6 +8,7 @@ #include "mozilla/CSSStyleSheet.h" #include "mozilla/dom/StyleSheetListBinding.h" +#include "nsStubDocumentObserver.h" namespace mozilla { namespace dom { @@ -17,7 +18,8 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(StyleSheetList) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(StyleSheetList) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_INTERFACE_MAP_ENTRY(nsIDOMStyleSheetList) - NS_INTERFACE_MAP_ENTRY(nsISupports) + NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMStyleSheetList) NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(StyleSheetList) @@ -43,5 +45,24 @@ StyleSheetList::SlowItem(uint32_t aIndex, nsIDOMStyleSheet** aItem) return NS_OK; } +void +StyleSheetList::NodeWillBeDestroyed(const nsINode* aNode) +{ + mStyleScope = nullptr; +} + +StyleSheetList::StyleSheetList(StyleScope& aScope) + : mStyleScope(&aScope) +{ + mStyleScope->AsNode().AddMutationObserver(this); +} + +StyleSheetList::~StyleSheetList() +{ + if (mStyleScope) { + mStyleScope->AsNode().RemoveMutationObserver(this); + } +} + } // namespace dom } // namespace mozilla diff --git a/dom/base/StyleSheetList.h b/dom/base/StyleSheetList.h index dfedc2214..ea5c33a98 100644 --- a/dom/base/StyleSheetList.h +++ b/dom/base/StyleSheetList.h @@ -7,8 +7,10 @@ #ifndef mozilla_dom_StyleSheetList_h #define mozilla_dom_StyleSheetList_h +#include "mozilla/dom/StyleScope.h" #include "nsIDOMStyleSheetList.h" #include "nsWrapperCache.h" +#include "nsStubDocumentObserver.h" class nsINode; @@ -17,28 +19,54 @@ class StyleSheet; namespace dom { -class StyleSheetList : public nsIDOMStyleSheetList - , public nsWrapperCache +class StyleSheetList final : public nsIDOMStyleSheetList + , public nsWrapperCache + , public nsStubDocumentObserver { public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(StyleSheetList) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(StyleSheetList, nsIDOMStyleSheetList) + NS_DECL_NSIDOMSTYLESHEETLIST + NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED + + explicit StyleSheetList(StyleScope& aScope); + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override final; - virtual nsINode* GetParentObject() const = 0; + nsINode* GetParentObject() const + { + return mStyleScope ? &mStyleScope->AsNode() : nullptr; + } - virtual uint32_t Length() = 0; - virtual StyleSheet* IndexedGetter(uint32_t aIndex, bool& aFound) = 0; - StyleSheet* Item(uint32_t aIndex) + uint32_t Length() const + { + return mStyleScope ? mStyleScope->SheetCount() : 0; + } + + StyleSheet* IndexedGetter(uint32_t aIndex, bool& aFound) const + { + if (!mStyleScope) { + aFound = false; + return nullptr; + } + + StyleSheet* sheet = mStyleScope->SheetAt(aIndex); + aFound = !!sheet; + return sheet; + } + + StyleSheet* Item(uint32_t aIndex) const { bool dummy = false; return IndexedGetter(aIndex, dummy); } protected: - virtual ~StyleSheetList() {} + virtual ~StyleSheetList(); + + StyleScope* mStyleScope; // Weak, cleared on "NodeWillBeDestroyed". }; } // namespace dom diff --git a/dom/base/TabGroup.h b/dom/base/TabGroup.h index f9fa9eec5..c436ce98d 100644 --- a/dom/base/TabGroup.h +++ b/dom/base/TabGroup.h @@ -7,10 +7,12 @@ #ifndef TabGroup_h #define TabGroup_h +#include "nsIDocument.h" #include "nsISupports.h" #include "nsISupportsImpl.h" #include "nsIPrincipal.h" #include "nsTHashtable.h" +#include "nsHashKeys.h" #include "nsString.h" #include "mozilla/RefPtr.h" diff --git a/dom/base/ThirdPartyUtil.cpp b/dom/base/ThirdPartyUtil.cpp index 61d97f634..37d326dbc 100644 --- a/dom/base/ThirdPartyUtil.cpp +++ b/dom/base/ThirdPartyUtil.cpp @@ -10,6 +10,7 @@ #include "nsIChannel.h" #include "nsIServiceManager.h" #include "nsIHttpChannelInternal.h" +#include "nsPIDOMWindow.h" #include "nsIDOMWindow.h" #include "nsILoadContext.h" #include "nsIPrincipal.h" diff --git a/dom/base/TimeoutHandler.cpp b/dom/base/TimeoutHandler.cpp index 78c3f16dd..f34275840 100644 --- a/dom/base/TimeoutHandler.cpp +++ b/dom/base/TimeoutHandler.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "TimeoutHandler.h" +#include "nsJSUtils.h" namespace mozilla { namespace dom { diff --git a/dom/base/TimeoutHandler.h b/dom/base/TimeoutHandler.h index cb0a0ce94..a80dc2995 100644 --- a/dom/base/TimeoutHandler.h +++ b/dom/base/TimeoutHandler.h @@ -7,9 +7,11 @@ #ifndef mozilla_dom_timeout_handler_h #define mozilla_dom_timeout_handler_h +#include "nsCycleCollectionParticipant.h" // NS_DECL_CYCLE_* #include "nsCOMPtr.h" #include "nsISupports.h" #include "nsITimeoutHandler.h" +#include "nsString.h" namespace mozilla { namespace dom { diff --git a/dom/base/TimerClamping.h b/dom/base/TimerClamping.h index 2ffd6add5..2bd1f019c 100755 --- a/dom/base/TimerClamping.h +++ b/dom/base/TimerClamping.h @@ -7,6 +7,8 @@ #ifndef TimerClamping_h___ #define TimerClamping_h___ +#include <math.h> + namespace mozilla { class TimerClamping diff --git a/dom/base/crashtests/1419799.html b/dom/base/crashtests/1419799.html new file mode 100644 index 000000000..b6d34a1a9 --- /dev/null +++ b/dom/base/crashtests/1419799.html @@ -0,0 +1,17 @@ +<html> + <head> + <script> + try { o1 = document.createElement('textarea') } catch(e) { } + try { o2 = document.createElement('div') } catch(e) { } + try { o3 = document.createElement('map') } catch(e) { } + try { document.documentElement.appendChild(o2) } catch(e) { } + try { o2.appendChild(o1) } catch(e) { } + try { document.documentElement.getClientRects() } catch(e) { } + try { o4 = o2.attachShadow({ mode: "open" }); } catch(e) { } + try { o1.appendChild(o3) } catch(e) { } + try { o5 = o3.parentElement } catch(e) { } + try { o3.outerHTML = "\n" } catch(e) { } + try { o4.prepend("", o5, "") } catch(e) { } + </script> + </head> +</html> diff --git a/dom/base/crashtests/1422931.html b/dom/base/crashtests/1422931.html new file mode 100644 index 000000000..9f09f41ef --- /dev/null +++ b/dom/base/crashtests/1422931.html @@ -0,0 +1,6 @@ +<!DOCTYPE html> +<html> +<body> +<!-- Testing slot element with "dom.webcomponents.enabled" set to false --> +<slot><div></div></slot> +</html> diff --git a/dom/base/crashtests/crashtests.list b/dom/base/crashtests/crashtests.list index 0fb597b30..40d358b38 100644 --- a/dom/base/crashtests/crashtests.list +++ b/dom/base/crashtests/crashtests.list @@ -193,7 +193,7 @@ load 930250.html load 942979.html load 973401.html load 978646.html -pref(dom.webcomponents.enabled,true) load 1024428-1.html +pref(dom.webcomponents.enabled,true) load 1024428-1.html # bug 1340009 load 1026714.html pref(dom.webcomponents.enabled,true) load 1027461-1.html pref(dom.webcomponents.enabled,true) load 1029710.html @@ -209,4 +209,6 @@ load 1230422.html load 1251361.html load 1304437.html pref(clipboard.autocopy,true) load 1385272-1.html -pref(dom.webcomponents.customelements.enabled,true) load 1341693.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 diff --git a/dom/base/moz.build b/dom/base/moz.build index 75ddefded..5acb49d4e 100755 --- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -213,6 +213,7 @@ EXPORTS.mozilla.dom += [ 'SimpleTreeIterator.h', 'StructuredCloneHolder.h', 'StructuredCloneTags.h', + 'StyleScope.h', 'StyleSheetList.h', 'SubtleCrypto.h', 'TabGroup.h', @@ -225,7 +226,7 @@ EXPORTS.mozilla.dom += [ 'WindowOrientationObserver.h', ] -UNIFIED_SOURCES += [ +SOURCES += [ 'AnonymousContent.cpp', 'Attr.cpp', 'BarProps.cpp', @@ -359,6 +360,7 @@ UNIFIED_SOURCES += [ 'ScriptSettings.cpp', 'ShadowRoot.cpp', 'StructuredCloneHolder.cpp', + 'StyleScope.cpp', 'StyleSheetList.cpp', 'SubtleCrypto.cpp', 'TabGroup.cpp', @@ -376,7 +378,7 @@ UNIFIED_SOURCES += [ ] if CONFIG['MOZ_WEBRTC']: - UNIFIED_SOURCES += [ + SOURCES += [ 'nsDOMDataChannel.cpp', ] diff --git a/dom/base/nsAttrAndChildArray.cpp b/dom/base/nsAttrAndChildArray.cpp index 9fd27262b..f8e4c3f18 100644 --- a/dom/base/nsAttrAndChildArray.cpp +++ b/dom/base/nsAttrAndChildArray.cpp @@ -382,12 +382,15 @@ nsAttrAndChildArray::AttrAt(uint32_t aPos) const } nsresult -nsAttrAndChildArray::SetAndSwapAttr(nsIAtom* aLocalName, nsAttrValue& aValue) +nsAttrAndChildArray::SetAndSwapAttr(nsIAtom* aLocalName, nsAttrValue& aValue, + bool* aHadValue) { + *aHadValue = false; uint32_t i, slotCount = AttrSlotCount(); for (i = 0; i < slotCount && AttrSlotIsTaken(i); ++i) { if (ATTRS(mImpl)[i].mName.Equals(aLocalName)) { ATTRS(mImpl)[i].mValue.SwapValueWith(aValue); + *aHadValue = true; return NS_OK; } } @@ -407,21 +410,22 @@ nsAttrAndChildArray::SetAndSwapAttr(nsIAtom* aLocalName, nsAttrValue& aValue) } nsresult -nsAttrAndChildArray::SetAndSwapAttr(mozilla::dom::NodeInfo* aName, nsAttrValue& aValue) +nsAttrAndChildArray::SetAndSwapAttr(mozilla::dom::NodeInfo* aName, + nsAttrValue& aValue, bool* aHadValue) { int32_t namespaceID = aName->NamespaceID(); nsIAtom* localName = aName->NameAtom(); if (namespaceID == kNameSpaceID_None) { - return SetAndSwapAttr(localName, aValue); + return SetAndSwapAttr(localName, aValue, aHadValue); } + *aHadValue = false; uint32_t i, slotCount = AttrSlotCount(); for (i = 0; i < slotCount && AttrSlotIsTaken(i); ++i) { if (ATTRS(mImpl)[i].mName.Equals(localName, namespaceID)) { ATTRS(mImpl)[i].mName.SetTo(aName); - ATTRS(mImpl)[i].mValue.Reset(); ATTRS(mImpl)[i].mValue.SwapValueWith(aValue); - + *aHadValue = true; return NS_OK; } } @@ -576,10 +580,11 @@ nsAttrAndChildArray::IndexOfAttr(nsIAtom* aLocalName, int32_t aNamespaceID) cons } nsresult -nsAttrAndChildArray::SetAndTakeMappedAttr(nsIAtom* aLocalName, +nsAttrAndChildArray::SetAndSwapMappedAttr(nsIAtom* aLocalName, nsAttrValue& aValue, nsMappedAttributeElement* aContent, - nsHTMLStyleSheet* aSheet) + nsHTMLStyleSheet* aSheet, + bool* aHadValue) { bool willAdd = true; if (mImpl && mImpl->mMappedAttrs) { @@ -589,7 +594,7 @@ nsAttrAndChildArray::SetAndTakeMappedAttr(nsIAtom* aLocalName, RefPtr<nsMappedAttributes> mapped = GetModifiableMapped(aContent, aSheet, willAdd); - mapped->SetAndTakeAttr(aLocalName, aValue); + mapped->SetAndSwapAttr(aLocalName, aValue, aHadValue); return MakeMappedUnique(mapped); } @@ -714,10 +719,19 @@ nsAttrAndChildArray::MappedAttrCount() const return mImpl && mImpl->mMappedAttrs ? (uint32_t)mImpl->mMappedAttrs->Count() : 0; } +nsresult +nsAttrAndChildArray::ForceMapped(nsMappedAttributeElement* aContent, nsIDocument* aDocument) +{ + nsHTMLStyleSheet* sheet = aDocument->GetAttributeStyleSheet(); + RefPtr<nsMappedAttributes> mapped = GetModifiableMapped(aContent, sheet, false, 0); + return MakeMappedUnique(mapped); +} + nsMappedAttributes* nsAttrAndChildArray::GetModifiableMapped(nsMappedAttributeElement* aContent, nsHTMLStyleSheet* aSheet, - bool aWillAddAttr) + bool aWillAddAttr, + int32_t aAttrCount) { if (mImpl && mImpl->mMappedAttrs) { return mImpl->mMappedAttrs->Clone(aWillAddAttr); @@ -727,7 +741,7 @@ nsAttrAndChildArray::GetModifiableMapped(nsMappedAttributeElement* aContent, nsMapRuleToAttributesFunc mapRuleFunc = aContent->GetAttributeMappingFunction(); - return new nsMappedAttributes(aSheet, mapRuleFunc); + return new (aAttrCount) nsMappedAttributes(aSheet, mapRuleFunc); } nsresult diff --git a/dom/base/nsAttrAndChildArray.h b/dom/base/nsAttrAndChildArray.h index f34370c43..0ce51c52e 100644 --- a/dom/base/nsAttrAndChildArray.h +++ b/dom/base/nsAttrAndChildArray.h @@ -91,8 +91,13 @@ public: nsCaseTreatment aCaseSensitive) const; const nsAttrValue* AttrAt(uint32_t aPos) const; // SetAndSwapAttr swaps the current attribute value with aValue. - nsresult SetAndSwapAttr(nsIAtom* aLocalName, nsAttrValue& aValue); - nsresult SetAndSwapAttr(mozilla::dom::NodeInfo* aName, nsAttrValue& aValue); + // If the attribute was unset, an empty value will be swapped into aValue + // and aHadValue will be set to false. Otherwise, aHadValue will be set to + // true. + nsresult SetAndSwapAttr(nsIAtom* aLocalName, nsAttrValue& aValue, + bool* aHadValue); + nsresult SetAndSwapAttr(mozilla::dom::NodeInfo* aName, nsAttrValue& aValue, + bool* aHadValue); // Remove the attr at position aPos. The value of the attr is placed in // aValue; any value that was already in aValue is destroyed. @@ -110,9 +115,14 @@ public: const nsAttrName* GetExistingAttrNameFromQName(const nsAString& aName) const; int32_t IndexOfAttr(nsIAtom* aLocalName, int32_t aNamespaceID = kNameSpaceID_None) const; - nsresult SetAndTakeMappedAttr(nsIAtom* aLocalName, nsAttrValue& aValue, + // SetAndSwapMappedAttr swaps the current attribute value with aValue. + // If the attribute was unset, an empty value will be swapped into aValue + // and aHadValue will be set to false. Otherwise, aHadValue will be set to + // true. + nsresult SetAndSwapMappedAttr(nsIAtom* aLocalName, nsAttrValue& aValue, nsMappedAttributeElement* aContent, - nsHTMLStyleSheet* aSheet); + nsHTMLStyleSheet* aSheet, + bool* aHadValue); nsresult SetMappedAttrStyleSheet(nsHTMLStyleSheet* aSheet) { if (!mImpl || !mImpl->mMappedAttrs) { return NS_OK; @@ -135,6 +145,9 @@ public: return MappedAttrCount(); } + // Force this to have mapped attributes, even if those attributes are empty. + nsresult ForceMapped(nsMappedAttributeElement* aContent, nsIDocument* aDocument); + private: nsAttrAndChildArray(const nsAttrAndChildArray& aOther) = delete; nsAttrAndChildArray& operator=(const nsAttrAndChildArray& aOther) = delete; @@ -148,7 +161,8 @@ private: nsMappedAttributes* GetModifiableMapped(nsMappedAttributeElement* aContent, nsHTMLStyleSheet* aSheet, - bool aWillAddAttr); + bool aWillAddAttr, + int32_t aAttrCount = 1); nsresult MakeMappedUnique(nsMappedAttributes* aAttributes); uint32_t AttrSlotsSize() const diff --git a/dom/base/nsAttrValueOrString.h b/dom/base/nsAttrValueOrString.h index df0dac17d..1a62a8428 100644 --- a/dom/base/nsAttrValueOrString.h +++ b/dom/base/nsAttrValueOrString.h @@ -49,20 +49,13 @@ public: , mCheapString(nullptr) { } - void TakeParsedValue(nsAttrValue& aValue) + void ResetToAttrValue(const nsAttrValue& aValue) { - mStoredAttrValue.SwapValueWith(aValue); - mAttrValue = &mStoredAttrValue; + mAttrValue = &aValue; mStringPtr = nullptr; + // No need to touch mCheapString here. If we need to use it, we will reset + // it to the rigthe value anyway. } - /** - * If TakeParsedValue has been called, returns the value that it set. - */ - nsAttrValue* GetStoredAttrValue() - { - return mAttrValue == &mStoredAttrValue ? &mStoredAttrValue : nullptr; - } - const nsAttrValue* GetAttrValue() { return mAttrValue; } /** * Returns a reference to the string value of the contents of this object. @@ -85,11 +78,24 @@ public: return aOther.EqualsAsStrings(*mAttrValue); } + /* + * Returns true if the value stored is empty + */ + bool IsEmpty() const + { + if (mStringPtr) { + return mStringPtr->IsEmpty(); + } + if (mAttrValue) { + return mAttrValue->IsEmptyString(); + } + return true; + } + protected: const nsAttrValue* mAttrValue; mutable const nsAString* mStringPtr; mutable nsCheapString mCheapString; - nsAttrValue mStoredAttrValue; }; #endif // nsAttrValueOrString_h___ diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index 4f7b71b19..c0d81a41e 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -44,9 +44,8 @@ #include "mozilla/dom/Element.h" #include "mozilla/dom/FileSystemSecurity.h" #include "mozilla/dom/HTMLMediaElement.h" +#include "mozilla/dom/HTMLSlotElement.h" #include "mozilla/dom/HTMLTemplateElement.h" -#include "mozilla/dom/HTMLContentElement.h" -#include "mozilla/dom/HTMLShadowElement.h" #include "mozilla/dom/ipc/BlobChild.h" #include "mozilla/dom/ipc/BlobParent.h" #include "mozilla/dom/Promise.h" @@ -102,7 +101,9 @@ #include "nsHostObjectProtocolHandler.h" #include "nsHtml5Module.h" #include "nsHtml5StringParser.h" +#include "nsHTMLTags.h" #include "nsIAddonPolicyService.h" +#include "nsIAnonymousContentCreator.h" #include "nsIAsyncVerifyRedirectCallback.h" #include "nsICategoryManager.h" #include "nsIChannelEventSink.h" @@ -500,6 +501,8 @@ nsContentUtils::Init() return NS_OK; } + nsHTMLTags::AddRefTable(); + sNameSpaceManager = nsNameSpaceManager::GetInstance(); NS_ENSURE_TRUE(sNameSpaceManager, NS_ERROR_OUT_OF_MEMORY); @@ -590,7 +593,7 @@ nsContentUtils::Init() "dom.webcomponents.enabled", false); Preferences::AddBoolVarCache(&sIsCustomElementsEnabled, - "dom.webcomponents.customelements.enabled", false); + "dom.webcomponents.enabled", false); Preferences::AddBoolVarCache(&sEncodeDecodeURLHash, "dom.url.encode_decode_hash", false); @@ -1926,6 +1929,8 @@ nsContentUtils::Shutdown() { sInitialized = false; + nsHTMLTags::ReleaseTable(); + NS_IF_RELEASE(sContentPolicyService); sTriedToGetContentPolicy = false; uint32_t i; @@ -2390,6 +2395,9 @@ nsContentUtils::ComparePoints(nsINode* aParent1, int32_t aOffset1, bool* aDisconnected) { if (aParent1 == aParent2) { + // XXX This is odd. aOffset1 and/or aOffset2 may be -1, e.g., it's result + // of nsINode::IndexOf(), but this compares such invalid offset with + // valid offset. return aOffset1 < aOffset2 ? -1 : aOffset1 > aOffset2 ? 1 : 0; @@ -2440,10 +2448,14 @@ nsContentUtils::ComparePoints(nsINode* aParent1, int32_t aOffset1, if (!pos1) { nsINode* child2 = parents2.ElementAt(--pos2); + // XXX aOffset1 may be -1 as mentioned above. So, why does this return + // it's *before* of the valid DOM point? return aOffset1 <= parent->IndexOf(child2) ? -1 : 1; } nsINode* child1 = parents1.ElementAt(--pos1); + // XXX aOffset2 may be -1 as mentioned above. So, why does this return it's + // *after* of the valid DOM point? return parent->IndexOf(child1) < aOffset2 ? -1 : 1; } @@ -4943,17 +4955,7 @@ nsContentUtils::IsInSameAnonymousTree(const nsINode* aNode, return aContent->GetBindingParent() == nullptr; } - const nsIContent* nodeAsContent = static_cast<const nsIContent*>(aNode); - - // For nodes in a shadow tree, it is insufficient to simply compare - // the binding parent because a node may host multiple ShadowRoots, - // thus nodes in different shadow tree may have the same binding parent. - if (aNode->IsInShadowTree()) { - return nodeAsContent->GetContainingShadow() == - aContent->GetContainingShadow(); - } - - return nodeAsContent->GetBindingParent() == aContent->GetBindingParent(); + return aNode->AsContent()->GetBindingParent() == aContent->GetBindingParent(); } class AnonymousContentDestroyer : public Runnable { @@ -7015,25 +7017,11 @@ nsContentUtils::GetHTMLEditor(nsPresContext* aPresContext) return editor; } -bool -nsContentUtils::IsContentInsertionPoint(nsIContent* aContent) -{ - // Check if the content is a XBL insertion point. - if (aContent->IsActiveChildrenElement()) { - return true; - } - - // Check if the content is a web components content insertion point. - HTMLContentElement* contentElement = - HTMLContentElement::FromContent(aContent); - return contentElement && contentElement->IsInsertionPoint(); -} - // static bool nsContentUtils::HasDistributedChildren(nsIContent* aContent) { - if (!aContent) { + if (!aContent || !nsDocument::IsWebComponentsEnabled(aContent)) { return false; } @@ -7043,26 +7031,11 @@ nsContentUtils::HasDistributedChildren(nsIContent* aContent) return true; } - ShadowRoot* shadow = ShadowRoot::FromNode(aContent); - if (shadow) { - // Children of a shadow root are distributed to - // the shadow insertion point of the younger shadow root. - return shadow->GetYoungerShadowRoot(); - } - - HTMLShadowElement* shadowEl = HTMLShadowElement::FromContent(aContent); - if (shadowEl && shadowEl->IsInsertionPoint()) { - // Children of a shadow insertion points are distributed - // to the insertion points in the older shadow root. - return shadowEl->GetOlderShadowRoot(); - } - - HTMLContentElement* contentEl = HTMLContentElement::FromContent(aContent); - if (contentEl && contentEl->IsInsertionPoint()) { - // Children of a content insertion point are distributed to the - // content insertion point if the content insertion point does - // not match any nodes (fallback content). - return contentEl->MatchedNodes().IsEmpty(); + HTMLSlotElement* slotEl = HTMLSlotElement::FromContent(aContent); + if (slotEl && slotEl->GetContainingShadow()) { + // Children of a slot are rendered if the slot does not have any assigned + // nodes (fallback content). + return slotEl->AssignedNodes().IsEmpty(); } return false; @@ -9675,35 +9648,6 @@ nsContentUtils::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType, aDefinition); } -/* static */ void -nsContentUtils::GetCustomPrototype(nsIDocument* aDoc, - int32_t aNamespaceID, - nsIAtom* aAtom, - JS::MutableHandle<JSObject*> aPrototype) -{ - MOZ_ASSERT(aDoc); - - // To support imported document. - nsCOMPtr<nsIDocument> doc = aDoc->MasterDocument(); - - if (aNamespaceID != kNameSpaceID_XHTML || - !doc->GetDocShell()) { - return; - } - - nsCOMPtr<nsPIDOMWindowInner> window(doc->GetInnerWindow()); - if (!window) { - return; - } - - RefPtr<CustomElementRegistry> registry(window->CustomElements()); - if (!registry) { - return; - } - - return registry->GetCustomPrototype(aAtom, aPrototype); -} - /* static */ bool nsContentUtils::AttemptLargeAllocationLoad(nsIHttpChannel* aChannel) { @@ -9837,6 +9781,24 @@ nsContentUtils::AttemptLargeAllocationLoad(nsIHttpChannel* aChannel) return reloadSucceeded; } +/* static */ void +nsContentUtils::AppendDocumentLevelNativeAnonymousContentTo( + nsIDocument* aDocument, + nsTArray<nsIContent*>& aElements) +{ + MOZ_ASSERT(aDocument); + + // XXXheycam This probably needs to find the nsCanvasFrame's NAC too. + if (nsIPresShell* presShell = aDocument->GetShell()) { + if (nsIFrame* scrollFrame = presShell->GetRootScrollFrame()) { + nsIAnonymousContentCreator* creator = do_QueryFrame(scrollFrame); + MOZ_ASSERT(creator, + "scroll frame should always implement nsIAnonymousContentCreator"); + creator->AppendAnonymousContentTo(aElements, 0); + } + } +} + /* static */ bool nsContentUtils::IsLocalRefURL(const nsString& aString) { @@ -9852,3 +9814,16 @@ nsContentUtils::IsLocalRefURL(const nsString& aString) return false; } + +/* static */ Element* +nsContentUtils::GetClosestNonNativeAnonymousAncestor(Element* aElement) +{ + MOZ_ASSERT(aElement); + MOZ_ASSERT(aElement->IsNativeAnonymous()); + + Element* e = aElement; + while (e && e->IsNativeAnonymous()) { + e = e->GetParentElement(); + } + return e; +} diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index 64f7485cb..b58b0e0e3 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -339,6 +339,13 @@ public: * NOTE! If the two nodes aren't in the same connected subtree, * the result is 1, and the optional aDisconnected parameter * is set to true. + * + * XXX aOffset1 and aOffset2 should be uint32_t since valid offset value is + * between 0 - UINT32_MAX. However, these methods work even with + * negative offset values! E.g., when aOffset1 is -1 and aOffset is 0, + * these methods return -1. Some root callers depend on this behavior. + * On the other hand, nsINode can have ATTRCHILD_ARRAY_MAX_CHILD_COUN + * (0x3FFFFF) at most. Therefore, they can be int32_t for now. */ static int32_t ComparePoints(nsINode* aParent1, int32_t aOffset1, nsINode* aParent2, int32_t aOffset2, @@ -582,7 +589,7 @@ public: /** * Returns true if |aName| is a valid name to be registered via - * document.registerElement. + * customElements.define. */ static bool IsCustomElementName(nsIAtom* aName); @@ -2408,18 +2415,6 @@ public: static mozilla::LogModule* DOMDumpLog(); /** - * Returns whether a content is an insertion point for XBL - * bindings or web components ShadowRoot. In web components, - * this corresponds to a <content> element that participates - * in node distribution. In XBL this corresponds to an - * <xbl:children> element in anonymous content. - * - * @param aContent The content to test for being an insertion point. - */ - static bool IsContentInsertionPoint(nsIContent* aContent); - - - /** * Returns whether the children of the provided content are * nodes that are distributed to Shadow DOM insertion points. */ @@ -2735,14 +2730,19 @@ public: mozilla::dom::LifecycleAdoptedCallbackArgs* aAdoptedCallbackArgs = nullptr, mozilla::dom::CustomElementDefinition* aDefinition = nullptr); - static void GetCustomPrototype(nsIDocument* aDoc, - int32_t aNamespaceID, - nsIAtom* aAtom, - JS::MutableHandle<JSObject*> prototype); - static bool AttemptLargeAllocationLoad(nsIHttpChannel* aChannel); /** + * Appends all "document level" native anonymous content subtree roots for + * aDocument to aElements. Document level NAC subtrees are those created + * by ancestor frames of the document element's primary frame, such as + * the scrollbar elements created by the root scroll frame. + */ + static void AppendDocumentLevelNativeAnonymousContentTo( + nsIDocument* aDocument, + nsTArray<nsIContent*>& aElements); + + /** * Detect whether a string is a (CSS) local-url. * https://drafts.csswg.org/css-values/#local-urls */ @@ -2752,6 +2752,12 @@ public: static bool IsWebComponentsEnabled() { return sIsWebComponentsEnabled; } + /** + * Walks up the tree from aElement until it finds an element that is + * not native anonymous content. aElement must be NAC itself. + */ + static Element* GetClosestNonNativeAnonymousAncestor(Element* aElement); + static bool IsCustomElementsEnabled() { return sIsCustomElementsEnabled; } diff --git a/dom/base/nsDOMAttributeMap.cpp b/dom/base/nsDOMAttributeMap.cpp index 2a90df7e4..29a4a6349 100644 --- a/dom/base/nsDOMAttributeMap.cpp +++ b/dom/base/nsDOMAttributeMap.cpp @@ -524,3 +524,9 @@ nsDOMAttributeMap::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { return NamedNodeMapBinding::Wrap(aCx, this, aGivenProto); } + +DocGroup* +nsDOMAttributeMap::GetDocGroup() const +{ + return mContent ? mContent->OwnerDoc()->GetDocGroup() : nullptr; +} diff --git a/dom/base/nsDOMAttributeMap.h b/dom/base/nsDOMAttributeMap.h index 31eb701e3..d73d1c3b6 100644 --- a/dom/base/nsDOMAttributeMap.h +++ b/dom/base/nsDOMAttributeMap.h @@ -22,6 +22,11 @@ class nsIAtom; class nsIDocument; +namespace mozilla { +namespace dom { +class DocGroup; +} // namespace dom +} // namespace mozilla /** * Structure used as a key for caching Attrs in nsDOMAttributeMap's mAttributeCache. @@ -87,6 +92,7 @@ class nsDOMAttributeMap final : public nsIDOMMozNamedAttrMap { public: typedef mozilla::dom::Attr Attr; + typedef mozilla::dom::DocGroup DocGroup; typedef mozilla::dom::Element Element; typedef mozilla::ErrorResult ErrorResult; @@ -135,6 +141,7 @@ public: return mContent; } virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + DocGroup* GetDocGroup() const; // WebIDL Attr* GetNamedItem(const nsAString& aAttrName); diff --git a/dom/base/nsDOMMutationObserver.cpp b/dom/base/nsDOMMutationObserver.cpp index 4c4731c11..1f8e8f6a8 100644 --- a/dom/base/nsDOMMutationObserver.cpp +++ b/dom/base/nsDOMMutationObserver.cpp @@ -7,11 +7,13 @@ #include "nsDOMMutationObserver.h" #include "mozilla/AnimationTarget.h" +#include "mozilla/CycleCollectedJSContext.h" #include "mozilla/Maybe.h" #include "mozilla/OwningNonNull.h" #include "mozilla/dom/Animation.h" #include "mozilla/dom/KeyframeEffectReadOnly.h" +#include "mozilla/dom/DocGroup.h" #include "nsContentUtils.h" #include "nsCSSPseudoElements.h" @@ -29,6 +31,9 @@ using mozilla::dom::TreeOrderComparator; using mozilla::dom::Animation; using mozilla::dom::Element; +using namespace mozilla; +using namespace mozilla::dom; + AutoTArray<RefPtr<nsDOMMutationObserver>, 4>* nsDOMMutationObserver::sScheduledMutationObservers = nullptr; @@ -609,6 +614,28 @@ public: } }; +/* static */ void +nsDOMMutationObserver::QueueMutationObserverMicroTask() +{ + CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get(); + if (!ccjs) { + return; + } + + RefPtr<MutationObserverMicroTask> momt = + new MutationObserverMicroTask(); + ccjs->DispatchMicroTaskRunnable(momt.forget()); +} + +void +nsDOMMutationObserver::HandleMutations(mozilla::AutoSlowOperation& aAso) +{ + if (sScheduledMutationObservers || + mozilla::dom::DocGroup::sPendingDocGroups) { + HandleMutationsInternal(aAso); + } +} + void nsDOMMutationObserver::RescheduleForRun() { @@ -887,7 +914,23 @@ nsDOMMutationObserver::HandleMutationsInternal(AutoSlowOperation& aAso) { nsTArray<RefPtr<nsDOMMutationObserver> >* suppressedObservers = nullptr; - while (sScheduledMutationObservers) { + // Let signalList be a copy of unit of related similar-origin browsing + // contexts' signal slot list. + nsTArray<RefPtr<HTMLSlotElement>> signalList; + if (DocGroup::sPendingDocGroups) { + for (uint32_t i = 0; i < DocGroup::sPendingDocGroups->Length(); ++i) { + DocGroup* docGroup = DocGroup::sPendingDocGroups->ElementAt(i); + signalList.AppendElements(docGroup->SignalSlotList()); + + // Empty unit of related similar-origin browsing contexts' signal slot + // list. + docGroup->ClearSignalSlotList(); + } + delete DocGroup::sPendingDocGroups; + DocGroup::sPendingDocGroups = nullptr; + } + + if (sScheduledMutationObservers) { AutoTArray<RefPtr<nsDOMMutationObserver>, 4>* observers = sScheduledMutationObservers; sScheduledMutationObservers = nullptr; @@ -917,6 +960,11 @@ nsDOMMutationObserver::HandleMutationsInternal(AutoSlowOperation& aAso) delete suppressedObservers; suppressedObservers = nullptr; } + + // Fire slotchange event for each slot in signalList. + for (uint32_t i = 0; i < signalList.Length(); ++i) { + signalList[i]->FireSlotChangeEvent(); + } } nsDOMMutationRecord* diff --git a/dom/base/nsDOMMutationObserver.h b/dom/base/nsDOMMutationObserver.h index a8babc603..d6bdfb3e0 100644 --- a/dom/base/nsDOMMutationObserver.h +++ b/dom/base/nsDOMMutationObserver.h @@ -552,12 +552,9 @@ public: } // static methods - static void HandleMutations(mozilla::AutoSlowOperation& aAso) - { - if (sScheduledMutationObservers) { - HandleMutationsInternal(aAso); - } - } + static void QueueMutationObserverMicroTask(); + + static void HandleMutations(mozilla::AutoSlowOperation& aAso); static bool AllScheduledMutationObserversAreSuppressed() { diff --git a/dom/base/nsDOMTokenList.cpp b/dom/base/nsDOMTokenList.cpp index 39ff60e12..961beb50e 100644 --- a/dom/base/nsDOMTokenList.cpp +++ b/dom/base/nsDOMTokenList.cpp @@ -366,6 +366,12 @@ nsDOMTokenList::Stringify(nsAString& aResult) mElement->GetAttr(kNameSpaceID_None, mAttrAtom, aResult); } +DocGroup* +nsDOMTokenList::GetDocGroup() const +{ + return mElement ? mElement->OwnerDoc()->GetDocGroup() : nullptr; +} + JSObject* nsDOMTokenList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) { diff --git a/dom/base/nsDOMTokenList.h b/dom/base/nsDOMTokenList.h index e44e042d5..0b3f70319 100644 --- a/dom/base/nsDOMTokenList.h +++ b/dom/base/nsDOMTokenList.h @@ -22,7 +22,9 @@ namespace mozilla { class ErrorResult; - +namespace dom { +class DocGroup; +} // namespace dom } // namespace mozilla class nsAttrValue; @@ -35,6 +37,7 @@ class nsDOMTokenList : public nsISupports, { protected: typedef mozilla::dom::Element Element; + typedef mozilla::dom::DocGroup DocGroup; typedef nsWhitespaceTokenizerTemplate<nsContentUtils::IsHTMLWhitespace> WhitespaceTokenizer; @@ -52,6 +55,8 @@ public: return mElement; } + DocGroup* GetDocGroup() const; + uint32_t Length(); void Item(uint32_t aIndex, nsAString& aResult) { diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 2ab5937ac..a923e8f70 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -3473,7 +3473,7 @@ nsDOMWindowUtils::AddSheet(nsIDOMStyleSheet *aSheet, uint32_t aSheetType) nsIDocument::additionalSheetType type = convertSheetType(aSheetType); RefPtr<CSSStyleSheet> sheet = do_QueryObject(aSheet); NS_ENSURE_TRUE(sheet, NS_ERROR_FAILURE); - if (sheet->GetOwningDocument()) { + if (sheet->GetAssociatedDocument()) { return NS_ERROR_INVALID_ARG; } return doc->AddAdditionalStyleSheet(type, sheet); @@ -3657,11 +3657,11 @@ nsDOMWindowUtils::GetOMTAStyle(nsIDOMElement* aElement, RefPtr<nsROCSSPrimitiveValue> cssValue = nullptr; nsIFrame* frame = element->GetPrimaryFrame(); - if (frame && !aPseudoElement.IsEmpty()) { + if (!aPseudoElement.IsEmpty()) { if (aPseudoElement.EqualsLiteral("::before")) { - frame = nsLayoutUtils::GetBeforeFrame(frame); + frame = nsLayoutUtils::GetBeforeFrame(element); } else if (aPseudoElement.EqualsLiteral("::after")) { - frame = nsLayoutUtils::GetAfterFrame(frame); + frame = nsLayoutUtils::GetAfterFrame(element); } else { return NS_ERROR_INVALID_ARG; } diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index b05bf827b..7b280a188 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -116,7 +116,6 @@ #include "nsBidiUtils.h" -#include "nsIParserService.h" #include "nsContentCreatorFunctions.h" #include "nsIScriptContext.h" @@ -571,78 +570,6 @@ struct nsRadioGroupStruct bool mGroupSuffersFromValueMissing; }; - -nsDOMStyleSheetList::nsDOMStyleSheetList(nsIDocument *aDocument) -{ - mLength = -1; - // Not reference counted to avoid circular references. - // The document will tell us when its going away. - mDocument = aDocument; - mDocument->AddObserver(this); -} - -nsDOMStyleSheetList::~nsDOMStyleSheetList() -{ - if (mDocument) { - mDocument->RemoveObserver(this); - } -} - -NS_IMPL_ISUPPORTS_INHERITED(nsDOMStyleSheetList, StyleSheetList, - nsIDocumentObserver, - nsIMutationObserver) - -uint32_t -nsDOMStyleSheetList::Length() -{ - if (!mDocument) { - return 0; - } - - // XXX Find the number and then cache it. We'll use the - // observer notification to figure out if new ones have - // been added or removed. - if (-1 == mLength) { - mLength = mDocument->GetNumberOfStyleSheets(); - } - return mLength; -} - -StyleSheet* -nsDOMStyleSheetList::IndexedGetter(uint32_t aIndex, bool& aFound) -{ - if (!mDocument || aIndex >= (uint32_t)mDocument->GetNumberOfStyleSheets()) { - aFound = false; - return nullptr; - } - aFound = true; - return mDocument->GetStyleSheetAt(aIndex); -} - -void -nsDOMStyleSheetList::NodeWillBeDestroyed(const nsINode *aNode) -{ - mDocument = nullptr; -} - -void -nsDOMStyleSheetList::StyleSheetAdded(StyleSheet* aStyleSheet, - bool aDocumentSheet) -{ - if (aDocumentSheet && -1 != mLength) { - mLength++; - } -} - -void -nsDOMStyleSheetList::StyleSheetRemoved(StyleSheet* aStyleSheet, - bool aDocumentSheet) -{ - if (aDocumentSheet && -1 != mLength) { - mLength--; - } -} - // nsOnloadBlocker implementation NS_IMPL_ISUPPORTS(nsOnloadBlocker, nsIRequest) @@ -1200,10 +1127,10 @@ nsDOMStyleSheetSetList::EnsureFresh() // no document, for sure } - int32_t count = mDocument->GetNumberOfStyleSheets(); + size_t count = mDocument->SheetCount(); nsAutoString title; - for (int32_t index = 0; index < count; index++) { - StyleSheet* sheet = mDocument->GetStyleSheetAt(index); + for (size_t index = 0; index < count; index++) { + StyleSheet* sheet = mDocument->SheetAt(index); NS_ASSERTION(sheet, "Null sheet in sheet list!"); // XXXheycam ServoStyleSheets don't expose their title yet. if (sheet->IsServo()) { @@ -1333,6 +1260,10 @@ nsIDocument::nsIDocument() { SetIsInDocument(); + // Set this when document is created and value stays the same for the lifetime + // of the document. + mIsWebComponentsEnabled = nsContentUtils::IsWebComponentsEnabled(); + PR_INIT_CLIST(&mDOMMediaQueryLists); } @@ -1452,11 +1383,11 @@ nsDocument::~nsDocument() // Let the stylesheets know we're going away for (StyleSheet* sheet : mStyleSheets) { - sheet->SetOwningDocument(nullptr); + sheet->ClearAssociatedDocument(); } for (auto& sheets : mAdditionalSheets) { for (StyleSheet* sheet : sheets) { - sheet->SetOwningDocument(nullptr); + sheet->ClearAssociatedDocument(); } } if (mAttrStyleSheet) { @@ -2113,7 +2044,7 @@ nsDocument::RemoveDocStyleSheetsFromStyleSets() { // The stylesheets should forget us for (StyleSheet* sheet : Reversed(mStyleSheets)) { - sheet->SetOwningDocument(nullptr); + sheet->ClearAssociatedDocument(); if (sheet->IsApplicable()) { nsCOMPtr<nsIPresShell> shell = GetShell(); @@ -2132,7 +2063,7 @@ nsDocument::RemoveStyleSheetsFromStyleSets( { // The stylesheets should forget us for (StyleSheet* sheet : Reversed(aSheets)) { - sheet->SetOwningDocument(nullptr); + sheet->ClearAssociatedDocument(); if (sheet->IsApplicable()) { nsCOMPtr<nsIPresShell> shell = GetShell(); @@ -2311,6 +2242,29 @@ WarnIfSandboxIneffective(nsIDocShell* aDocShell, } } +bool +nsDocument::IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject) +{ + if (!nsContentUtils::IsWebComponentsEnabled()) { + return false; + } + + JS::Rooted<JSObject*> obj(aCx, aObject); + + JSAutoCompartment ac(aCx, obj); + JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForObject(aCx, obj)); + nsCOMPtr<nsPIDOMWindowInner> window = + do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(global)); + + nsIDocument* doc = window ? window->GetExtantDoc() : nullptr; + if (doc && doc->IsStyledByServo()) { + NS_WARNING("stylo: Web Components not supported yet"); + return false; + } + + return true; +} + nsresult nsDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel, nsILoadGroup* aLoadGroup, @@ -3927,24 +3881,6 @@ nsDocument::AddOnDemandBuiltInUASheet(StyleSheet* aSheet) NotifyStyleSheetAdded(aSheet, false); } -int32_t -nsDocument::GetNumberOfStyleSheets() const -{ - return mStyleSheets.Length(); -} - -StyleSheet* -nsDocument::GetStyleSheetAt(int32_t aIndex) const -{ - return mStyleSheets.SafeElementAt(aIndex, nullptr); -} - -int32_t -nsDocument::GetIndexOfStyleSheet(const StyleSheet* aSheet) const -{ - return mStyleSheets.IndexOf(aSheet); -} - void nsDocument::AddStyleSheetToStyleSets(StyleSheet* aSheet) { @@ -4007,7 +3943,7 @@ nsDocument::AddStyleSheet(StyleSheet* aSheet) { NS_PRECONDITION(aSheet, "null arg"); mStyleSheets.AppendElement(aSheet); - aSheet->SetOwningDocument(this); + aSheet->SetAssociatedDocument(this, StyleSheet::OwnedByDocument); if (aSheet->IsApplicable()) { AddStyleSheetToStyleSets(aSheet); @@ -4044,7 +3980,7 @@ nsDocument::RemoveStyleSheet(StyleSheet* aSheet) NotifyStyleSheetRemoved(aSheet, true); } - aSheet->SetOwningDocument(nullptr); + aSheet->ClearAssociatedDocument(); } void @@ -4072,7 +4008,7 @@ nsDocument::UpdateStyleSheets(nsTArray<RefPtr<StyleSheet>>& aOldSheets, StyleSheet* newSheet = aNewSheets[i]; if (newSheet) { mStyleSheets.InsertElementAt(oldIndex, newSheet); - newSheet->SetOwningDocument(this); + newSheet->SetAssociatedDocument(this, StyleSheet::OwnedByDocument); if (newSheet->IsApplicable()) { AddStyleSheetToStyleSets(newSheet); } @@ -4085,13 +4021,13 @@ nsDocument::UpdateStyleSheets(nsTArray<RefPtr<StyleSheet>>& aOldSheets, } void -nsDocument::InsertStyleSheetAt(StyleSheet* aSheet, int32_t aIndex) +nsDocument::InsertStyleSheetAt(StyleSheet* aSheet, size_t aIndex) { - NS_PRECONDITION(aSheet, "null ptr"); + MOZ_ASSERT(aSheet); mStyleSheets.InsertElementAt(aIndex, aSheet); - aSheet->SetOwningDocument(this); + aSheet->SetAssociatedDocument(this, StyleSheet::OwnedByDocument); if (aSheet->IsApplicable()) { AddStyleSheetToStyleSets(aSheet); @@ -4216,7 +4152,7 @@ nsDocument::LoadAdditionalStyleSheet(additionalSheetType aType, nsresult rv = loader->LoadSheetSync(aSheetURI, parsingMode, true, &sheet); NS_ENSURE_SUCCESS(rv, rv); - sheet->SetOwningDocument(this); + sheet->SetAssociatedDocument(this, StyleSheet::OwnedByDocument); MOZ_ASSERT(sheet->IsApplicable()); return AddAdditionalStyleSheet(aType, sheet); @@ -4274,7 +4210,7 @@ nsDocument::RemoveAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheet NotifyStyleSheetRemoved(sheetRef, false); EndUpdate(UPDATE_STYLE); - sheetRef->SetOwningDocument(nullptr); + sheetRef->ClearAssociatedDocument(); } } @@ -5353,29 +5289,18 @@ bool IsLowercaseASCII(const nsAString& aValue) return true; } -already_AddRefed<mozilla::dom::CustomElementRegistry> -nsDocument::GetCustomElementRegistry() +// We only support pseudo-elements with two colons in this function. +static CSSPseudoElementType +GetPseudoElementType(const nsString& aString, ErrorResult& aRv) { - nsAutoString contentType; - GetContentType(contentType); - if (!IsHTMLDocument() && - !contentType.EqualsLiteral("application/xhtml+xml")) { - return nullptr; + MOZ_ASSERT(!aString.IsEmpty(), "GetPseudoElementType aString should be non-null"); + if (aString.Length() <= 2 || aString[0] != ':' || aString[1] != ':') { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return CSSPseudoElementType::NotPseudo; } - - nsCOMPtr<nsPIDOMWindowInner> window( - do_QueryInterface(mScriptGlobalObject ? mScriptGlobalObject - : GetScopeObject())); - if (!window) { - return nullptr; - } - - RefPtr<CustomElementRegistry> registry = window->CustomElements(); - if (!registry) { - return nullptr; - } - - return registry.forget(); + nsCOMPtr<nsIAtom> pseudo = NS_Atomize(Substring(aString, 1)); + return nsCSSPseudoElements::GetPseudoType(pseudo, + nsCSSProps::EnabledState::eInUASheets); } already_AddRefed<Element> @@ -5395,10 +5320,36 @@ nsDocument::CreateElement(const nsAString& aTagName, } const nsString* is = nullptr; + CSSPseudoElementType pseudoType = CSSPseudoElementType::NotPseudo; + if (aOptions.IsElementCreationOptions()) { + const ElementCreationOptions& options = + aOptions.GetAsElementCreationOptions(); + + if (CustomElementRegistry::IsCustomElementEnabled() && + options.mIs.WasPassed()) { + is = &options.mIs.Value(); + } + + // Check 'pseudo' and throw an exception if it's not one allowed + // with CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC. + if (options.mPseudo.WasPassed()) { + pseudoType = GetPseudoElementType(options.mPseudo.Value(), rv); + if (rv.Failed() || + pseudoType == CSSPseudoElementType::NotPseudo || + !nsCSSPseudoElements::PseudoElementIsJSCreatedNAC(pseudoType)) { + rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return nullptr; + } + } + } RefPtr<Element> elem = CreateElem( needsLowercase ? lcTagName : aTagName, nullptr, mDefaultElementType, is); + if (pseudoType != CSSPseudoElementType::NotPseudo) { + elem->SetPseudoElementType(pseudoType); + } + if (is) { elem->SetAttr(kNameSpaceID_None, nsGkAtoms::is, *is, true); } @@ -5413,8 +5364,8 @@ nsDocument::CreateElementNS(const nsAString& aNamespaceURI, { *aReturn = nullptr; ElementCreationOptionsOrString options; - options.SetAsString(); + options.SetAsString(); ErrorResult rv; nsCOMPtr<Element> element = CreateElementNS(aNamespaceURI, aQualifiedName, options, rv); @@ -5439,6 +5390,13 @@ nsDocument::CreateElementNS(const nsAString& aNamespaceURI, } const nsString* is = nullptr; + if (CustomElementRegistry::IsCustomElementEnabled() && + aOptions.IsElementCreationOptions()) { + const ElementCreationOptions& options = aOptions.GetAsElementCreationOptions(); + if (options.mIs.WasPassed()) { + is = &options.mIs.Value(); + } + } nsCOMPtr<Element> element; rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(), @@ -5647,278 +5605,9 @@ nsIDocument::CreateAttributeNS(const nsAString& aNamespaceURI, } bool -nsDocument::CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp) +nsDocument::IsWebComponentsEnabled(const nsINode* aNode) { - JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp); - - JS::Rooted<JSObject*> global(aCx, - JS_GetGlobalForObject(aCx, &args.callee())); - RefPtr<nsGlobalWindow> window; - UNWRAP_OBJECT(Window, global, window); - MOZ_ASSERT(window, "Should have a non-null window"); - - nsDocument* document = static_cast<nsDocument*>(window->GetDoc()); - - // Function name is the type of the custom element. - JSString* jsFunName = - JS_GetFunctionId(JS_ValueToFunction(aCx, args.calleev())); - nsAutoJSString elemName; - if (!elemName.init(aCx, jsFunName)) { - return true; - } - - RefPtr<mozilla::dom::CustomElementRegistry> registry = window->CustomElements(); - if (!registry) { - return true; - } - - nsCOMPtr<nsIAtom> typeAtom(NS_Atomize(elemName)); - CustomElementDefinition* definition = - registry->mCustomDefinitions.GetWeak(typeAtom); - if (!definition) { - return true; - } - - RefPtr<Element> element; - - // We integrate with construction stack and do prototype swizzling here, so - // that old upgrade behavior could also share the new upgrade steps. - // And this old upgrade will be remove at some point (when everything is - // switched to latest custom element spec). - nsTArray<RefPtr<nsGenericHTMLElement>>& constructionStack = - definition->mConstructionStack; - if (constructionStack.Length()) { - element = constructionStack.LastElement(); - NS_ENSURE_TRUE(element != ALEADY_CONSTRUCTED_MARKER, false); - - // Do prototype swizzling if dom reflector exists. - JS::Rooted<JSObject*> reflector(aCx, element->GetWrapper()); - if (reflector) { - Maybe<JSAutoCompartment> ac; - JS::Rooted<JSObject*> prototype(aCx, definition->mPrototype); - if (element->NodePrincipal()->SubsumesConsideringDomain(nsContentUtils::ObjectPrincipal(prototype))) { - ac.emplace(aCx, reflector); - if (!JS_WrapObject(aCx, &prototype) || - !JS_SetPrototype(aCx, reflector, prototype)) { - return false; - } - } else { - // We want to set the custom prototype in the compartment where it was - // registered. We store the prototype from define() without unwrapped, - // hence the prototype's compartment is the compartment where it was - // registered. - // In the case that |reflector| and |prototype| are in different - // compartments, this will set the prototype on the |reflector|'s wrapper - // and thus only visible in the wrapper's compartment, since we know - // reflector's principal does not subsume prototype's in this case. - ac.emplace(aCx, prototype); - if (!JS_WrapObject(aCx, &reflector) || - !JS_SetPrototype(aCx, reflector, prototype)) { - return false; - } - } - - // Wrap into current context. - if (!JS_WrapObject(aCx, &reflector)) { - return false; - } - - args.rval().setObject(*reflector); - return true; - } - } else { - nsDependentAtomString localName(definition->mLocalName); - element = - document->CreateElem(localName, nullptr, kNameSpaceID_XHTML, - (definition->mLocalName != typeAtom) ? &elemName - : nullptr); - NS_ENSURE_TRUE(element, false); - } - - // The prototype setup happens in Element::WrapObject(). - - nsresult rv = nsContentUtils::WrapNative(aCx, element, element, args.rval()); - NS_ENSURE_SUCCESS(rv, true); - - return true; -} - -bool -nsDocument::IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject) -{ - JS::Rooted<JSObject*> obj(aCx, aObject); - - if (nsContentUtils::IsWebComponentsEnabled()) { - return true; - } - - // Check for the webcomponents permission. See Bug 1181555. - JSAutoCompartment ac(aCx, obj); - JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForObject(aCx, obj)); - nsCOMPtr<nsPIDOMWindowInner> window = - do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(global)); - - return IsWebComponentsEnabled(window); -} - -bool -nsDocument::IsWebComponentsEnabled(dom::NodeInfo* aNodeInfo) -{ - if (nsContentUtils::IsWebComponentsEnabled()) { - return true; - } - - nsIDocument* doc = aNodeInfo->GetDocument(); - // Use GetScopeObject() here so that data documents work the same way as the - // main document they're associated with. - nsCOMPtr<nsPIDOMWindowInner> window = - do_QueryInterface(doc->GetScopeObject()); - return IsWebComponentsEnabled(window); -} - -bool -nsDocument::IsWebComponentsEnabled(nsPIDOMWindowInner* aWindow) -{ - if (aWindow) { - nsresult rv; - nsCOMPtr<nsIPermissionManager> permMgr = - do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, false); - - uint32_t perm; - rv = permMgr->TestPermissionFromWindow( - aWindow, "moz-extremely-unstable-and-will-change-webcomponents", &perm); - NS_ENSURE_SUCCESS(rv, false); - - return perm == nsIPermissionManager::ALLOW_ACTION; - } - - return false; -} - -void -nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType, - const ElementRegistrationOptions& aOptions, - JS::MutableHandle<JSObject*> aRetval, - ErrorResult& rv) -{ - RefPtr<CustomElementRegistry> registry(GetCustomElementRegistry()); - if (!registry) { - rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); - return; - } - - AutoCEReaction ceReaction(this->GetDocGroup()->CustomElementReactionsStack(), - aCx); - // Unconditionally convert TYPE to lowercase. - nsAutoString lcType; - nsContentUtils::ASCIIToLower(aType, lcType); - - nsIGlobalObject* sgo = GetScopeObject(); - if (!sgo) { - rv.Throw(NS_ERROR_UNEXPECTED); - return; - } - - JS::Rooted<JSObject*> global(aCx, sgo->GetGlobalJSObject()); - JS::Rooted<JSObject*> protoObject(aCx); - - if (!aOptions.mPrototype) { - JS::Rooted<JSObject*> htmlProto(aCx); - htmlProto = HTMLElementBinding::GetProtoObjectHandle(aCx); - if (!htmlProto) { - rv.Throw(NS_ERROR_OUT_OF_MEMORY); - return; - } - - protoObject = JS_NewObjectWithGivenProto(aCx, nullptr, htmlProto); - if (!protoObject) { - rv.Throw(NS_ERROR_UNEXPECTED); - return; - } - } else { - protoObject = aOptions.mPrototype; - - // Get the unwrapped prototype to do some checks. - JS::Rooted<JSObject*> protoObjectUnwrapped(aCx, js::CheckedUnwrap(protoObject)); - if (!protoObjectUnwrapped) { - // If the caller's compartment does not have permission to access the - // unwrapped prototype then throw. - rv.Throw(NS_ERROR_DOM_SECURITY_ERR); - return; - } - - // If PROTOTYPE is already an interface prototype object for any interface - // object or PROTOTYPE has a non-configurable property named constructor, - // throw a NotSupportedError and stop. - const js::Class* clasp = js::GetObjectClass(protoObjectUnwrapped); - if (IsDOMIfaceAndProtoClass(clasp)) { - rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); - return; - } - - JS::Rooted<JS::PropertyDescriptor> descRoot(aCx); - JS::MutableHandle<JS::PropertyDescriptor> desc(&descRoot); - // This check may go through a wrapper, but as we checked above - // it should be transparent or an xray. This should be fine for now, - // until the spec is sorted out. - if (!JS_GetPropertyDescriptor(aCx, protoObject, "constructor", desc)) { - rv.Throw(NS_ERROR_UNEXPECTED); - return; - } - - if (!desc.configurable()) { - rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); - return; - } - } - - JS::Rooted<JSFunction*> constructor(aCx); - { - // Go into the document's global compartment when creating the constructor - // function because we want to get the correct document (where the - // definition is registered) when it is called. - JSAutoCompartment ac(aCx, global); - - // Create constructor to return. Store the name of the custom element as the - // name of the function. - constructor = JS_NewFunction(aCx, nsDocument::CustomElementConstructor, 0, - JSFUN_CONSTRUCTOR, - NS_ConvertUTF16toUTF8(lcType).get()); - if (!constructor) { - rv.Throw(NS_ERROR_OUT_OF_MEMORY); - return; - } - } - - JS::Rooted<JSObject*> wrappedConstructor(aCx); - wrappedConstructor = JS_GetFunctionObject(constructor); - if (!JS_WrapObject(aCx, &wrappedConstructor)) { - rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); - return; - } - - if (!JS_LinkConstructorAndPrototype(aCx, wrappedConstructor, protoObject)) { - rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); - return; - } - - ElementDefinitionOptions options; - if (!aOptions.mExtends.IsVoid()) { - // Only convert NAME to lowercase in HTML documents. - nsAutoString lcName; - IsHTMLDocument() ? nsContentUtils::ASCIIToLower(aOptions.mExtends, lcName) - : lcName.Assign(aOptions.mExtends); - - options.mExtends.Construct(lcName); - } - - RootedCallback<OwningNonNull<binding_detail::FastFunction>> functionConstructor(aCx); - functionConstructor = new binding_detail::FastFunction(aCx, wrappedConstructor, sgo); - - registry->Define(lcType, functionConstructor, options, rv); - - aRetval.set(wrappedConstructor); + return aNode->OwnerDoc()->IsWebComponentsEnabled(); } NS_IMETHODIMP @@ -6006,15 +5695,6 @@ nsDocument::GetStyleSheets(nsIDOMStyleSheetList** aStyleSheets) return NS_OK; } -StyleSheetList* -nsDocument::StyleSheets() -{ - if (!mDOMStyleSheets) { - mDOMStyleSheets = new nsDOMStyleSheetList(this); - } - return mDOMStyleSheets; -} - NS_IMETHODIMP nsDocument::GetMozSelectedStyleSheetSet(nsAString& aSheetSet) { @@ -6028,10 +5708,10 @@ nsIDocument::GetSelectedStyleSheetSet(nsAString& aSheetSet) aSheetSet.Truncate(); // Look through our sheets, find the selected set title - int32_t count = GetNumberOfStyleSheets(); + size_t count = SheetCount(); nsAutoString title; - for (int32_t index = 0; index < count; index++) { - StyleSheet* sheet = GetStyleSheetAt(index); + for (size_t index = 0; index < count; index++) { + StyleSheet* sheet = SheetAt(index); NS_ASSERTION(sheet, "Null sheet in sheet list!"); // XXXheycam Make this work with ServoStyleSheets. @@ -6148,10 +5828,10 @@ nsDocument::EnableStyleSheetsForSetInternal(const nsAString& aSheetSet, bool aUpdateCSSLoader) { BeginUpdate(UPDATE_STYLE); - int32_t count = GetNumberOfStyleSheets(); + size_t count = SheetCount(); nsAutoString title; - for (int32_t index = 0; index < count; index++) { - StyleSheet* sheet = GetStyleSheetAt(index); + for (size_t index = 0; index < count; index++) { + StyleSheet* sheet = SheetAt(index); NS_ASSERTION(sheet, "Null sheet in sheet list!"); // XXXheycam Make this work with ServoStyleSheets. @@ -6421,7 +6101,7 @@ already_AddRefed<nsRange> nsIDocument::CreateRange(ErrorResult& rv) { RefPtr<nsRange> range = new nsRange(this); - nsresult res = range->Set(this, 0, this, 0); + nsresult res = range->CollapseTo(this, 0); if (NS_FAILED(res)) { rv.Throw(res); return nullptr; @@ -7712,7 +7392,7 @@ nsDocument::GetExistingListenerManager() const } nsresult -nsDocument::PreHandleEvent(EventChainPreVisitor& aVisitor) +nsDocument::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.mCanHandle = true; // FIXME! This is a hack to make middle mouse paste working also in Editor. @@ -7722,8 +7402,8 @@ nsDocument::PreHandleEvent(EventChainPreVisitor& aVisitor) // Load events must not propagate to |window| object, see bug 335251. if (aVisitor.mEvent->mMessage != eLoad) { nsGlobalWindow* window = nsGlobalWindow::Cast(GetWindow()); - aVisitor.mParentTarget = - window ? window->GetTargetForEventTargetChain() : nullptr; + aVisitor.SetParentTarget( + window ? window->GetTargetForEventTargetChain() : nullptr, false); } return NS_OK; } @@ -9859,9 +9539,9 @@ nsIDocument::CreateStaticClone(nsIDocShell* aCloneContainer) clonedDoc->mOriginalDocument->mStaticCloneCount++; - int32_t sheetsCount = GetNumberOfStyleSheets(); - for (int32_t i = 0; i < sheetsCount; ++i) { - RefPtr<StyleSheet> sheet = GetStyleSheetAt(i); + size_t sheetsCount = SheetCount(); + for (size_t i = 0; i < sheetsCount; ++i) { + RefPtr<StyleSheet> sheet = SheetAt(i); if (sheet) { if (sheet->IsApplicable()) { // XXXheycam Need to make ServoStyleSheet cloning work. @@ -12079,7 +11759,7 @@ SizeOfOwnedSheetArrayExcludingThis(const nsTArray<RefPtr<StyleSheet>>& aSheets, size_t n = 0; n += aSheets.ShallowSizeOfExcludingThis(aMallocSizeOf); for (StyleSheet* sheet : aSheets) { - if (!sheet->GetOwningDocument()) { + if (!sheet->GetAssociatedDocument()) { // Avoid over-reporting shared sheets. continue; } diff --git a/dom/base/nsDocument.h b/dom/base/nsDocument.h index 90e511dcb..6520d905d 100644 --- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -293,36 +293,6 @@ public: nsDocHeaderData* mNext; }; -class nsDOMStyleSheetList : public mozilla::dom::StyleSheetList, - public nsStubDocumentObserver -{ -public: - explicit nsDOMStyleSheetList(nsIDocument* aDocument); - - NS_DECL_ISUPPORTS_INHERITED - - // nsIDocumentObserver - NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETADDED - NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETREMOVED - - // nsIMutationObserver - NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED - - virtual nsINode* GetParentObject() const override - { - return mDocument; - } - - uint32_t Length() override; - mozilla::StyleSheet* IndexedGetter(uint32_t aIndex, bool& aFound) override; - -protected: - virtual ~nsDOMStyleSheetList(); - - int32_t mLength; - nsIDocument* mDocument; -}; - class nsOnloadBlocker final : public nsIRequest { public: @@ -624,14 +594,6 @@ public: virtual void EnsureOnDemandBuiltInUASheet(mozilla::StyleSheet* aSheet) override; - /** - * Get the (document) style sheets owned by this document. - * These are ordered, highest priority last - */ - virtual int32_t GetNumberOfStyleSheets() const override; - virtual mozilla::StyleSheet* GetStyleSheetAt(int32_t aIndex) const override; - virtual int32_t GetIndexOfStyleSheet( - const mozilla::StyleSheet* aSheet) const override; virtual void AddStyleSheet(mozilla::StyleSheet* aSheet) override; virtual void RemoveStyleSheet(mozilla::StyleSheet* aSheet) override; @@ -642,7 +604,7 @@ public: virtual void RemoveStyleSheetFromStyleSets(mozilla::StyleSheet* aSheet); virtual void InsertStyleSheetAt(mozilla::StyleSheet* aSheet, - int32_t aIndex) override; + size_t aIndex) override; virtual void SetStyleSheetApplicableState(mozilla::StyleSheet* aSheet, bool aApplicable) override; @@ -793,7 +755,11 @@ public: virtual void NotifyLayerManagerRecreated() override; - + // Check whether web components are enabled for the global of aObject. + static bool IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject); + // Check whether web components are enabled for the document this node belongs + // to. + static bool IsWebComponentsEnabled(const nsINode* aNode); private: void AddOnDemandBuiltInUASheet(mozilla::StyleSheet* aSheet); nsRadioGroupStruct* GetRadioGroupInternal(const nsAString& aName) const; @@ -810,7 +776,7 @@ public: NS_DECL_NSIDOMDOCUMENTXBL // nsIDOMEventTarget - virtual nsresult PreHandleEvent( + virtual nsresult GetEventTargetParent( mozilla::EventChainPreVisitor& aVisitor) override; virtual mozilla::EventListenerManager* GetOrCreateListenerManager() override; @@ -1129,12 +1095,7 @@ public: // WebIDL bits virtual mozilla::dom::DOMImplementation* GetImplementation(mozilla::ErrorResult& rv) override; - virtual void - RegisterElement(JSContext* aCx, const nsAString& aName, - const mozilla::dom::ElementRegistrationOptions& aOptions, - JS::MutableHandle<JSObject*> aRetval, - mozilla::ErrorResult& rv) override; - virtual mozilla::dom::StyleSheetList* StyleSheets() override; + virtual void SetSelectedStyleSheetSet(const nsAString& aSheetSet) override; virtual void GetLastStyleSheetSet(nsString& aSheetSet) override; virtual mozilla::dom::DOMStringList* StyleSheetSets() override; @@ -1356,7 +1317,6 @@ protected: // EndLoad() has already happened. nsWeakPtr mWeakSink; - nsTArray<RefPtr<mozilla::StyleSheet>> mStyleSheets; nsTArray<RefPtr<mozilla::StyleSheet>> mOnDemandBuiltInUASheets; nsTArray<RefPtr<mozilla::StyleSheet>> mAdditionalSheets[AdditionalSheetTypeCount]; @@ -1385,23 +1345,8 @@ protected: // non-null when this document is in fullscreen mode. nsWeakPtr mFullscreenRoot; -private: - static bool CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp); - public: - virtual already_AddRefed<mozilla::dom::CustomElementRegistry> - GetCustomElementRegistry() override; - - // Check whether web components are enabled for the global of aObject. - static bool IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject); - // Check whether web components are enabled for the global of the document - // this nodeinfo comes from. - static bool IsWebComponentsEnabled(mozilla::dom::NodeInfo* aNodeInfo); - // Check whether web components are enabled for the given window. - static bool IsWebComponentsEnabled(nsPIDOMWindowInner* aWindow); - RefPtr<mozilla::EventListenerManager> mListenerManager; - RefPtr<mozilla::dom::StyleSheetList> mDOMStyleSheets; RefPtr<nsDOMStyleSheetSetList> mStyleSheetSetList; RefPtr<nsScriptLoader> mScriptLoader; nsDocHeaderData* mHeaderData; diff --git a/dom/base/nsDocumentEncoder.cpp b/dom/base/nsDocumentEncoder.cpp index 34eb6ed38..2f5c3dc0a 100644 --- a/dom/base/nsDocumentEncoder.cpp +++ b/dom/base/nsDocumentEncoder.cpp @@ -32,7 +32,6 @@ #include "nsIDOMDocument.h" #include "nsGkAtoms.h" #include "nsIContent.h" -#include "nsIParserService.h" #include "nsIScriptContext.h" #include "nsIScriptGlobalObject.h" #include "nsIScriptSecurityManager.h" @@ -40,6 +39,7 @@ #include "nsISelectionPrivate.h" #include "nsITransferable.h" // for kUnicodeMime #include "nsContentUtils.h" +#include "nsElementTable.h" #include "nsNodeUtils.h" #include "nsUnicharUtils.h" #include "nsReadableUtils.h" @@ -1588,10 +1588,13 @@ nsHTMLCopyEncoder::IncludeInContext(nsINode *aNode) nsresult nsHTMLCopyEncoder::PromoteRange(nsIDOMRange *inRange) { - if (!inRange) return NS_ERROR_NULL_POINTER; + RefPtr<nsRange> range = static_cast<nsRange*>(inRange); + if (!range) { + return NS_ERROR_NULL_POINTER; + } nsresult rv; nsCOMPtr<nsIDOMNode> startNode, endNode, common; - int32_t startOffset, endOffset; + uint32_t startOffset, endOffset; rv = inRange->GetCommonAncestorContainer(getter_AddRefs(common)); NS_ENSURE_SUCCESS(rv, rv); @@ -1609,9 +1612,11 @@ nsHTMLCopyEncoder::PromoteRange(nsIDOMRange *inRange) int32_t opStartOffset, opEndOffset; // examine range endpoints. - rv = GetPromotedPoint( kStart, startNode, startOffset, address_of(opStartNode), &opStartOffset, common); + rv = GetPromotedPoint(kStart, startNode, static_cast<int32_t>(startOffset), + address_of(opStartNode), &opStartOffset, common); NS_ENSURE_SUCCESS(rv, rv); - rv = GetPromotedPoint( kEnd, endNode, endOffset, address_of(opEndNode), &opEndOffset, common); + rv = GetPromotedPoint(kEnd, endNode, static_cast<int32_t>(endOffset), + address_of(opEndNode), &opEndOffset, common); NS_ENSURE_SUCCESS(rv, rv); // if both range endpoints are at the common ancestor, check for possible inclusion of ancestors @@ -1623,9 +1628,9 @@ nsHTMLCopyEncoder::PromoteRange(nsIDOMRange *inRange) } // set the range to the new values - rv = inRange->SetStart(opStartNode, opStartOffset); + rv = inRange->SetStart(opStartNode, static_cast<uint32_t>(opStartOffset)); NS_ENSURE_SUCCESS(rv, rv); - rv = inRange->SetEnd(opEndNode, opEndOffset); + rv = inRange->SetEnd(opEndNode, static_cast<uint32_t>(opEndOffset)); return rv; } @@ -1736,9 +1741,6 @@ nsHTMLCopyEncoder::GetPromotedPoint(Endpoint aWhere, nsIDOMNode *aNode, int32_t rv = GetNodeLocation(node, address_of(parent), &offset); NS_ENSURE_SUCCESS(rv, rv); if (offset == -1) return NS_OK; // we hit generated content; STOP - nsIParserService *parserService = nsContentUtils::GetParserService(); - if (!parserService) - return NS_ERROR_OUT_OF_MEMORY; while ((IsFirstNode(node)) && (!IsRoot(parent)) && (parent != common)) { if (bResetPromotion) @@ -1746,11 +1748,8 @@ nsHTMLCopyEncoder::GetPromotedPoint(Endpoint aWhere, nsIDOMNode *aNode, int32_t nsCOMPtr<nsIContent> content = do_QueryInterface(parent); if (content && content->IsHTMLElement()) { - bool isBlock = false; - parserService->IsBlock(parserService->HTMLAtomTagToId( - content->NodeInfo()->NameAtom()), isBlock); - if (isBlock) - { + if (nsHTMLElement::IsBlock(nsHTMLTags::AtomTagToId( + content->NodeInfo()->NameAtom()))) { bResetPromotion = false; } } @@ -1819,9 +1818,6 @@ nsHTMLCopyEncoder::GetPromotedPoint(Endpoint aWhere, nsIDOMNode *aNode, int32_t rv = GetNodeLocation(node, address_of(parent), &offset); NS_ENSURE_SUCCESS(rv, rv); if (offset == -1) return NS_OK; // we hit generated content; STOP - nsIParserService *parserService = nsContentUtils::GetParserService(); - if (!parserService) - return NS_ERROR_OUT_OF_MEMORY; while ((IsLastNode(node)) && (!IsRoot(parent)) && (parent != common)) { if (bResetPromotion) @@ -1829,11 +1825,8 @@ nsHTMLCopyEncoder::GetPromotedPoint(Endpoint aWhere, nsIDOMNode *aNode, int32_t nsCOMPtr<nsIContent> content = do_QueryInterface(parent); if (content && content->IsHTMLElement()) { - bool isBlock = false; - parserService->IsBlock(parserService->HTMLAtomTagToId( - content->NodeInfo()->NameAtom()), isBlock); - if (isBlock) - { + if (nsHTMLElement::IsBlock(nsHTMLTags::AtomTagToId( + content->NodeInfo()->NameAtom()))) { bResetPromotion = false; } } diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp index 01c1944be..c14087c8a 100644 --- a/dom/base/nsFocusManager.cpp +++ b/dom/base/nsFocusManager.cpp @@ -2457,7 +2457,7 @@ nsFocusManager::GetSelectionLocation(nsIDocument* aDocument, nsCOMPtr<nsIDOMNode> startNode, endNode; bool isCollapsed = false; nsCOMPtr<nsIContent> startContent, endContent; - int32_t startOffset = 0; + uint32_t startOffset = 0; if (domSelection) { domSelection->GetIsCollapsed(&isCollapsed); nsCOMPtr<nsIDOMRange> domRange; @@ -2471,7 +2471,6 @@ nsFocusManager::GetSelectionLocation(nsIDocument* aDocument, startContent = do_QueryInterface(startNode); if (startContent && startContent->IsElement()) { - NS_ASSERTION(startOffset >= 0, "Start offset cannot be negative"); childContent = startContent->GetChildAt(startOffset); if (childContent) { startContent = childContent; @@ -2480,9 +2479,8 @@ nsFocusManager::GetSelectionLocation(nsIDocument* aDocument, endContent = do_QueryInterface(endNode); if (endContent && endContent->IsElement()) { - int32_t endOffset = 0; + uint32_t endOffset = 0; domRange->GetEndOffset(&endOffset); - NS_ASSERTION(endOffset >= 0, "End offset cannot be negative"); childContent = endContent->GetChildAt(endOffset); if (childContent) { endContent = childContent; @@ -2510,7 +2508,7 @@ nsFocusManager::GetSelectionLocation(nsIDocument* aDocument, bool isFormControl = startContent->IsNodeOfType(nsINode::eHTML_FORM_CONTROL); - if (nodeValue.Length() == (uint32_t)startOffset && !isFormControl && + if (nodeValue.Length() == startOffset && !isFormControl && startContent != aDocument->GetRootElement()) { // Yes, indeed we were at the end of the last node nsCOMPtr<nsIFrameEnumerator> frameTraversal; diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index 2804f2d4c..942a84102 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -57,6 +57,7 @@ #include "nsGlobalWindow.h" #include "nsPIWindowRoot.h" #include "nsLayoutUtils.h" +#include "nsMappedAttributes.h" #include "nsView.h" #include "GroupedSHistory.h" #include "PartialSHistory.h" @@ -936,6 +937,8 @@ nsFrameLoader::MarginsChanged(uint32_t aMarginWidth, RefPtr<nsPresContext> presContext; mDocShell->GetPresContext(getter_AddRefs(presContext)); if (presContext) + // rebuild, because now the same nsMappedAttributes* will produce + // a different style presContext->RebuildAllStyleData(nsChangeHint(0), eRestyle_Subtree); } diff --git a/dom/base/nsGenConImageContent.cpp b/dom/base/nsGenConImageContent.cpp index e1b1f5a17..af3b186bd 100644 --- a/dom/base/nsGenConImageContent.cpp +++ b/dom/base/nsGenConImageContent.cpp @@ -46,7 +46,7 @@ public: virtual void UnbindFromTree(bool aDeep, bool aNullParent) override; virtual EventStates IntrinsicState() const override; - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override + virtual nsresult GetEventTargetParent(EventChainPreVisitor& aVisitor) override { MOZ_ASSERT(IsInNativeAnonymousSubtree()); if (aVisitor.mEvent->mMessage == eLoad || @@ -54,7 +54,7 @@ public: // Don't propagate the events to the parent. return NS_OK; } - return nsXMLElement::PreHandleEvent(aVisitor); + return nsXMLElement::GetEventTargetParent(aVisitor); } private: diff --git a/dom/base/nsGenericDOMDataNode.cpp b/dom/base/nsGenericDOMDataNode.cpp index 73463ea5e..d23783368 100644 --- a/dom/base/nsGenericDOMDataNode.cpp +++ b/dom/base/nsGenericDOMDataNode.cpp @@ -15,6 +15,7 @@ #include "mozilla/AsyncEventDispatcher.h" #include "mozilla/MemoryReporting.h" #include "mozilla/dom/Element.h" +#include "mozilla/dom/HTMLSlotElement.h" #include "mozilla/dom/ShadowRoot.h" #include "nsIDocument.h" #include "nsIDOMDocument.h" @@ -736,21 +737,18 @@ nsGenericDOMDataNode::SetShadowRoot(ShadowRoot* aShadowRoot) { } -nsTArray<nsIContent*>& -nsGenericDOMDataNode::DestInsertionPoints() +HTMLSlotElement* +nsGenericDOMDataNode::GetAssignedSlot() const { - nsDataSlots *slots = DataSlots(); - return slots->mDestInsertionPoints; + nsDataSlots *slots = GetExistingDataSlots(); + return slots ? slots->mAssignedSlot.get() : nullptr; } -nsTArray<nsIContent*>* -nsGenericDOMDataNode::GetExistingDestInsertionPoints() const +void +nsGenericDOMDataNode::SetAssignedSlot(HTMLSlotElement* aSlot) { - nsDataSlots *slots = GetExistingDataSlots(); - if (slots) { - return &slots->mDestInsertionPoints; - } - return nullptr; + nsDataSlots *slots = DataSlots(); + slots->mAssignedSlot = aSlot; } nsXBLBinding * @@ -843,6 +841,9 @@ nsGenericDOMDataNode::nsDataSlots::Traverse(nsCycleCollectionTraversalCallback & NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mContainingShadow"); cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mContainingShadow)); + + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mAssignedSlot"); + cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mAssignedSlot.get())); } void @@ -850,6 +851,7 @@ nsGenericDOMDataNode::nsDataSlots::Unlink() { mXBLInsertionParent = nullptr; mContainingShadow = nullptr; + mAssignedSlot = nullptr; } //---------------------------------------------------------------------- diff --git a/dom/base/nsGenericDOMDataNode.h b/dom/base/nsGenericDOMDataNode.h index e8818b518..4d0114cb1 100644 --- a/dom/base/nsGenericDOMDataNode.h +++ b/dom/base/nsGenericDOMDataNode.h @@ -26,6 +26,12 @@ class nsIDocument; class nsIDOMText; +namespace mozilla { +namespace dom { +class HTMLSlotElement; +} // namespace dom +} // namespace mozilla + #define DATA_NODE_FLAG_BIT(n_) NODE_FLAG_BIT(NODE_TYPE_SPECIFIC_BITS_OFFSET + (n_)) // Data node specific flags @@ -154,9 +160,9 @@ public: virtual void SetXBLBinding(nsXBLBinding* aBinding, nsBindingManager* aOldBindingManager = nullptr) override; virtual mozilla::dom::ShadowRoot *GetContainingShadow() const override; - virtual nsTArray<nsIContent*> &DestInsertionPoints() override; - virtual nsTArray<nsIContent*> *GetExistingDestInsertionPoints() const override; virtual void SetShadowRoot(mozilla::dom::ShadowRoot* aShadowRoot) override; + virtual mozilla::dom::HTMLSlotElement* GetAssignedSlot() const override; + virtual void SetAssignedSlot(mozilla::dom::HTMLSlotElement* aSlot) override; virtual nsIContent *GetXBLInsertionParent() const override; virtual void SetXBLInsertionParent(nsIContent* aContent) override; virtual bool IsNodeOfType(uint32_t aFlags) const override; @@ -261,9 +267,9 @@ protected: RefPtr<mozilla::dom::ShadowRoot> mContainingShadow; /** - * @see nsIContent::GetDestInsertionPoints + * @see nsIContent::GetAssignedSlot */ - nsTArray<nsIContent*> mDestInsertionPoints; + RefPtr<mozilla::dom::HTMLSlotElement> mAssignedSlot; }; // Override from nsINode diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h index 73a3a02b1..35963afd7 100644 --- a/dom/base/nsGkAtomList.h +++ b/dom/base/nsGkAtomList.h @@ -1560,11 +1560,11 @@ GK_ATOM(saturate, "saturate") GK_ATOM(saturation, "saturation") GK_ATOM(set, "set") GK_ATOM(seed, "seed") -GK_ATOM(shadow, "shadow") GK_ATOM(shape_rendering, "shape-rendering") GK_ATOM(skewX, "skewX") GK_ATOM(skewY, "skewY") GK_ATOM(slope, "slope") +GK_ATOM(slot, "slot") GK_ATOM(softLight, "soft-light") GK_ATOM(spacing, "spacing") GK_ATOM(spacingAndGlyphs, "spacingAndGlyphs") @@ -2144,12 +2144,14 @@ GK_ATOM(ongamepaddisconnected, "ongamepaddisconnected") #endif // Content property names +GK_ATOM(afterPseudoProperty, "afterPseudoProperty") // nsXMLElement* GK_ATOM(animationsProperty, "AnimationsProperty") // FrameAnimations* GK_ATOM(animationsOfBeforeProperty, "AnimationsOfBeforeProperty") // FrameAnimations* GK_ATOM(animationsOfAfterProperty, "AnimationsOfAfterProperty") // FrameAnimations* GK_ATOM(animationEffectsProperty, "AnimationEffectsProperty") // EffectSet* GK_ATOM(animationEffectsForBeforeProperty, "AnimationsEffectsForBeforeProperty") // EffectSet* GK_ATOM(animationEffectsForAfterProperty, "AnimationsEffectsForAfterProperty") // EffectSet* +GK_ATOM(beforePseudoProperty, "beforePseudoProperty") // nsXMLElement* GK_ATOM(cssPseudoElementBeforeProperty, "CSSPseudoElementBeforeProperty") // CSSPseudoElement* GK_ATOM(cssPseudoElementAfterProperty, "CSSPseudoElementAfterProperty") // CSSPseudoElement* GK_ATOM(transitionsProperty, "TransitionsProperty") // FrameTransitions* @@ -2162,6 +2164,7 @@ GK_ATOM(lockedStyleStates, "lockedStyleStates") GK_ATOM(apzCallbackTransform, "apzCallbackTransform") GK_ATOM(restylableAnonymousNode, "restylableAnonymousNode") GK_ATOM(paintRequestTime, "PaintRequestTime") +GK_ATOM(pseudoProperty, "PseudoProperty") // CSSPseudoElementType // Languages for lang-specific transforms GK_ATOM(Japanese, "ja") diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index c965d5b97..69643762c 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -3577,9 +3577,10 @@ nsGlobalWindow::WillHandleEvent(EventChainPostVisitor& aVisitor) } nsresult -nsGlobalWindow::PreHandleEvent(EventChainPreVisitor& aVisitor) +nsGlobalWindow::GetEventTargetParent(EventChainPreVisitor& aVisitor) { - NS_PRECONDITION(IsInnerWindow(), "PreHandleEvent is used on outer window!?"); + NS_PRECONDITION(IsInnerWindow(), + "GetEventTargetParent is used on outer window!?"); EventMessage msg = aVisitor.mEvent->mMessage; aVisitor.mCanHandle = true; @@ -3607,7 +3608,7 @@ nsGlobalWindow::PreHandleEvent(EventChainPreVisitor& aVisitor) } } - aVisitor.mParentTarget = GetParentTarget(); + aVisitor.SetParentTarget(GetParentTarget(), true); // Handle 'active' event. if (!mIdleObservers.IsEmpty() && @@ -3812,7 +3813,7 @@ nsGlobalWindow::PostHandleEvent(EventChainPostVisitor& aVisitor) } else if (aVisitor.mEvent->mMessage == eLoad && aVisitor.mEvent->IsTrusted()) { // This is page load event since load events don't propagate to |window|. - // @see nsDocument::PreHandleEvent. + // @see nsDocument::GetEventTargetParent. mIsDocumentLoaded = true; nsCOMPtr<Element> element = GetOuterWindow()->GetFrameElementInternal(); diff --git a/dom/base/nsHTMLContentSerializer.cpp b/dom/base/nsHTMLContentSerializer.cpp index c135c4cf8..ea9c5a66f 100644 --- a/dom/base/nsHTMLContentSerializer.cpp +++ b/dom/base/nsHTMLContentSerializer.cpp @@ -15,6 +15,7 @@ #include "nsIDOMElement.h" #include "nsIContent.h" #include "nsIDocument.h" +#include "nsElementTable.h" #include "nsNameSpaceManager.h" #include "nsString.h" #include "nsUnicharUtils.h" @@ -347,20 +348,13 @@ nsHTMLContentSerializer::AppendElementEnd(Element* aElement, } if (ns == kNameSpaceID_XHTML) { - nsIParserService* parserService = nsContentUtils::GetParserService(); - - if (parserService) { - bool isContainer; - - parserService-> - IsContainer(parserService->HTMLCaseSensitiveAtomTagToId(name), - isContainer); - if (!isContainer) { - // Keep this in sync with the cleanup at the end of this method. - MOZ_ASSERT(name != nsGkAtoms::body); - MaybeLeaveFromPreContent(content); - return NS_OK; - } + bool isContainer = + nsHTMLElement::IsContainer(nsHTMLTags::CaseSensitiveAtomTagToId(name)); + if (!isContainer) { + // Keep this in sync with the cleanup at the end of this method. + MOZ_ASSERT(name != nsGkAtoms::body); + MaybeLeaveFromPreContent(content); + return NS_OK; } } diff --git a/dom/base/nsIContent.h b/dom/base/nsIContent.h index dcdc632b4..ce0a4e596 100644 --- a/dom/base/nsIContent.h +++ b/dom/base/nsIContent.h @@ -26,6 +26,7 @@ namespace mozilla { class EventChainPreVisitor; namespace dom { class ShadowRoot; +class HTMLSlotElement; } // namespace dom namespace widget { struct IMEState; @@ -144,7 +145,14 @@ public: * Skip native anonymous content created for placeholder of HTML input, * used in conjunction with eAllChildren or eAllButXBL. */ - eSkipPlaceholderContent = 2 + eSkipPlaceholderContent = 2, + + /** + * Skip native anonymous content created by ancestor frames of the root + * element's primary frame, such as scrollbar elements created by the root + * scroll frame. + */ + eSkipDocumentLevelNativeAnonymousContent = 4, }; /** @@ -186,7 +194,7 @@ public: void SetIsNativeAnonymousRoot() { SetFlags(NODE_IS_ANONYMOUS_ROOT | NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE | - NODE_IS_NATIVE_ANONYMOUS_ROOT); + NODE_IS_NATIVE_ANONYMOUS_ROOT | NODE_IS_NATIVE_ANONYMOUS); } /** @@ -689,18 +697,27 @@ public: virtual mozilla::dom::ShadowRoot *GetContainingShadow() const = 0; /** - * Gets an array of destination insertion points where this content - * is distributed by web component distribution algorithms. - * The array is created if it does not already exist. + * Gets the assigned slot associated with this content. + * + * @return The assigned slot element or null. */ - virtual nsTArray<nsIContent*> &DestInsertionPoints() = 0; + virtual mozilla::dom::HTMLSlotElement* GetAssignedSlot() const = 0; /** - * Same as DestInsertionPoints except that this method will return - * null if the array of destination insertion points does not already - * exist. + * Sets the assigned slot associated with this content. + * + * @param aSlot The assigned slot. */ - virtual nsTArray<nsIContent*> *GetExistingDestInsertionPoints() const = 0; + virtual void SetAssignedSlot(mozilla::dom::HTMLSlotElement* aSlot) = 0; + + /** + * Gets the assigned slot associated with this content based on parent's + * shadow root mode. Returns null if parent's shadow root is "closed". + * https://dom.spec.whatwg.org/#dom-slotable-assignedslot + * + * @return The assigned slot element or null. + */ + mozilla::dom::HTMLSlotElement* GetAssignedSlotByMode() const; /** * Gets the insertion parent element of the XBL binding. @@ -723,10 +740,9 @@ public: */ inline nsIContent *GetFlattenedTreeParent() const; - /** - * Helper method, which we leave public so that it's accessible from nsINode. - */ - nsINode *GetFlattenedTreeParentNodeInternal() const; + // Helper method, which we leave public so that it's accessible from nsINode. + enum FlattenedParentType { eNotForStyle, eForStyle }; + nsINode* GetFlattenedTreeParentNodeInternal(FlattenedParentType aType) const; /** * API to check if this is a link that's traversed in response to user input @@ -944,10 +960,18 @@ public: return false; } + // Returns true if this element is native-anonymous scrollbar content. + bool IsNativeScrollbarContent() const { + return IsNativeAnonymous() && + IsAnyOfXULElements(nsGkAtoms::scrollbar, + nsGkAtoms::resizer, + nsGkAtoms::scrollcorner); + } + // Overloaded from nsINode virtual already_AddRefed<nsIURI> GetBaseURI(bool aTryUseXHRDocBaseURI = false) const override; - virtual nsresult PreHandleEvent( + virtual nsresult GetEventTargetParent( mozilla::EventChainPreVisitor& aVisitor) override; virtual bool IsPurple() = 0; @@ -961,6 +985,12 @@ protected: */ nsIAtom* DoGetID() const; + /** + * Returns the assigned slot, if it exists, or the direct parent, if it's a + * fallback content of a slot. + */ + nsINode* GetFlattenedTreeParentForMaybeAssignedNode() const; + public: #ifdef DEBUG /** @@ -1014,9 +1044,17 @@ inline nsIContent* nsINode::AsContent() { \ return aContent->_check ? static_cast<_class*>(aContent) : nullptr; \ } \ + static const _class* FromContent(const nsIContent* aContent) \ + { \ + return aContent->_check ? static_cast<const _class*>(aContent) : nullptr; \ + } \ static _class* FromContentOrNull(nsIContent* aContent) \ { \ return aContent ? FromContent(aContent) : nullptr; \ + } \ + static const _class* FromContentOrNull(const nsIContent* aContent) \ + { \ + return aContent ? FromContent(aContent) : nullptr; \ } #define NS_IMPL_FROMCONTENT(_class, _nsid) \ diff --git a/dom/base/nsIContentInlines.h b/dom/base/nsIContentInlines.h index 368a0422b..6a9bd7afa 100644 --- a/dom/base/nsIContentInlines.h +++ b/dom/base/nsIContentInlines.h @@ -33,14 +33,15 @@ inline mozilla::dom::ShadowRoot* nsIContent::GetShadowRoot() const return AsElement()->FastGetShadowRoot(); } -inline nsINode* nsINode::GetFlattenedTreeParentNode() const +template<nsIContent::FlattenedParentType Type> +static inline nsINode* +GetFlattenedTreeParentNode(const nsINode* aNode) { - nsINode* parent = GetParentNode(); - + nsINode* parent = aNode->GetParentNode(); // Try to short-circuit past the complicated and not-exactly-fast logic for // computing the flattened parent. // - // There are three cases where we need might something other than parentNode: + // There are four cases where we need might something other than parentNode: // (1) The node is an explicit child of an XBL-bound element, re-bound // to an XBL insertion point. // (2) The node is a top-level element in a shadow tree, whose flattened @@ -48,18 +49,31 @@ inline nsINode* nsINode::GetFlattenedTreeParentNode() const // is the shadow root). // (3) The node is an explicit child of an element with a shadow root, // re-bound to an insertion point. - bool needSlowCall = HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) || - IsInShadowTree() || - (parent && parent->IsContent() && - parent->AsContent()->GetShadowRoot()); + // (4) We want the flattened parent for style, and the node is the root + // of a native anonymous content subtree parented to the document's + // root element. + bool needSlowCall = aNode->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) || + aNode->IsInShadowTree() || + (parent && + parent->IsContent() && + parent->AsContent()->GetShadowRoot()) || + (Type == nsIContent::eForStyle && + aNode->IsContent() && + aNode->AsContent()->IsRootOfNativeAnonymousSubtree() && + aNode->OwnerDoc()->GetRootElement() == parent); if (MOZ_UNLIKELY(needSlowCall)) { - MOZ_ASSERT(IsContent()); - return AsContent()->GetFlattenedTreeParentNodeInternal(); + MOZ_ASSERT(aNode->IsContent()); + return aNode->AsContent()->GetFlattenedTreeParentNodeInternal(Type); } - return parent; } +inline nsINode* +nsINode::GetFlattenedTreeParentNode() const +{ + return ::GetFlattenedTreeParentNode<nsIContent::eNotForStyle>(this); +} + inline nsIContent* nsIContent::GetFlattenedTreeParent() const { @@ -67,5 +81,16 @@ nsIContent::GetFlattenedTreeParent() const return (parent && parent->IsContent()) ? parent->AsContent() : nullptr; } +inline nsINode* +nsINode::GetFlattenedTreeParentNodeForStyle() const +{ + return ::GetFlattenedTreeParentNode<nsIContent::eForStyle>(this); +} + +inline bool +nsINode::NodeOrAncestorHasDirAuto() const +{ + return AncestorHasDirAuto() || (IsElement() && AsElement()->HasDirAuto()); +} #endif // nsIContentInlines_h diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index 125816c95..b4fda21c1 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -34,6 +34,7 @@ #include "prclist.h" #include "mozilla/UniquePtr.h" #include "mozilla/CORSMode.h" +#include "mozilla/dom/StyleScope.h" #include "mozilla/LinkedList.h" #include "mozilla/StyleBackendType.h" #include "mozilla/StyleSheet.h" @@ -133,7 +134,6 @@ class DOMIntersectionObserver; class DOMStringList; class Element; struct ElementCreationOptions; -struct ElementRegistrationOptions; class Event; class EventTarget; class FontFaceSet; @@ -197,7 +197,8 @@ class nsContentList; // Document interface. This is implemented by all document objects in // Gecko. -class nsIDocument : public nsINode +class nsIDocument : public nsINode, + public mozilla::dom::StyleScope { typedef mozilla::dom::GlobalObject GlobalObject; @@ -1070,40 +1071,24 @@ public: */ virtual void EnsureOnDemandBuiltInUASheet(mozilla::StyleSheet* aSheet) = 0; - /** - * Get the number of (document) stylesheets - * - * @return the number of stylesheets - * @throws no exceptions - */ - virtual int32_t GetNumberOfStyleSheets() const = 0; + nsINode& AsNode() final + { + return *this; + } - /** - * Get a particular stylesheet - * @param aIndex the index the stylesheet lives at. This is zero-based - * @return the stylesheet at aIndex. Null if aIndex is out of range. - * @throws no exceptions - */ - virtual mozilla::StyleSheet* GetStyleSheetAt(int32_t aIndex) const = 0; + mozilla::dom::StyleSheetList* StyleSheets() + { + return &StyleScope::EnsureDOMStyleSheets(); + } /** * Insert a sheet at a particular spot in the stylesheet list (zero-based) * @param aSheet the sheet to insert - * @param aIndex the index to insert at. This index will be - * adjusted for the "special" sheets. + * @param aIndex the index to insert at. * @throws no exceptions */ virtual void InsertStyleSheetAt(mozilla::StyleSheet* aSheet, - int32_t aIndex) = 0; - - /** - * Get the index of a particular stylesheet. This will _always_ - * consider the "special" sheets as part of the sheet list. - * @param aSheet the sheet to get the index of - * @return aIndex the index of the sheet in the full list - */ - virtual int32_t GetIndexOfStyleSheet( - const mozilla::StyleSheet* aSheet) const = 0; + size_t aIndex) = 0; /** * Replace the stylesheets in aOldSheets with the stylesheets in @@ -1154,11 +1139,13 @@ public: * sheets for this document, returns the index that aSheet should * be inserted at to maintain document ordering. * + * Type T has to cast to StyleSheet*. + * * Defined in nsIDocumentInlines.h. */ template<typename T> - size_t FindDocStyleSheetInsertionPoint(const nsTArray<RefPtr<T>>& aDocSheets, - T* aSheet); + size_t FindDocStyleSheetInsertionPoint(const nsTArray<T>& aDocSheets, + const mozilla::StyleSheet& aSheet); /** * Get this document's CSSLoader. This is guaranteed to not return null. @@ -2587,14 +2574,6 @@ public: nsIDocument* GetTopLevelContentDocument(); - virtual void - RegisterElement(JSContext* aCx, const nsAString& aName, - const mozilla::dom::ElementRegistrationOptions& aOptions, - JS::MutableHandle<JSObject*> aRetval, - mozilla::ErrorResult& rv) = 0; - virtual already_AddRefed<mozilla::dom::CustomElementRegistry> - GetCustomElementRegistry() = 0; - already_AddRefed<nsContentList> GetElementsByTagName(const nsAString& aTagName) { @@ -2706,7 +2685,6 @@ public: return mVisibilityState; } #endif - virtual mozilla::dom::StyleSheetList* StyleSheets() = 0; void GetSelectedStyleSheetSet(nsAString& aSheetSet); virtual void SetSelectedStyleSheetSet(const nsAString& aSheetSet) = 0; virtual void GetLastStyleSheetSet(nsString& aSheetSet) = 0; @@ -2894,6 +2872,11 @@ public: --mThrowOnDynamicMarkupInsertionCounter; } + bool IsWebComponentsEnabled() const + { + return mIsWebComponentsEnabled; + } + protected: bool GetUseCounter(mozilla::UseCounter aUseCounter) { @@ -3037,6 +3020,9 @@ protected: // container for per-context fonts (downloadable, SVG, etc.) RefPtr<mozilla::dom::FontFaceSet> mFontFaceSet; + // True if dom.webcomponents.enabled pref is set when document is created. + bool mIsWebComponentsEnabled : 1; + // Compatibility mode nsCompatibility mCompatMode; diff --git a/dom/base/nsIDocumentInlines.h b/dom/base/nsIDocumentInlines.h index 4ba21dfe6..5b95a1bd3 100644 --- a/dom/base/nsIDocumentInlines.h +++ b/dom/base/nsIDocumentInlines.h @@ -19,24 +19,23 @@ nsIDocument::GetBodyElement() template<typename T> size_t nsIDocument::FindDocStyleSheetInsertionPoint( - const nsTArray<RefPtr<T>>& aDocSheets, - T* aSheet) + const nsTArray<T>& aDocSheets, + const mozilla::StyleSheet& aSheet) { nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance(); // lowest index first - int32_t newDocIndex = GetIndexOfStyleSheet(aSheet); - - int32_t count = aDocSheets.Length(); - int32_t index; - for (index = 0; index < count; index++) { - T* sheet = aDocSheets[index]; - int32_t sheetDocIndex = GetIndexOfStyleSheet(sheet); + int32_t newDocIndex = IndexOfSheet(aSheet); + + size_t count = aDocSheets.Length(); + size_t index = 0; + for (; index < count; index++) { + auto* sheet = static_cast<mozilla::StyleSheet*>(aDocSheets[index]); + MOZ_ASSERT(sheet); + int32_t sheetDocIndex = IndexOfSheet(*sheet); if (sheetDocIndex > newDocIndex) break; - mozilla::StyleSheet* sheetHandle = sheet; - // If the sheet is not owned by the document it can be an author // sheet registered at nsStyleSheetService or an additional author // sheet on the document, which means the new @@ -44,11 +43,11 @@ nsIDocument::FindDocStyleSheetInsertionPoint( if (sheetDocIndex < 0) { if (sheetService) { auto& authorSheets = *sheetService->AuthorStyleSheets(); - if (authorSheets.IndexOf(sheetHandle) != authorSheets.NoIndex) { + if (authorSheets.IndexOf(sheet) != authorSheets.NoIndex) { break; } } - if (sheetHandle == GetFirstAdditionalAuthorSheet()) { + if (sheet == GetFirstAdditionalAuthorSheet()) { break; } } diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp index 212110b72..d0cbdb454 100644 --- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp @@ -1243,7 +1243,7 @@ nsINode::RemoveEventListener(const nsAString& aType, NS_IMPL_REMOVE_SYSTEM_EVENT_LISTENER(nsINode) nsresult -nsINode::PreHandleEvent(EventChainPreVisitor& aVisitor) +nsINode::GetEventTargetParent(EventChainPreVisitor& aVisitor) { // This is only here so that we can use the NS_DECL_NSIDOMTARGET macro NS_ABORT(); @@ -1515,7 +1515,6 @@ nsINode::SetExplicitBaseURI(nsIURI* aURI) { nsresult rv = SetProperty(nsGkAtoms::baseURIProperty, aURI, ReleaseURI); if (NS_SUCCEEDED(rv)) { - SetHasExplicitBaseURI(); NS_ADDREF(aURI); } return rv; @@ -3133,3 +3132,9 @@ nsINode::IsStyledByServo() const return OwnerDoc()->IsStyledByServo(); } #endif + +DocGroup* +nsINode::GetDocGroup() const +{ + return OwnerDoc()->GetDocGroup(); +} diff --git a/dom/base/nsINode.h b/dom/base/nsINode.h index d82f5f899..7fb535701 100644 --- a/dom/base/nsINode.h +++ b/dom/base/nsINode.h @@ -72,6 +72,7 @@ inline bool IsSpaceCharacter(char aChar) { class AccessibleNode; struct BoxQuadOptions; struct ConvertCoordinateOptions; +class DocGroup; class DOMPoint; class DOMQuad; class DOMRectReadOnly; @@ -126,9 +127,28 @@ enum { NODE_IS_EDITABLE = NODE_FLAG_BIT(7), - // For all Element nodes, NODE_MAY_HAVE_CLASS is guaranteed to be set if the - // node in fact has a class, but may be set even if it doesn't. - NODE_MAY_HAVE_CLASS = NODE_FLAG_BIT(8), + // This node was created by layout as native anonymous content. This + // generally corresponds to things created by nsIAnonymousContentCreator, + // though there are exceptions (svg:use content does not have this flag + // set, and any non-nsIAnonymousContentCreator callers of + // SetIsNativeAnonymousRoot also get this flag). + // + // One very important aspect here is that this node is not transitive over + // the subtree (if you want that, use NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE). + // If Gecko code somewhere attaches children to a node with this bit set, + // the children will not have the bit themselves unless the calling code sets + // it explicitly. This means that XBL content bound to NAC doesn't get this + // bit, nor do nodes inserted by editor. + // + // For now, this bit exists primarily to control style inheritance behavior, + // since the nodes for which we set it are often used to implement pseudo- + // elements, which need to inherit style from a script-visible element. + // + // A more general principle for this bit might be this: If the node is entirely + // a detail of layout, is not script-observable in any way, and other engines + // might accomplish the same task with a nodeless layout frame, then the node + // should have this bit set. + NODE_IS_NATIVE_ANONYMOUS = NODE_FLAG_BIT(8), // Whether the node participates in a shadow tree. NODE_IS_IN_SHADOW_TREE = NODE_FLAG_BIT(9), @@ -280,6 +300,7 @@ class nsINode : public mozilla::dom::EventTarget public: typedef mozilla::dom::BoxQuadOptions BoxQuadOptions; typedef mozilla::dom::ConvertCoordinateOptions ConvertCoordinateOptions; + typedef mozilla::dom::DocGroup DocGroup; typedef mozilla::dom::DOMPoint DOMPoint; typedef mozilla::dom::DOMPointInit DOMPointInit; typedef mozilla::dom::DOMQuad DOMQuad; @@ -389,6 +410,12 @@ public: */ virtual bool IsNodeOfType(uint32_t aFlags) const = 0; + bool + IsSlotable() const + { + return IsElement() || IsNodeOfType(eTEXT); + } + virtual JSObject* WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override; /** @@ -589,6 +616,11 @@ public: } /** + * Returns the DocGroup of the "node document" of this node. + */ + DocGroup* GetDocGroup() const; + + /** * Print a debugger friendly descriptor of this element. This will describe * the position of this element in the document. */ @@ -921,6 +953,14 @@ public: inline nsINode* GetFlattenedTreeParentNode() const; /** + * Like GetFlattenedTreeParentNode, but returns null for any native + * anonymous content that was generated for ancestor frames of the + * root element's primary frame, such as scrollbar elements created + * by the root scroll frame. + */ + inline nsINode* GetFlattenedTreeParentNodeForStyle() const; + + /** * Get the parent nsINode for this node if it is an Element. * @return the parent node */ @@ -1204,6 +1244,15 @@ public: } /** + * Returns true if |this| is native anonymous (i.e. created by + * nsIAnonymousContentCreator); + */ + bool IsNativeAnonymous() const + { + return HasFlag(NODE_IS_NATIVE_ANONYMOUS); + } + + /** * Returns true if |this| or any of its ancestors is native anonymous. */ bool IsInNativeAnonymousSubtree() const @@ -1335,10 +1384,11 @@ public: protected: nsIURI* GetExplicitBaseURI() const { - if (HasExplicitBaseURI()) { - return static_cast<nsIURI*>(GetProperty(nsGkAtoms::baseURIProperty)); + if (!HasProperties()) { + return nullptr; } - return nullptr; + + return static_cast<nsIURI*>(GetProperty(nsGkAtoms::baseURIProperty)); } public: @@ -1541,6 +1591,8 @@ private: // cases lie for nsXMLElement, such as when the node has been moved between // documents with different id mappings. ElementHasID, + // Set if the element might have a class. + ElementMayHaveClass, // Set if the element might have inline style. ElementMayHaveStyle, // Set if the element has a name attribute set. @@ -1559,8 +1611,6 @@ private: // Maybe set if the node is a root of a subtree // which needs to be kept in the purple buffer. NodeIsPurpleRoot, - // Set if the node has an explicit base URI stored - NodeHasExplicitBaseURI, // Set if the element has some style states locked ElementHasLockedStyleStates, // Set if element has pointer locked @@ -1571,10 +1621,11 @@ private: NodeIsContent, // Set if the node has animations or transitions ElementHasAnimations, - // Set if node has a dir attribute with a valid value (ltr, rtl, or auto) + // Set if node has a dir attribute with a valid value (ltr, rtl, or auto). + // Note that we cannot compute this from the dir attribute event state + // flags, because we can't use those to distinguish + // <bdi dir="some-invalid-value"> and <bdi dir="auto">. NodeHasValidDirAttribute, - // Set if node has a dir attribute with a fixed value (ltr or rtl, NOT auto) - NodeHasFixedDir, // Set if the node has dir=auto and has a property pointing to the text // node that determines its direction NodeHasDirAutoSet, @@ -1582,8 +1633,6 @@ private: // and has a TextNodeDirectionalityMap property listing the elements whose // direction it determines. NodeHasTextNodeDirectionalityMap, - // Set if the node has dir=auto. - NodeHasDirAuto, // Set if a node in the node's parent chain has dir=auto. NodeAncestorHasDirAuto, // Set if the element is in the scope of a scoped style sheet; this flag is @@ -1637,6 +1686,8 @@ public: { SetBoolFlag(NodeHasRenderingObservers, aValue); } bool IsContent() const { return GetBoolFlag(NodeIsContent); } bool HasID() const { return GetBoolFlag(ElementHasID); } + bool MayHaveClass() const { return GetBoolFlag(ElementMayHaveClass); } + void SetMayHaveClass() { SetBoolFlag(ElementMayHaveClass); } bool MayHaveStyle() const { return GetBoolFlag(ElementMayHaveStyle); } bool HasName() const { return GetBoolFlag(ElementHasName); } bool MayHaveContentEditableAttr() const @@ -1676,17 +1727,6 @@ public: void SetHasValidDir() { SetBoolFlag(NodeHasValidDirAttribute); } void ClearHasValidDir() { ClearBoolFlag(NodeHasValidDirAttribute); } bool HasValidDir() const { return GetBoolFlag(NodeHasValidDirAttribute); } - void SetHasFixedDir() { - MOZ_ASSERT(NodeType() != nsIDOMNode::TEXT_NODE, - "SetHasFixedDir on text node"); - SetBoolFlag(NodeHasFixedDir); - } - void ClearHasFixedDir() { - MOZ_ASSERT(NodeType() != nsIDOMNode::TEXT_NODE, - "ClearHasFixedDir on text node"); - ClearBoolFlag(NodeHasFixedDir); - } - bool HasFixedDir() const { return GetBoolFlag(NodeHasFixedDir); } void SetHasDirAutoSet() { MOZ_ASSERT(NodeType() != nsIDOMNode::TEXT_NODE, "SetHasDirAutoSet on text node"); @@ -1715,16 +1755,12 @@ public: return GetBoolFlag(NodeHasTextNodeDirectionalityMap); } - void SetHasDirAuto() { SetBoolFlag(NodeHasDirAuto); } - void ClearHasDirAuto() { ClearBoolFlag(NodeHasDirAuto); } - bool HasDirAuto() const { return GetBoolFlag(NodeHasDirAuto); } - void SetAncestorHasDirAuto() { SetBoolFlag(NodeAncestorHasDirAuto); } void ClearAncestorHasDirAuto() { ClearBoolFlag(NodeAncestorHasDirAuto); } bool AncestorHasDirAuto() const { return GetBoolFlag(NodeAncestorHasDirAuto); } - bool NodeOrAncestorHasDirAuto() const - { return HasDirAuto() || AncestorHasDirAuto(); } + // Implemented in nsIContentInlines.h. + inline bool NodeOrAncestorHasDirAuto() const; void SetIsElementInStyleScope(bool aValue) { MOZ_ASSERT(IsElement(), "SetIsInStyleScope on a non-Element node"); @@ -1766,8 +1802,6 @@ protected: void ClearHasName() { ClearBoolFlag(ElementHasName); } void SetMayHaveContentEditableAttr() { SetBoolFlag(ElementMayHaveContentEditableAttr); } - bool HasExplicitBaseURI() const { return GetBoolFlag(NodeHasExplicitBaseURI); } - void SetHasExplicitBaseURI() { SetBoolFlag(NodeHasExplicitBaseURI); } void SetHasLockedStyleStates() { SetBoolFlag(ElementHasLockedStyleStates); } void ClearHasLockedStyleStates() { ClearBoolFlag(ElementHasLockedStyleStates); } bool HasLockedStyleStates() const diff --git a/dom/base/nsInProcessTabChildGlobal.cpp b/dom/base/nsInProcessTabChildGlobal.cpp index 10ccf4aec..9885b41a8 100644 --- a/dom/base/nsInProcessTabChildGlobal.cpp +++ b/dom/base/nsInProcessTabChildGlobal.cpp @@ -97,7 +97,7 @@ nsInProcessTabChildGlobal::nsInProcessTabChildGlobal(nsIDocShell* aShell, mozilla::HoldJSObjects(this); // If owner corresponds to an <iframe mozbrowser> or <iframe mozapp>, we'll - // have to tweak our PreHandleEvent implementation. + // GetEventTargetParent implementation. nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwner); if (browserFrame) { mIsBrowserOrAppFrame = browserFrame->GetReallyIsBrowserOrApp(); @@ -251,7 +251,7 @@ nsInProcessTabChildGlobal::GetOwnerContent() } nsresult -nsInProcessTabChildGlobal::PreHandleEvent(EventChainPreVisitor& aVisitor) +nsInProcessTabChildGlobal::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.mForceContentDispatch = true; aVisitor.mCanHandle = true; @@ -270,7 +270,7 @@ nsInProcessTabChildGlobal::PreHandleEvent(EventChainPreVisitor& aVisitor) #endif if (mPreventEventsEscaping) { - aVisitor.mParentTarget = nullptr; + aVisitor.SetParentTarget(nullptr, false); return NS_OK; } @@ -278,11 +278,13 @@ nsInProcessTabChildGlobal::PreHandleEvent(EventChainPreVisitor& aVisitor) (!mOwner || !nsContentUtils::IsInChromeDocshell(mOwner->OwnerDoc()))) { if (mOwner) { if (nsPIDOMWindowInner* innerWindow = mOwner->OwnerDoc()->GetInnerWindow()) { - aVisitor.mParentTarget = innerWindow->GetParentTarget(); + // 'this' is already a "chrome handler", so we consider window's + // parent target to be part of that same part of the event path. + aVisitor.SetParentTarget(innerWindow->GetParentTarget(), false); } } } else { - aVisitor.mParentTarget = mOwner; + aVisitor.SetParentTarget(mOwner, false); } return NS_OK; diff --git a/dom/base/nsInProcessTabChildGlobal.h b/dom/base/nsInProcessTabChildGlobal.h index e7dd9cb5a..55ffc5965 100644 --- a/dom/base/nsInProcessTabChildGlobal.h +++ b/dom/base/nsInProcessTabChildGlobal.h @@ -96,7 +96,7 @@ public: JS::Handle<JSObject *> aCpows, nsIPrincipal* aPrincipal) override; - virtual nsresult PreHandleEvent( + virtual nsresult GetEventTargetParent( mozilla::EventChainPreVisitor& aVisitor) override; NS_IMETHOD AddEventListener(const nsAString& aType, nsIDOMEventListener* aListener, @@ -168,7 +168,7 @@ protected: // Is this the message manager for an in-process <iframe mozbrowser> or // <iframe mozapp>? This affects where events get sent, so it affects - // PreHandleEvent. + // GetEventTargetParent. bool mIsBrowserOrAppFrame; bool mPreventEventsEscaping; diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index dfd380fc2..efea3ee40 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -57,6 +57,7 @@ #include "mozilla/dom/ErrorEvent.h" #include "nsAXPCNativeCallContext.h" #include "mozilla/CycleCollectedJSContext.h" +#include "mozilla/Telemetry.h" #include "nsJSPrincipals.h" diff --git a/dom/base/nsJSUtils.cpp b/dom/base/nsJSUtils.cpp index b6c843065..c24adc739 100644 --- a/dom/base/nsJSUtils.cpp +++ b/dom/base/nsJSUtils.cpp @@ -31,6 +31,7 @@ #include "mozilla/dom/Element.h" #include "mozilla/dom/ScriptSettings.h" +using namespace mozilla; using namespace mozilla::dom; bool diff --git a/dom/base/nsMappedAttributeElement.cpp b/dom/base/nsMappedAttributeElement.cpp index 1c1f8838f..d06cbf2bc 100644 --- a/dom/base/nsMappedAttributeElement.cpp +++ b/dom/base/nsMappedAttributeElement.cpp @@ -15,17 +15,19 @@ nsMappedAttributeElement::WalkContentStyleRules(nsRuleWalker* aRuleWalker) } bool -nsMappedAttributeElement::SetMappedAttribute(nsIDocument* aDocument, - nsIAtom* aName, - nsAttrValue& aValue, - nsresult* aRetval) +nsMappedAttributeElement::SetAndSwapMappedAttribute(nsIDocument* aDocument, + nsIAtom* aName, + nsAttrValue& aValue, + bool* aValueWasSet, + nsresult* aRetval) + { NS_PRECONDITION(aDocument == GetComposedDoc(), "Unexpected document"); nsHTMLStyleSheet* sheet = aDocument ? aDocument->GetAttributeStyleSheet() : nullptr; - *aRetval = mAttrsAndChildren.SetAndTakeMappedAttr(aName, aValue, - this, sheet); + *aRetval = mAttrsAndChildren.SetAndSwapMappedAttr(aName, aValue, + this, sheet, aValueWasSet); return true; } diff --git a/dom/base/nsMappedAttributeElement.h b/dom/base/nsMappedAttributeElement.h index 4668b36a1..a37af85ca 100644 --- a/dom/base/nsMappedAttributeElement.h +++ b/dom/base/nsMappedAttributeElement.h @@ -39,10 +39,11 @@ public: nsRuleData* aRuleData); NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker) override; - virtual bool SetMappedAttribute(nsIDocument* aDocument, - nsIAtom* aName, - nsAttrValue& aValue, - nsresult* aRetval) override; + virtual bool SetAndSwapMappedAttribute(nsIDocument* aDocument, + nsIAtom* aName, + nsAttrValue& aValue, + bool* aValueWasSet, + nsresult* aRetval) override; }; #endif // NS_MAPPEDATTRIBUTEELEMENT_H_ diff --git a/dom/base/nsMappedAttributes.cpp b/dom/base/nsMappedAttributes.cpp index a3accd2a7..05ad42310 100644 --- a/dom/base/nsMappedAttributes.cpp +++ b/dom/base/nsMappedAttributes.cpp @@ -62,11 +62,17 @@ nsMappedAttributes::Clone(bool aWillAddAttr) void* nsMappedAttributes::operator new(size_t aSize, uint32_t aAttrCount) CPP_THROW_NEW { - NS_ASSERTION(aAttrCount > 0, "zero-attribute nsMappedAttributes requested"); + size_t size = aSize + aAttrCount * sizeof(InternalAttr); // aSize will include the mAttrs buffer so subtract that. - void* newAttrs = ::operator new(aSize - sizeof(void*[1]) + - aAttrCount * sizeof(InternalAttr)); + // We don't want to under-allocate, however, so do not subtract + // if we have zero attributes. The zero attribute case only happens + // for <body>'s mapped attributes + if (aAttrCount != 0) { + size -= sizeof(void*[1]); + } + + void* newAttrs = ::operator new(size); #ifdef DEBUG static_cast<nsMappedAttributes*>(newAttrs)->mBufferSize = aAttrCount; @@ -79,15 +85,16 @@ NS_IMPL_ISUPPORTS(nsMappedAttributes, nsIStyleRule) void -nsMappedAttributes::SetAndTakeAttr(nsIAtom* aAttrName, nsAttrValue& aValue) +nsMappedAttributes::SetAndSwapAttr(nsIAtom* aAttrName, nsAttrValue& aValue, + bool* aValueWasSet) { NS_PRECONDITION(aAttrName, "null name"); - + *aValueWasSet = false; uint32_t i; for (i = 0; i < mAttrCount && !Attrs()[i].mName.IsSmaller(aAttrName); ++i) { if (Attrs()[i].mName.Equals(aAttrName)) { - Attrs()[i].mValue.Reset(); Attrs()[i].mValue.SwapValueWith(aValue); + *aValueWasSet = true; return; } } diff --git a/dom/base/nsMappedAttributes.h b/dom/base/nsMappedAttributes.h index f00b888b9..0c24fd973 100644 --- a/dom/base/nsMappedAttributes.h +++ b/dom/base/nsMappedAttributes.h @@ -33,7 +33,8 @@ public: NS_DECL_ISUPPORTS - void SetAndTakeAttr(nsIAtom* aAttrName, nsAttrValue& aValue); + void SetAndSwapAttr(nsIAtom* aAttrName, nsAttrValue& aValue, + bool* aValueWasSet); const nsAttrValue* GetAttr(nsIAtom* aAttrName) const; const nsAttrValue* GetAttr(const nsAString& aAttrName) const; diff --git a/dom/base/nsNodeUtils.cpp b/dom/base/nsNodeUtils.cpp index 384e56cde..5fdd24e43 100644 --- a/dom/base/nsNodeUtils.cpp +++ b/dom/base/nsNodeUtils.cpp @@ -65,7 +65,7 @@ using mozilla::AutoJSContext; } \ ShadowRoot* shadow = ShadowRoot::FromNode(node); \ if (shadow) { \ - node = shadow->GetPoolHost(); \ + node = shadow->GetHost(); \ } else { \ node = node->GetParentNode(); \ } \ @@ -93,7 +93,7 @@ using mozilla::AutoJSContext; } \ ShadowRoot* shadow = ShadowRoot::FromNode(node); \ if (shadow) { \ - node = shadow->GetPoolHost(); \ + node = shadow->GetHost(); \ } else { \ node = node->GetParentNode(); \ } \ diff --git a/dom/base/nsRange.cpp b/dom/base/nsRange.cpp index d45a2c975..82e28f645 100644 --- a/dom/base/nsRange.cpp +++ b/dom/base/nsRange.cpp @@ -49,6 +49,12 @@ nsRange::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) return RangeBinding::Wrap(aCx, this, aGivenProto); } +DocGroup* +nsRange::GetDocGroup() const +{ + return mOwner ? mOwner->GetDocGroup() : nullptr; +} + /****************************************************** * stack based utilty class for managing monitor ******************************************************/ @@ -111,31 +117,37 @@ nsRange::CompareNodeToRange(nsINode* aNode, nsRange* aRange, // so instead represent it by (node,0) and (node,numChildren) parent = aNode; nodeStart = 0; - nodeEnd = aNode->GetChildCount(); + uint32_t childCount = aNode->GetChildCount(); + MOZ_ASSERT(childCount <= INT32_MAX, + "There shouldn't be over INT32_MAX children"); + nodeEnd = static_cast<int32_t>(childCount); } else { nodeStart = parent->IndexOf(aNode); nodeEnd = nodeStart + 1; + MOZ_ASSERT(nodeStart < nodeEnd, "nodeStart shouldn't be INT32_MAX"); } nsINode* rangeStartParent = aRange->GetStartParent(); nsINode* rangeEndParent = aRange->GetEndParent(); - int32_t rangeStartOffset = aRange->StartOffset(); - int32_t rangeEndOffset = aRange->EndOffset(); + uint32_t rangeStartOffset = aRange->StartOffset(); + uint32_t rangeEndOffset = aRange->EndOffset(); // is RANGE(start) <= NODE(start) ? bool disconnected = false; - *outNodeBefore = nsContentUtils::ComparePoints(rangeStartParent, - rangeStartOffset, - parent, nodeStart, - &disconnected) > 0; + *outNodeBefore = + nsContentUtils::ComparePoints(rangeStartParent, + static_cast<int32_t>(rangeStartOffset), + parent, nodeStart, + &disconnected) > 0; NS_ENSURE_TRUE(!disconnected, NS_ERROR_DOM_WRONG_DOCUMENT_ERR); // is RANGE(end) >= NODE(end) ? - *outNodeAfter = nsContentUtils::ComparePoints(rangeEndParent, - rangeEndOffset, - parent, nodeEnd, - &disconnected) < 0; + *outNodeAfter = + nsContentUtils::ComparePoints(rangeEndParent, + static_cast<int32_t>(rangeEndOffset), + parent, nodeEnd, + &disconnected) < 0; NS_ENSURE_TRUE(!disconnected, NS_ERROR_DOM_WRONG_DOCUMENT_ERR); return NS_OK; } @@ -164,13 +176,17 @@ struct IsItemInRangeComparator int operator()(const nsRange* const aRange) const { - int32_t cmp = nsContentUtils::ComparePoints(mNode, mEndOffset, - aRange->GetStartParent(), - aRange->StartOffset()); + int32_t cmp = + nsContentUtils::ComparePoints( + mNode, static_cast<int32_t>(mEndOffset), + aRange->GetStartParent(), + static_cast<int32_t>(aRange->StartOffset())); if (cmp == 1) { - cmp = nsContentUtils::ComparePoints(mNode, mStartOffset, - aRange->GetEndParent(), - aRange->EndOffset()); + cmp = + nsContentUtils::ComparePoints( + mNode, static_cast<int32_t>(mStartOffset), + aRange->GetEndParent(), + static_cast<int32_t>(aRange->EndOffset())); if (cmp == -1) { return 0; } @@ -266,48 +282,38 @@ nsRange::nsRange(nsINode* aNode) /* static */ nsresult -nsRange::CreateRange(nsINode* aStartParent, int32_t aStartOffset, - nsINode* aEndParent, int32_t aEndOffset, +nsRange::CreateRange(nsINode* aStartParent, uint32_t aStartOffset, + nsINode* aEndParent, uint32_t aEndOffset, nsRange** aRange) { - nsCOMPtr<nsIDOMNode> startDomNode = do_QueryInterface(aStartParent); - nsCOMPtr<nsIDOMNode> endDomNode = do_QueryInterface(aEndParent); - - nsresult rv = CreateRange(startDomNode, aStartOffset, endDomNode, aEndOffset, - aRange); - - return rv; + MOZ_ASSERT(aRange); + *aRange = nullptr; + RefPtr<nsRange> range = new nsRange(aStartParent); + nsresult rv = range->SetStartAndEnd(aStartParent, aStartOffset, + aEndParent, aEndOffset); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + range.forget(aRange); + return NS_OK; } /* static */ nsresult -nsRange::CreateRange(nsIDOMNode* aStartParent, int32_t aStartOffset, - nsIDOMNode* aEndParent, int32_t aEndOffset, +nsRange::CreateRange(nsIDOMNode* aStartParent, uint32_t aStartOffset, + nsIDOMNode* aEndParent, uint32_t aEndOffset, nsRange** aRange) { - MOZ_ASSERT(aRange); - *aRange = nullptr; - nsCOMPtr<nsINode> startParent = do_QueryInterface(aStartParent); - NS_ENSURE_ARG_POINTER(startParent); - - RefPtr<nsRange> range = new nsRange(startParent); - - nsresult rv = range->SetStart(startParent, aStartOffset); - NS_ENSURE_SUCCESS(rv, rv); - - rv = range->SetEnd(aEndParent, aEndOffset); - NS_ENSURE_SUCCESS(rv, rv); - - range.forget(aRange); - return NS_OK; + nsCOMPtr<nsINode> endParent = do_QueryInterface(aEndParent); + return CreateRange(startParent, aStartOffset, endParent, aEndOffset, aRange); } /* static */ nsresult -nsRange::CreateRange(nsIDOMNode* aStartParent, int32_t aStartOffset, - nsIDOMNode* aEndParent, int32_t aEndOffset, +nsRange::CreateRange(nsIDOMNode* aStartParent, uint32_t aStartOffset, + nsIDOMNode* aEndParent, uint32_t aEndOffset, nsIDOMRange** aRange) { RefPtr<nsRange> range; @@ -465,15 +471,27 @@ nsRange::CharacterDataChanged(nsIDocument* aDocument, // again (when the new text node is notified). nsINode* parentNode = aContent->GetParentNode(); int32_t index = -1; - if (parentNode == mEndParent && mEndOffset > 0 && - (index = parentNode->IndexOf(aContent)) + 1 == mEndOffset) { - ++mEndOffset; - mEndOffsetWasIncremented = true; + if (parentNode == mEndParent && mEndOffset > 0) { + index = parentNode->IndexOf(aContent); + NS_WARNING_ASSERTION(index >= 0, + "Shouldn't be called during removing the node or something"); + if (static_cast<uint32_t>(index + 1) == mEndOffset) { + newEndNode = mEndParent; + newEndOffset = mEndOffset + 1; + MOZ_ASSERT(IsValidOffset(newEndOffset)); + mEndOffsetWasIncremented = true; + } } - if (parentNode == mStartParent && mStartOffset > 0 && - (index != -1 ? index : parentNode->IndexOf(aContent)) + 1 == mStartOffset) { - ++mStartOffset; - mStartOffsetWasIncremented = true; + if (parentNode == mStartParent && mStartOffset > 0) { + if (index <= 0) { + index = parentNode->IndexOf(aContent); + } + if (static_cast<uint32_t>(index + 1) == mStartOffset) { + newStartNode = mStartParent; + newStartOffset = mStartOffset + 1; + MOZ_ASSERT(IsValidOffset(newStartOffset)); + mStartOffsetWasIncremented = true; + } } #ifdef DEBUG if (mStartOffsetWasIncremented || mEndOffsetWasIncremented) { @@ -486,16 +504,15 @@ nsRange::CharacterDataChanged(nsIDocument* aDocument, // If the changed node contains our start boundary and the change starts // before the boundary we'll need to adjust the offset. - if (aContent == mStartParent && - aInfo->mChangeStart < static_cast<uint32_t>(mStartOffset)) { + if (aContent == mStartParent && aInfo->mChangeStart < mStartOffset) { if (aInfo->mDetails) { // splitText(), aInfo->mDetails->mNextSibling is the new text node NS_ASSERTION(aInfo->mDetails->mType == CharacterDataChangeInfo::Details::eSplit, "only a split can start before the end"); - NS_ASSERTION(static_cast<uint32_t>(mStartOffset) <= aInfo->mChangeEnd + 1, + NS_ASSERTION(mStartOffset <= aInfo->mChangeEnd + 1, "mStartOffset is beyond the end of this node"); - newStartOffset = static_cast<uint32_t>(mStartOffset) - aInfo->mChangeStart; + newStartOffset = mStartOffset - aInfo->mChangeStart; newStartNode = aInfo->mDetails->mNextSibling; if (MOZ_UNLIKELY(aContent == mRoot)) { newRoot = IsValidBoundary(newStartNode); @@ -512,7 +529,7 @@ nsRange::CharacterDataChanged(nsIDocument* aDocument, } else { // If boundary is inside changed text, position it before change // else adjust start offset for the change in length. - mStartOffset = static_cast<uint32_t>(mStartOffset) <= aInfo->mChangeEnd ? + mStartOffset = mStartOffset <= aInfo->mChangeEnd ? aInfo->mChangeStart : mStartOffset + aInfo->mChangeStart - aInfo->mChangeEnd + aInfo->mReplaceLength; @@ -522,16 +539,15 @@ nsRange::CharacterDataChanged(nsIDocument* aDocument, // Do the same thing for the end boundary, except for splitText of a node // with no parent then only switch to the new node if the start boundary // did so too (otherwise the range would end up with disconnected nodes). - if (aContent == mEndParent && - aInfo->mChangeStart < static_cast<uint32_t>(mEndOffset)) { + if (aContent == mEndParent && aInfo->mChangeStart < mEndOffset) { if (aInfo->mDetails && (aContent->GetParentNode() || newStartNode)) { // splitText(), aInfo->mDetails->mNextSibling is the new text node NS_ASSERTION(aInfo->mDetails->mType == CharacterDataChangeInfo::Details::eSplit, "only a split can start before the end"); - NS_ASSERTION(static_cast<uint32_t>(mEndOffset) <= aInfo->mChangeEnd + 1, + NS_ASSERTION(mEndOffset <= aInfo->mChangeEnd + 1, "mEndOffset is beyond the end of this node"); - newEndOffset = static_cast<uint32_t>(mEndOffset) - aInfo->mChangeStart; + newEndOffset = mEndOffset - aInfo->mChangeStart; newEndNode = aInfo->mDetails->mNextSibling; bool isCommonAncestor = IsInSelection() && mStartParent == mEndParent; @@ -544,7 +560,7 @@ nsRange::CharacterDataChanged(nsIDocument* aDocument, newEndNode->SetDescendantOfCommonAncestorForRangeInSelection(); } } else { - mEndOffset = static_cast<uint32_t>(mEndOffset) <= aInfo->mChangeEnd ? + mEndOffset = mEndOffset <= aInfo->mChangeEnd ? aInfo->mChangeStart : mEndOffset + aInfo->mChangeStart - aInfo->mChangeEnd + aInfo->mReplaceLength; @@ -557,14 +573,14 @@ nsRange::CharacterDataChanged(nsIDocument* aDocument, // that will be removed nsIContent* removed = aInfo->mDetails->mNextSibling; if (removed == mStartParent) { - newStartOffset = static_cast<uint32_t>(mStartOffset) + aInfo->mChangeStart; + newStartOffset = mStartOffset + aInfo->mChangeStart; newStartNode = aContent; if (MOZ_UNLIKELY(removed == mRoot)) { newRoot = IsValidBoundary(newStartNode); } } if (removed == mEndParent) { - newEndOffset = static_cast<uint32_t>(mEndOffset) + aInfo->mChangeStart; + newEndOffset = mEndOffset + aInfo->mChangeStart; newEndNode = aContent; if (MOZ_UNLIKELY(removed == mRoot)) { newRoot = IsValidBoundary(newEndNode); @@ -578,13 +594,13 @@ nsRange::CharacterDataChanged(nsIDocument* aDocument, // point before the first child is never affected by normalize().) nsINode* parentNode = aContent->GetParentNode(); if (parentNode == mStartParent && mStartOffset > 0 && - uint32_t(mStartOffset) < parentNode->GetChildCount() && + mStartOffset < parentNode->GetChildCount() && removed == parentNode->GetChildAt(mStartOffset)) { newStartNode = aContent; newStartOffset = aInfo->mChangeStart; } if (parentNode == mEndParent && mEndOffset > 0 && - uint32_t(mEndOffset) < parentNode->GetChildCount() && + mEndOffset < parentNode->GetChildCount() && removed == parentNode->GetChildAt(mEndOffset)) { newEndNode = aContent; newEndOffset = aInfo->mChangeEnd; @@ -649,13 +665,19 @@ nsRange::ContentInserted(nsIDocument* aDocument, nsINode* container = NODE_FROM(aContainer, aDocument); // Adjust position if a sibling was inserted. - if (container == mStartParent && aIndexInContainer < mStartOffset && + if (container == mStartParent && + (NS_WARN_IF(aIndexInContainer < 0) || + static_cast<uint32_t>(aIndexInContainer) < mStartOffset) && !mStartOffsetWasIncremented) { ++mStartOffset; + MOZ_ASSERT(IsValidOffset(mStartOffset)); } - if (container == mEndParent && aIndexInContainer < mEndOffset && + if (container == mEndParent && + (NS_WARN_IF(aIndexInContainer < 0) || + static_cast<uint32_t>(aIndexInContainer) < mEndOffset) && !mEndOffsetWasIncremented) { ++mEndOffset; + MOZ_ASSERT(IsValidOffset(mEndOffset)); } if (container->IsSelectionDescendant() && !aChild->IsDescendantOfCommonAncestorForRangeInSelection()) { @@ -694,7 +716,7 @@ nsRange::ContentRemoved(nsIDocument* aDocument, // Adjust position if a sibling was removed... if (container == mStartParent) { - if (aIndexInContainer < mStartOffset) { + if (aIndexInContainer < static_cast<int32_t>(mStartOffset)) { --mStartOffset; } } else { // ...or gravitate if an ancestor was removed. @@ -704,7 +726,7 @@ nsRange::ContentRemoved(nsIDocument* aDocument, // Do same thing for end boundry. if (container == mEndParent) { - if (aIndexInContainer < mEndOffset) { + if (aIndexInContainer < static_cast<int32_t>(mEndOffset)) { --mEndOffset; } } else if (didCheckStartParentDescendant && mStartParent == mEndParent) { @@ -763,12 +785,15 @@ nsRange::ParentChainChanged(nsIContent *aContent) * Utilities for comparing points: API from nsIDOMRange ******************************************************/ NS_IMETHODIMP -nsRange::IsPointInRange(nsIDOMNode* aParent, int32_t aOffset, bool* aResult) +nsRange::IsPointInRange(nsIDOMNode* aParent, uint32_t aOffset, bool* aResult) { nsCOMPtr<nsINode> parent = do_QueryInterface(aParent); if (!parent) { return NS_ERROR_DOM_NOT_OBJECT_ERR; } + if (NS_WARN_IF(!IsValidOffset(aOffset))) { + return NS_ERROR_DOM_INDEX_SIZE_ERR; + } ErrorResult rv; *aResult = IsPointInRange(*parent, aOffset, rv); @@ -791,7 +816,7 @@ nsRange::IsPointInRange(nsINode& aParent, uint32_t aOffset, ErrorResult& aRv) // returns -1 if point is before range, 0 if point is in range, // 1 if point is after range. NS_IMETHODIMP -nsRange::ComparePoint(nsIDOMNode* aParent, int32_t aOffset, int16_t* aResult) +nsRange::ComparePoint(nsIDOMNode* aParent, uint32_t aOffset, int16_t* aResult) { nsCOMPtr<nsINode> parent = do_QueryInterface(aParent); NS_ENSURE_TRUE(parent, NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); @@ -825,14 +850,18 @@ nsRange::ComparePoint(nsINode& aParent, uint32_t aOffset, ErrorResult& aRv) return 0; } - int32_t cmp; - if ((cmp = nsContentUtils::ComparePoints(&aParent, aOffset, - mStartParent, mStartOffset)) <= 0) { - + int32_t cmp = + nsContentUtils::ComparePoints(&aParent, + static_cast<int32_t>(aOffset), + mStartParent, + static_cast<int32_t>(mStartOffset)); + if (cmp <= 0) { return cmp; } - if (nsContentUtils::ComparePoints(mEndParent, mEndOffset, - &aParent, aOffset) == -1) { + if (nsContentUtils::ComparePoints(mEndParent, + static_cast<int32_t>(mEndOffset), + &aParent, + static_cast<int32_t>(aOffset)) == -1) { return 1; } @@ -875,12 +904,15 @@ nsRange::IntersectsNode(nsINode& aNode, ErrorResult& aRv) // Steps 6-7. // Note: if disconnected is true, ComparePoints returns 1. bool disconnected = false; - bool result = nsContentUtils::ComparePoints(mStartParent, mStartOffset, - parent, nodeIndex + 1, - &disconnected) < 0 && - nsContentUtils::ComparePoints(parent, nodeIndex, - mEndParent, mEndOffset, - &disconnected) < 0; + bool result = + nsContentUtils::ComparePoints(mStartParent, + static_cast<int32_t>(mStartOffset), + parent, nodeIndex + 1, + &disconnected) < 0 && + nsContentUtils::ComparePoints(parent, nodeIndex, + mEndParent, + static_cast<int32_t>(mEndOffset), + &disconnected) < 0; // Step 2. if (disconnected) { @@ -899,8 +931,8 @@ nsRange::IntersectsNode(nsINode& aNode, ErrorResult& aRv) // Calling DoSetRange with either parent argument null will collapse // the range to have both endpoints point to the other node void -nsRange::DoSetRange(nsINode* aStartN, int32_t aStartOffset, - nsINode* aEndN, int32_t aEndOffset, +nsRange::DoSetRange(nsINode* aStartN, uint32_t aStartOffset, + nsINode* aEndN, uint32_t aEndOffset, nsINode* aRoot, bool aNotInsertedYet) { NS_PRECONDITION((aStartN && aEndN && aRoot) || @@ -926,6 +958,8 @@ nsRange::DoSetRange(nsINode* aStartN, int32_t aStartOffset, /*For backward compatibility*/ aRoot->IsNodeOfType(nsINode::eCONTENT))), "Bad root"); + MOZ_ASSERT(IsValidOffset(aStartOffset)); + MOZ_ASSERT(IsValidOffset(aEndOffset)); if (mRoot != aRoot) { if (mRoot) { @@ -1038,7 +1072,7 @@ nsRange::GetStartContainer(ErrorResult& aRv) const } NS_IMETHODIMP -nsRange::GetStartOffset(int32_t* aStartOffset) +nsRange::GetStartOffset(uint32_t* aStartOffset) { if (!mIsPositioned) return NS_ERROR_NOT_INITIALIZED; @@ -1080,7 +1114,7 @@ nsRange::GetEndContainer(ErrorResult& aRv) const } NS_IMETHODIMP -nsRange::GetEndOffset(int32_t* aEndOffset) +nsRange::GetEndOffset(uint32_t* aEndOffset) { if (!mIsPositioned) return NS_ERROR_NOT_INITIALIZED; @@ -1137,6 +1171,15 @@ nsRange::GetCommonAncestorContainer(nsIDOMNode** aCommonParent) return rv.StealNSResult(); } +/* static */ +bool +nsRange::IsValidOffset(nsINode* aNode, uint32_t aOffset) +{ + return aNode && + IsValidOffset(aOffset) && + static_cast<size_t>(aOffset) <= aNode->Length(); +} + nsINode* nsRange::IsValidBoundary(nsINode* aNode) { @@ -1197,7 +1240,7 @@ nsRange::SetStart(nsINode& aNode, uint32_t aOffset, ErrorResult& aRv) } NS_IMETHODIMP -nsRange::SetStart(nsIDOMNode* aParent, int32_t aOffset) +nsRange::SetStart(nsIDOMNode* aParent, uint32_t aOffset) { nsCOMPtr<nsINode> parent = do_QueryInterface(aParent); if (!parent) { @@ -1210,22 +1253,24 @@ nsRange::SetStart(nsIDOMNode* aParent, int32_t aOffset) } /* virtual */ nsresult -nsRange::SetStart(nsINode* aParent, int32_t aOffset) +nsRange::SetStart(nsINode* aParent, uint32_t aOffset) { nsINode* newRoot = IsValidBoundary(aParent); if (!newRoot) { return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR; } - if (aOffset < 0 || uint32_t(aOffset) > aParent->Length()) { + if (!IsValidOffset(aParent, aOffset)) { return NS_ERROR_DOM_INDEX_SIZE_ERR; } // Collapse if not positioned yet, if positioned in another doc or // if the new start is after end. if (!mIsPositioned || newRoot != mRoot || - nsContentUtils::ComparePoints(aParent, aOffset, - mEndParent, mEndOffset) == 1) { + nsContentUtils::ComparePoints(aParent, + static_cast<int32_t>(aOffset), + mEndParent, + static_cast<int32_t>(mEndOffset)) == 1) { DoSetRange(aParent, aOffset, aParent, aOffset, newRoot); return NS_OK; @@ -1246,7 +1291,12 @@ nsRange::SetStartBefore(nsINode& aNode, ErrorResult& aRv) } AutoInvalidateSelection atEndOfBlock(this); - aRv = SetStart(aNode.GetParentNode(), IndexOf(&aNode)); + // If the node is being removed from its parent, GetContainerAndOffsetBefore() + // returns nullptr. Then, SetStart() will throw + // NS_ERROR_DOM_INVALID_NODE_TYPE_ERR. + uint32_t offset = UINT32_MAX; + nsINode* parent = GetParentAndOffsetBefore(&aNode, &offset); + aRv = SetStart(parent, offset); } NS_IMETHODIMP @@ -1272,7 +1322,12 @@ nsRange::SetStartAfter(nsINode& aNode, ErrorResult& aRv) } AutoInvalidateSelection atEndOfBlock(this); - aRv = SetStart(aNode.GetParentNode(), IndexOf(&aNode) + 1); + // If the node is being removed from its parent, GetContainerAndOffsetAfter() + // returns nullptr. Then, SetStart() will throw + // NS_ERROR_DOM_INVALID_NODE_TYPE_ERR. + uint32_t offset = UINT32_MAX; + nsINode* parent = GetParentAndOffsetAfter(&aNode, &offset); + aRv = SetStart(parent, offset); } NS_IMETHODIMP @@ -1301,7 +1356,7 @@ nsRange::SetEnd(nsINode& aNode, uint32_t aOffset, ErrorResult& aRv) } NS_IMETHODIMP -nsRange::SetEnd(nsIDOMNode* aParent, int32_t aOffset) +nsRange::SetEnd(nsIDOMNode* aParent, uint32_t aOffset) { nsCOMPtr<nsINode> parent = do_QueryInterface(aParent); if (!parent) { @@ -1314,22 +1369,24 @@ nsRange::SetEnd(nsIDOMNode* aParent, int32_t aOffset) } /* virtual */ nsresult -nsRange::SetEnd(nsINode* aParent, int32_t aOffset) +nsRange::SetEnd(nsINode* aParent, uint32_t aOffset) { nsINode* newRoot = IsValidBoundary(aParent); if (!newRoot) { return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR; } - if (aOffset < 0 || uint32_t(aOffset) > aParent->Length()) { + if (!IsValidOffset(aParent, aOffset)) { return NS_ERROR_DOM_INDEX_SIZE_ERR; } // Collapse if not positioned yet, if positioned in another doc or // if the new end is before start. if (!mIsPositioned || newRoot != mRoot || - nsContentUtils::ComparePoints(mStartParent, mStartOffset, - aParent, aOffset) == 1) { + nsContentUtils::ComparePoints(mStartParent, + static_cast<int32_t>(mStartOffset), + aParent, + static_cast<int32_t>(aOffset)) == 1) { DoSetRange(aParent, aOffset, aParent, aOffset, newRoot); return NS_OK; @@ -1340,6 +1397,66 @@ nsRange::SetEnd(nsINode* aParent, int32_t aOffset) return NS_OK; } +nsresult +nsRange::SetStartAndEnd(nsINode* aStartParent, uint32_t aStartOffset, + nsINode* aEndParent, uint32_t aEndOffset) +{ + if (NS_WARN_IF(!aStartParent) || NS_WARN_IF(!aEndParent)) { + return NS_ERROR_INVALID_ARG; + } + + nsINode* newStartRoot = IsValidBoundary(aStartParent); + if (!newStartRoot) { + return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR; + } + if (!IsValidOffset(aStartParent, aStartOffset)) { + return NS_ERROR_DOM_INDEX_SIZE_ERR; + } + + if (aStartParent == aEndParent) { + if (!IsValidOffset(aEndParent, aEndOffset)) { + return NS_ERROR_DOM_INDEX_SIZE_ERR; + } + // If the end offset is less than the start offset, this should be + // collapsed at the end offset. + if (aStartOffset > aEndOffset) { + DoSetRange(aEndParent, aEndOffset, aEndParent, aEndOffset, newStartRoot); + } else { + DoSetRange(aStartParent, aStartOffset, + aEndParent, aEndOffset, newStartRoot); + } + return NS_OK; + } + + nsINode* newEndRoot = IsValidBoundary(aEndParent); + if (!newEndRoot) { + return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR; + } + if (!IsValidOffset(aEndParent, aEndOffset)) { + return NS_ERROR_DOM_INDEX_SIZE_ERR; + } + + // If they have different root, this should be collapsed at the end point. + if (newStartRoot != newEndRoot) { + DoSetRange(aEndParent, aEndOffset, aEndParent, aEndOffset, newEndRoot); + return NS_OK; + } + + // If the end point is before the start point, this should be collapsed at + // the end point. + if (nsContentUtils::ComparePoints(aStartParent, + static_cast<int32_t>(aStartOffset), + aEndParent, + static_cast<int32_t>(aEndOffset)) == 1) { + DoSetRange(aEndParent, aEndOffset, aEndParent, aEndOffset, newEndRoot); + return NS_OK; + } + + // Otherwise, set the range as specified. + DoSetRange(aStartParent, aStartOffset, aEndParent, aEndOffset, newStartRoot); + return NS_OK; +} + void nsRange::SetEndBefore(nsINode& aNode, ErrorResult& aRv) { @@ -1350,7 +1467,12 @@ nsRange::SetEndBefore(nsINode& aNode, ErrorResult& aRv) } AutoInvalidateSelection atEndOfBlock(this); - aRv = SetEnd(aNode.GetParentNode(), IndexOf(&aNode)); + // If the node is being removed from its parent, GetContainerAndOffsetBefore() + // returns nullptr. Then, SetEnd() will throw + // NS_ERROR_DOM_INVALID_NODE_TYPE_ERR. + uint32_t offset = UINT32_MAX; + nsINode* parent = GetParentAndOffsetBefore(&aNode, &offset); + aRv = SetEnd(parent, offset); } NS_IMETHODIMP @@ -1376,7 +1498,12 @@ nsRange::SetEndAfter(nsINode& aNode, ErrorResult& aRv) } AutoInvalidateSelection atEndOfBlock(this); - aRv = SetEnd(aNode.GetParentNode(), IndexOf(&aNode) + 1); + // If the node is being removed from its parent, GetContainerAndOffsetAfter() + // returns nullptr. Then, SetEnd() will throw + // NS_ERROR_DOM_INVALID_NODE_TYPE_ERR. + uint32_t offset = UINT32_MAX; + nsINode* parent = GetParentAndOffsetAfter(&aNode, &offset); + aRv = SetEnd(parent, offset); } NS_IMETHODIMP @@ -1435,7 +1562,9 @@ nsRange::SelectNode(nsINode& aNode, ErrorResult& aRv) } int32_t index = parent->IndexOf(&aNode); - if (index < 0) { + if (NS_WARN_IF(index < 0) || + !IsValidOffset(static_cast<uint32_t>(index)) || + !IsValidOffset(static_cast<uint32_t>(index) + 1)) { aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR); return; } @@ -1884,9 +2013,9 @@ nsRange::CutContents(DocumentFragment** aFragment) // of Range gravity during our edits! nsCOMPtr<nsINode> startContainer = mStartParent; - int32_t startOffset = mStartOffset; + uint32_t startOffset = mStartOffset; nsCOMPtr<nsINode> endContainer = mEndParent; - int32_t endOffset = mEndOffset; + uint32_t endOffset = mEndOffset; if (retval) { // For extractContents(), abort early if there's a doctype (bug 719533). @@ -1897,10 +2026,12 @@ nsRange::CutContents(DocumentFragment** aFragment) RefPtr<DocumentType> doctype = commonAncestorDocument->GetDoctype(); if (doctype && - nsContentUtils::ComparePoints(startContainer, startOffset, + nsContentUtils::ComparePoints(startContainer, + static_cast<int32_t>(startOffset), doctype, 0) < 0 && nsContentUtils::ComparePoints(doctype, 0, - endContainer, endOffset) < 0) { + endContainer, + static_cast<int32_t>(endOffset)) < 0) { return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; } } @@ -1995,8 +2126,7 @@ nsRange::CutContents(DocumentFragment** aFragment) rv = charData->GetLength(&dataLength); NS_ENSURE_SUCCESS(rv, rv); - if (dataLength >= (uint32_t)startOffset) - { + if (dataLength >= startOffset) { nsMutationGuard guard; nsCOMPtr<nsIDOMCharacterData> cutNode; rv = SplitDataNode(charData, startOffset, getter_AddRefs(cutNode)); @@ -2012,22 +2142,17 @@ nsRange::CutContents(DocumentFragment** aFragment) else if (node == endContainer) { // Delete or extract everything before endOffset. - - if (endOffset >= 0) - { - nsMutationGuard guard; - nsCOMPtr<nsIDOMCharacterData> cutNode; - /* The Range spec clearly states clones get cut and original nodes - remain behind, so use false as the last parameter. - */ - rv = SplitDataNode(charData, endOffset, getter_AddRefs(cutNode), - false); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_STATE(!guard.Mutated(1) || - ValidateCurrentNode(this, iter)); - nodeToResult = do_QueryInterface(cutNode); - } - + nsMutationGuard guard; + nsCOMPtr<nsIDOMCharacterData> cutNode; + /* The Range spec clearly states clones get cut and original nodes + remain behind, so use false as the last parameter. + */ + rv = SplitDataNode(charData, endOffset, getter_AddRefs(cutNode), + false); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_STATE(!guard.Mutated(1) || + ValidateCurrentNode(this, iter)); + nodeToResult = do_QueryInterface(cutNode); handled = true; } } @@ -2037,8 +2162,7 @@ nsRange::CutContents(DocumentFragment** aFragment) if (node && node->IsElement() && ((node == endContainer && endOffset == 0) || (node == startContainer && - int32_t(node->AsElement()->GetChildCount()) == startOffset))) - { + node->AsElement()->GetChildCount() == startOffset))) { if (retval) { ErrorResult rv; nodeToResult = node->CloneNode(false, rv); @@ -2183,7 +2307,7 @@ nsRange::CompareBoundaryPoints(uint16_t aHow, nsRange& aOtherRange, } nsINode *ourNode, *otherNode; - int32_t ourOffset, otherOffset; + uint32_t ourOffset, otherOffset; switch (aHow) { case nsIDOMRange::START_TO_START: @@ -2221,8 +2345,10 @@ nsRange::CompareBoundaryPoints(uint16_t aHow, nsRange& aOtherRange, return 0; } - return nsContentUtils::ComparePoints(ourNode, ourOffset, - otherNode, otherOffset); + return nsContentUtils::ComparePoints(ourNode, + static_cast<int32_t>(ourOffset), + otherNode, + static_cast<int32_t>(otherOffset)); } /* static */ nsresult @@ -2339,8 +2465,7 @@ nsRange::CloneContents(ErrorResult& aRv) bool deepClone = !node->IsElement() || (!(node == mEndParent && mEndOffset == 0) && !(node == mStartParent && - mStartOffset == - int32_t(node->AsElement()->GetChildCount()))); + mStartOffset == node->AsElement()->GetChildCount())); // Clone the current subtree! @@ -2370,7 +2495,7 @@ nsRange::CloneContents(ErrorResult& aRv) return nullptr; } - if (dataLength > (uint32_t)mEndOffset) + if (dataLength > mEndOffset) { aRv = charData->DeleteData(mEndOffset, dataLength - mEndOffset); if (aRv.Failed()) { @@ -2528,7 +2653,7 @@ nsRange::InsertNode(nsINode& aNode, ErrorResult& aRv) return; } - int32_t tStartOffset = StartOffset(); + uint32_t tStartOffset = StartOffset(); nsCOMPtr<nsINode> tStartContainer = GetStartContainer(aRv); if (aRv.Failed()) { @@ -2589,18 +2714,20 @@ nsRange::InsertNode(nsINode& aNode, ErrorResult& aRv) // We might need to update the end to include the new node (bug 433662). // Ideally we'd only do this if needed, but it's tricky to know when it's // needed in advance (bug 765799). - int32_t newOffset; + uint32_t newOffset; if (referenceNode) { - newOffset = IndexOf(referenceNode); + int32_t indexInParent = IndexOf(referenceNode); + if (NS_WARN_IF(indexInParent < 0)) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + newOffset = static_cast<uint32_t>(indexInParent); } else { - uint32_t length; - aRv = tChildList->GetLength(&length); + aRv = tChildList->GetLength(&newOffset); if (aRv.Failed()) { return; } - - newOffset = length; } if (aNode.NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) { @@ -2956,10 +3083,15 @@ static nsresult GetPartialTextRect(nsLayoutUtils::RectCallback* aCallback, nsRange::CollectClientRectsAndText(nsLayoutUtils::RectCallback* aCollector, mozilla::dom::DOMStringList* aTextList, nsRange* aRange, - nsINode* aStartParent, int32_t aStartOffset, - nsINode* aEndParent, int32_t aEndOffset, + nsINode* aStartParent, uint32_t aStartOffset, + nsINode* aEndParent, uint32_t aEndOffset, bool aClampToEdge, bool aFlushLayout) { + // Currently, this method is called with start of end offset of nsRange. + // So, they must be between 0 - INT32_MAX. + MOZ_ASSERT(IsValidOffset(aStartOffset)); + MOZ_ASSERT(IsValidOffset(aEndOffset)); + // Hold strong pointers across the flush nsCOMPtr<nsINode> startContainer = aStartParent; nsCOMPtr<nsINode> endContainer = aEndParent; @@ -2990,13 +3122,15 @@ nsRange::CollectClientRectsAndText(nsLayoutUtils::RectCallback* aCollector, if (textFrame) { int32_t outOffset; nsIFrame* outFrame; - textFrame->GetChildFrameContainingOffset(aStartOffset, false, - &outOffset, &outFrame); + textFrame->GetChildFrameContainingOffset( + static_cast<int32_t>(aStartOffset), false, + &outOffset, &outFrame); if (outFrame) { nsIFrame* relativeTo = nsLayoutUtils::GetContainingBlockForClientRect(outFrame); nsRect r = outFrame->GetRectRelativeToSelf(); - ExtractRectFromOffset(outFrame, aStartOffset, &r, false, aClampToEdge); + ExtractRectFromOffset(outFrame, static_cast<int32_t>(aStartOffset), + &r, false, aClampToEdge); r.width = 0; r = nsLayoutUtils::TransformFrameRectToAncestor(outFrame, r, relativeTo); aCollector->AddRect(r); @@ -3015,12 +3149,14 @@ nsRange::CollectClientRectsAndText(nsLayoutUtils::RectCallback* aCollector, if (content->IsNodeOfType(nsINode::eTEXT)) { if (node == startContainer) { int32_t offset = startContainer == endContainer ? - aEndOffset : content->GetText()->GetLength(); - GetPartialTextRect(aCollector, aTextList, content, aStartOffset, offset, + static_cast<int32_t>(aEndOffset) : content->GetText()->GetLength(); + GetPartialTextRect(aCollector, aTextList, content, + static_cast<int32_t>(aStartOffset), offset, aClampToEdge, aFlushLayout); continue; } else if (node == endContainer) { - GetPartialTextRect(aCollector, aTextList, content, 0, aEndOffset, + GetPartialTextRect(aCollector, aTextList, content, + 0, static_cast<int32_t>(aEndOffset), aClampToEdge, aFlushLayout); continue; } @@ -3397,7 +3533,7 @@ ElementIsVisibleNoFlush(Element* aElement) static void AppendTransformedText(InnerTextAccumulator& aResult, nsGenericDOMDataNode* aTextNode, - int32_t aStart, int32_t aEnd) + uint32_t aStart, uint32_t aEnd) { nsIFrame* frame = aTextNode->GetPrimaryFrame(); if (!IsVisibleAndNotInReplacedElement(frame)) { @@ -3506,7 +3642,7 @@ nsRange::GetInnerTextNoFlush(DOMString& aValue, ErrorResult& aError, if (aEndParent->IsNodeOfType(nsINode::eTEXT)) { endState = AT_NODE; } else { - if (uint32_t(aEndOffset) < aEndParent->GetChildCount()) { + if (aEndOffset < aEndParent->GetChildCount()) { endNode = aEndParent->GetChildAt(aEndOffset); endState = AT_NODE; } diff --git a/dom/base/nsRange.h b/dom/base/nsRange.h index 4b35c749a..2c0c19edc 100644 --- a/dom/base/nsRange.h +++ b/dom/base/nsRange.h @@ -26,6 +26,7 @@ namespace mozilla { class ErrorResult; namespace dom { struct ClientRectsAndTexts; +class DocGroup; class DocumentFragment; class DOMRect; class DOMRectList; @@ -38,6 +39,7 @@ class nsRange final : public nsIDOMRange, public nsWrapperCache { typedef mozilla::ErrorResult ErrorResult; + typedef mozilla::dom::DocGroup DocGroup; typedef mozilla::dom::DOMRect DOMRect; typedef mozilla::dom::DOMRectList DOMRectList; @@ -46,14 +48,14 @@ class nsRange final : public nsIDOMRange, public: explicit nsRange(nsINode* aNode); - static nsresult CreateRange(nsIDOMNode* aStartParent, int32_t aStartOffset, - nsIDOMNode* aEndParent, int32_t aEndOffset, + static nsresult CreateRange(nsIDOMNode* aStartParent, uint32_t aStartOffset, + nsIDOMNode* aEndParent, uint32_t aEndOffset, nsRange** aRange); - static nsresult CreateRange(nsIDOMNode* aStartParent, int32_t aStartOffset, - nsIDOMNode* aEndParent, int32_t aEndOffset, + static nsresult CreateRange(nsIDOMNode* aStartParent, uint32_t aStartOffset, + nsIDOMNode* aEndParent, uint32_t aEndOffset, nsIDOMRange** aRange); - static nsresult CreateRange(nsINode* aStartParent, int32_t aStartOffset, - nsINode* aEndParent, int32_t aEndOffset, + static nsresult CreateRange(nsINode* aStartParent, uint32_t aStartOffset, + nsINode* aEndParent, uint32_t aEndOffset, nsRange** aRange); NS_DECL_CYCLE_COLLECTING_ISUPPORTS @@ -91,12 +93,12 @@ public: return mEndParent; } - int32_t StartOffset() const + uint32_t StartOffset() const { return mStartOffset; } - int32_t EndOffset() const + uint32_t EndOffset() const { return mEndOffset; } @@ -148,19 +150,74 @@ public: nsINode* GetCommonAncestor() const; void Reset(); - nsresult SetStart(nsINode* aParent, int32_t aOffset); - nsresult SetEnd(nsINode* aParent, int32_t aOffset); + + /** + * SetStart() and SetEnd() sets start point or end point separately. + * However, this is expensive especially when it's a range of Selection. + * When you set both start and end of a range, you should use + * SetStartAndEnd() instead. + */ + nsresult SetStart(nsINode* aParent, uint32_t aOffset); + nsresult SetEnd(nsINode* aParent, uint32_t aOffset); + already_AddRefed<nsRange> CloneRange() const; - nsresult Set(nsINode* aStartParent, int32_t aStartOffset, - nsINode* aEndParent, int32_t aEndOffset) + /** + * SetStartAndEnd() works similar to call both SetStart() and SetEnd(). + * Different from calls them separately, this does nothing if either + * the start point or the end point is invalid point. + * If the specified start point is after the end point, the range will be + * collapsed at the end point. Similarly, if they are in different root, + * the range will be collapsed at the end point. + */ + nsresult SetStartAndEnd(nsINode* aStartParent, uint32_t aStartOffset, + nsINode* aEndParent, uint32_t aEndOffset); + + /** + * CollapseTo() works similar to call both SetStart() and SetEnd() with + * same node and offset. This just calls SetStartAndParent() to set + * collapsed range at aParent and aOffset. + */ + nsresult CollapseTo(nsINode* aParent, uint32_t aOffset) { - // If this starts being hot, we may be able to optimize this a bit, - // but for now just set start and end separately. - nsresult rv = SetStart(aStartParent, aStartOffset); - NS_ENSURE_SUCCESS(rv, rv); + return SetStartAndEnd(aParent, aOffset, aParent, aOffset); + } - return SetEnd(aEndParent, aEndOffset); + /** + * Retrieves node and offset for setting start or end of a range to + * before or after aNode. + */ + static nsINode* GetParentAndOffsetAfter(nsINode* aNode, uint32_t* aOffset) + { + MOZ_ASSERT(aNode); + MOZ_ASSERT(aOffset); + *aOffset = 0; + nsINode* parentNode = aNode->GetParentNode(); + if (!parentNode) { + return nullptr; + } + int32_t indexInParent = parentNode->IndexOf(aNode); + if (NS_WARN_IF(indexInParent < 0)) { + return nullptr; + } + *aOffset = static_cast<uint32_t>(indexInParent) + 1; + return parentNode; + } + static nsINode* GetParentAndOffsetBefore(nsINode* aNode, uint32_t* aOffset) + { + MOZ_ASSERT(aNode); + MOZ_ASSERT(aOffset); + *aOffset = 0; + nsINode* parentNode = aNode->GetParentNode(); + if (!parentNode) { + return nullptr; + } + int32_t indexInParent = parentNode->IndexOf(aNode); + if (NS_WARN_IF(indexInParent < 0)) { + return nullptr; + } + *aOffset = static_cast<uint32_t>(indexInParent); + return parentNode; } NS_IMETHOD GetUsedFontFaces(nsIDOMFontFaceList** aResult); @@ -225,6 +282,7 @@ public: nsINode* GetParentObject() const { return mOwner; } virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override final; + DocGroup* GetDocGroup() const; private: // no copy's or assigns @@ -274,8 +332,8 @@ public: static void CollectClientRectsAndText(nsLayoutUtils::RectCallback* aCollector, mozilla::dom::DOMStringList* aTextList, nsRange* aRange, - nsINode* aStartParent, int32_t aStartOffset, - nsINode* aEndParent, int32_t aEndOffset, + nsINode* aStartParent, uint32_t aStartOffset, + nsINode* aEndParent, uint32_t aEndOffset, bool aClampToEdge, bool aFlushLayout); /** @@ -297,12 +355,24 @@ protected: void UnregisterCommonAncestor(nsINode* aNode); nsINode* IsValidBoundary(nsINode* aNode); + /** + * XXX nsRange should accept 0 - UINT32_MAX as offset. However, users of + * nsRange treat offset as int32_t. Additionally, some other internal + * APIs like nsINode::IndexOf() use int32_t. Therefore, nsRange should + * accept only 0 - INT32_MAX as valid offset for now. + */ + static bool IsValidOffset(uint32_t aOffset) + { + return aOffset <= INT32_MAX; + } + static bool IsValidOffset(nsINode* aNode, uint32_t aOffset); + // CharacterDataChanged set aNotInsertedYet to true to disable an assertion // and suppress re-registering a range common ancestor node since // the new text node of a splitText hasn't been inserted yet. // CharacterDataChanged does the re-registering when needed. - void DoSetRange(nsINode* aStartN, int32_t aStartOffset, - nsINode* aEndN, int32_t aEndOffset, + void DoSetRange(nsINode* aStartN, uint32_t aStartOffset, + nsINode* aEndN, uint32_t aEndOffset, nsINode* aRoot, bool aNotInsertedYet = false); /** @@ -350,8 +420,8 @@ protected: nsCOMPtr<nsINode> mStartParent; nsCOMPtr<nsINode> mEndParent; RefPtr<mozilla::dom::Selection> mSelection; - int32_t mStartOffset; - int32_t mEndOffset; + uint32_t mStartOffset; + uint32_t mEndOffset; bool mIsPositioned : 1; bool mMaySpanAnonymousSubtrees : 1; diff --git a/dom/base/nsStyledElement.cpp b/dom/base/nsStyledElement.cpp index 03d1187ab..8cd448d47 100644 --- a/dom/base/nsStyledElement.cpp +++ b/dom/base/nsStyledElement.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsStyledElement.h" +#include "mozAutoDocUpdate.h" #include "nsGkAtoms.h" #include "nsAttrValue.h" #include "nsAttrValueInlines.h" @@ -39,7 +40,6 @@ nsStyledElement::ParseAttribute(int32_t aNamespaceID, nsAttrValue& aResult) { if (aAttribute == nsGkAtoms::style && aNamespaceID == kNameSpaceID_None) { - SetMayHaveStyle(); ParseStyleAttribute(aValue, aResult, false); return true; } @@ -49,6 +49,22 @@ nsStyledElement::ParseAttribute(int32_t aNamespaceID, } nsresult +nsStyledElement::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString* aValue, bool aNotify) +{ + if (aNamespaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::style) { + if (aValue) { + SetMayHaveStyle(); + } + } + } + + return nsStyledElementBase::BeforeSetAttr(aNamespaceID, aName, aValue, + aNotify); +} + +nsresult nsStyledElement::SetInlineStyleDeclaration(DeclarationBlock* aDeclaration, const nsAString* aSerialized, bool aNotify) @@ -56,6 +72,7 @@ nsStyledElement::SetInlineStyleDeclaration(DeclarationBlock* aDeclaration, SetMayHaveStyle(); bool modification = false; nsAttrValue oldValue; + bool oldValueSet = false; bool hasListeners = aNotify && nsContentUtils::HasMutationListeners(this, @@ -75,6 +92,7 @@ nsStyledElement::SetInlineStyleDeclaration(DeclarationBlock* aDeclaration, oldValueStr); if (modification) { oldValue.SetTo(oldValueStr); + oldValueSet = true; } } else if (aNotify && IsInUncomposedDoc()) { @@ -88,9 +106,12 @@ nsStyledElement::SetInlineStyleDeclaration(DeclarationBlock* aDeclaration, static_cast<uint8_t>(nsIDOMMutationEvent::MODIFICATION) : static_cast<uint8_t>(nsIDOMMutationEvent::ADDITION); + nsIDocument* document = GetComposedDoc(); + mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify); return SetAttrAndNotify(kNameSpaceID_None, nsGkAtoms::style, nullptr, - oldValue, attrValue, modType, hasListeners, - aNotify, kDontCallAfterSetAttr); + oldValueSet ? &oldValue : nullptr, attrValue, modType, + hasListeners, aNotify, kDontCallAfterSetAttr, + document, updateBatch); } DeclarationBlock* @@ -141,7 +162,9 @@ nsStyledElement::ReparseStyleAttribute(bool aForceInDataDoc) ParseStyleAttribute(stringValue, attrValue, aForceInDataDoc); // Don't bother going through SetInlineStyleDeclaration; we don't // want to fire off mutation events or document notifications anyway - nsresult rv = mAttrsAndChildren.SetAndSwapAttr(nsGkAtoms::style, attrValue); + bool oldValueSet; + nsresult rv = mAttrsAndChildren.SetAndSwapAttr(nsGkAtoms::style, attrValue, + &oldValueSet); NS_ENSURE_SUCCESS(rv, rv); } diff --git a/dom/base/nsStyledElement.h b/dom/base/nsStyledElement.h index c4894d87f..cb6dfd54b 100644 --- a/dom/base/nsStyledElement.h +++ b/dom/base/nsStyledElement.h @@ -80,6 +80,10 @@ protected: * document. */ nsresult ReparseStyleAttribute(bool aForceInDataDoc); + + virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString* aValue, + bool aNotify) override; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsStyledElement, NS_STYLED_ELEMENT_IID) diff --git a/dom/base/nsTextNode.cpp b/dom/base/nsTextNode.cpp index 25c2d3525..b488c85b7 100644 --- a/dom/base/nsTextNode.cpp +++ b/dom/base/nsTextNode.cpp @@ -21,6 +21,7 @@ #ifdef DEBUG #include "nsRange.h" #endif +#include "nsDocument.h" using namespace mozilla; using namespace mozilla::dom; @@ -155,6 +156,12 @@ void nsTextNode::UnbindFromTree(bool aDeep, bool aNullParent) nsGenericDOMDataNode::UnbindFromTree(aDeep, aNullParent); } +bool +nsTextNode::IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject) +{ + return nsDocument::IsWebComponentsEnabled(aCx, aObject); +} + #ifdef DEBUG void nsTextNode::List(FILE* out, int32_t aIndent) const diff --git a/dom/base/nsTextNode.h b/dom/base/nsTextNode.h index 488540a82..94b8dbd1d 100644 --- a/dom/base/nsTextNode.h +++ b/dom/base/nsTextNode.h @@ -75,6 +75,10 @@ public: virtual nsIDOMNode* AsDOMNode() override { return this; } + // Need to have a copy here because including nsDocument.h in this file will + // fail to build on Windows. + static bool IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject); + #ifdef DEBUG virtual void List(FILE* out, int32_t aIndent) const override; virtual void DumpContent(FILE* out, int32_t aIndent, bool aDumpAll) const override; diff --git a/dom/base/nsWindowRoot.cpp b/dom/base/nsWindowRoot.cpp index b839a0ee5..710ecd88f 100644 --- a/dom/base/nsWindowRoot.cpp +++ b/dom/base/nsWindowRoot.cpp @@ -180,13 +180,13 @@ nsWindowRoot::GetContextForEventHandlers(nsresult* aRv) } nsresult -nsWindowRoot::PreHandleEvent(EventChainPreVisitor& aVisitor) +nsWindowRoot::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.mCanHandle = true; aVisitor.mForceContentDispatch = true; //FIXME! Bug 329119 // To keep mWindow alive aVisitor.mItemData = static_cast<nsISupports *>(mWindow); - aVisitor.mParentTarget = mParent; + aVisitor.SetParentTarget(mParent, false); return NS_OK; } diff --git a/dom/base/nsXHTMLContentSerializer.cpp b/dom/base/nsXHTMLContentSerializer.cpp index 0a39ef663..42874f02b 100755 --- a/dom/base/nsXHTMLContentSerializer.cpp +++ b/dom/base/nsXHTMLContentSerializer.cpp @@ -15,6 +15,7 @@ #include "nsIDOMElement.h" #include "nsIContent.h" #include "nsIDocument.h" +#include "nsElementTable.h" #include "nsNameSpaceManager.h" #include "nsString.h" #include "nsUnicharUtils.h" @@ -27,7 +28,6 @@ #include "nsEscape.h" #include "nsITextToSubURI.h" #include "nsCRT.h" -#include "nsIParserService.h" #include "nsContentUtils.h" #include "nsLWBrkCIID.h" #include "nsIScriptElement.h" @@ -667,18 +667,7 @@ nsXHTMLContentSerializer::LineBreakBeforeOpen(int32_t aNamespaceID, nsIAtom* aNa aName == nsGkAtoms::html) { return true; } - else { - nsIParserService* parserService = nsContentUtils::GetParserService(); - - if (parserService) { - bool res; - parserService-> - IsBlock(parserService->HTMLCaseSensitiveAtomTagToId(aName), res); - return res; - } - } - - return mAddSpace; + return nsHTMLElement::IsBlock(nsHTMLTags::CaseSensitiveAtomTagToId(aName)); } bool @@ -748,31 +737,15 @@ nsXHTMLContentSerializer::LineBreakAfterClose(int32_t aNamespaceID, nsIAtom* aNa (aName == nsGkAtoms::tr) || (aName == nsGkAtoms::th) || (aName == nsGkAtoms::td) || - (aName == nsGkAtoms::pre) || (aName == nsGkAtoms::title) || - (aName == nsGkAtoms::li) || (aName == nsGkAtoms::dt) || (aName == nsGkAtoms::dd) || - (aName == nsGkAtoms::blockquote) || (aName == nsGkAtoms::select) || (aName == nsGkAtoms::option) || - (aName == nsGkAtoms::p) || - (aName == nsGkAtoms::map) || - (aName == nsGkAtoms::div)) { + (aName == nsGkAtoms::map)) { return true; } - else { - nsIParserService* parserService = nsContentUtils::GetParserService(); - - if (parserService) { - bool res; - parserService-> - IsBlock(parserService->HTMLCaseSensitiveAtomTagToId(aName), res); - return res; - } - } - - return false; + return nsHTMLElement::IsBlock(nsHTMLTags::CaseSensitiveAtomTagToId(aName)); } diff --git a/dom/base/nsXMLContentSerializer.cpp b/dom/base/nsXMLContentSerializer.cpp index f12bb8fdc..71a675e11 100644 --- a/dom/base/nsXMLContentSerializer.cpp +++ b/dom/base/nsXMLContentSerializer.cpp @@ -19,7 +19,7 @@ #include "nsIContent.h" #include "nsIDocument.h" #include "nsIDocumentEncoder.h" -#include "nsIParserService.h" +#include "nsElementTable.h" #include "nsNameSpaceManager.h" #include "nsTextFragment.h" #include "nsString.h" @@ -994,14 +994,9 @@ ElementNeedsSeparateEndTag(Element* aElement, Element* aOriginalElement) // HTML container tags should have a separate end tag even if empty, per spec. // See // https://w3c.github.io/DOM-Parsing/#dfn-concept-xml-serialization-algorithm - bool isHTMLContainer = true; // Default in case we get no parser service. - nsIParserService* parserService = nsContentUtils::GetParserService(); - if (parserService) { - nsIAtom* localName = aElement->NodeInfo()->NameAtom(); - parserService->IsContainer( - parserService->HTMLCaseSensitiveAtomTagToId(localName), - isHTMLContainer); - } + nsIAtom* localName = aElement->NodeInfo()->NameAtom(); + bool isHTMLContainer = + nsHTMLElement::IsContainer(nsHTMLTags::CaseSensitiveAtomTagToId(localName)); return isHTMLContainer; } diff --git a/dom/base/test/chrome/chrome.ini b/dom/base/test/chrome/chrome.ini index 765bbd2df..c6ee42391 100644 --- a/dom/base/test/chrome/chrome.ini +++ b/dom/base/test/chrome/chrome.ini @@ -18,8 +18,8 @@ support-files = file_bug1209621.xul fileconstructor_file.png frame_bug814638.xul - frame_registerElement_content.html - registerElement_ep.js + frame_custom_element_content.html + custom_element_ep.js host_bug814638.xul window_nsITextInputProcessor.xul title_window.xul @@ -62,8 +62,8 @@ support-files = ../file_bug357450.js [test_bug1139964.xul] [test_bug1209621.xul] [test_cpows.xul] -[test_registerElement_content.xul] -[test_registerElement_ep.xul] +[test_custom_element_content.xul] +[test_custom_element_ep.xul] [test_domparsing.xul] [test_fileconstructor.xul] [test_fileconstructor_tempfile.xul] diff --git a/dom/base/test/chrome/registerElement_ep.js b/dom/base/test/chrome/registerElement_ep.js deleted file mode 100644 index 9189593c0..000000000 --- a/dom/base/test/chrome/registerElement_ep.js +++ /dev/null @@ -1,8 +0,0 @@ -var proto = Object.create(HTMLElement.prototype); -proto.magicNumber = 42; -proto.connectedCallback = function() { - finishTest(this.magicNumber === 42); -}; -document.registerElement("x-foo", { prototype: proto }); - -document.firstChild.appendChild(document.createElement("x-foo")); diff --git a/dom/base/test/chrome/test_registerElement_content.xul b/dom/base/test/chrome/test_registerElement_content.xul deleted file mode 100644 index bf00ed53d..000000000 --- a/dom/base/test/chrome/test_registerElement_content.xul +++ /dev/null @@ -1,52 +0,0 @@ -<?xml version="1.0"?> -<?xml-stylesheet href="chrome://global/skin" type="text/css"?> -<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" - type="text/css"?> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=1130028 ---> -<window title="Mozilla Bug 1130028" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - <script type="application/javascript" - src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> - - <!-- test results are displayed in the html:body --> - <body xmlns="http://www.w3.org/1999/xhtml"> - <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1130028" - target="_blank">Mozilla Bug 1130028</a> - <iframe onload="startTests()" id="frame" src="http://example.com/chrome/dom/base/test/chrome/frame_registerElement_content.html"></iframe> - </body> - - <!-- test code goes here --> - <script type="application/javascript"><![CDATA[ - - /** Test for Bug 1130028 **/ - var connectedCallbackCount = 0; - - // Callback should be called only once by element created in content. - function connectedCallbackCalled() { - connectedCallbackCount++; - is(connectedCallbackCount, 1, "Connected callback called, should be called once in test."); - is(this.magicNumber, 42, "Callback should be able to see the custom prototype."); - } - - function startTests() { - var frame = $("frame"); - - var c = frame.contentDocument.registerElement("x-foo"); - var elem = new c(); - is(elem.tagName, "X-FOO", "Constructor should create an x-foo element."); - - var proto = Object.create(frame.contentWindow.HTMLElement.prototype); - proto.magicNumber = 42; - proto.connectedCallback = connectedCallbackCalled; - - frame.contentDocument.registerElement("x-bar", { prototype: proto }); - is(connectedCallbackCount, 1, "Connected callback should be called by element created in content."); - - var element = frame.contentDocument.createElement("x-bar"); - is(element.magicNumber, 42, "Should be able to see the custom prototype on created element."); - } - - ]]></script> -</window> diff --git a/dom/base/test/chrome/test_registerElement_ep.xul b/dom/base/test/chrome/test_registerElement_ep.xul deleted file mode 100644 index b6a160c2e..000000000 --- a/dom/base/test/chrome/test_registerElement_ep.xul +++ /dev/null @@ -1,44 +0,0 @@ -<?xml version="1.0"?> -<?xml-stylesheet href="chrome://global/skin" type="text/css"?> -<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" - type="text/css"?> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=1130028 ---> -<window title="Mozilla Bug 1130028" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - <script type="application/javascript" - src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> - - <!-- test results are displayed in the html:body --> - <body xmlns="http://www.w3.org/1999/xhtml"> - <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1130028" - target="_blank">Mozilla Bug 1130028</a> - <iframe onload="startTests()" id="frame" src="http://example.com/chrome/dom/base/test/chrome/frame_registerElement_content.html"></iframe> - </body> - - <!-- test code goes here --> - <script type="application/javascript"><![CDATA[ - - Components.utils.import("resource://gre/modules/Services.jsm"); - - /** Test for Bug 1130028 **/ - SimpleTest.waitForExplicitFinish(); - - function finishTest(canSeePrototype) { - ok(true, "connectedCallback called when reigsterElement was called with an extended principal."); - ok(canSeePrototype, "connectedCallback should be able to see custom prototype."); - SimpleTest.finish(); - } - - function startTests() { - var frame = $("frame"); - - // Create a sandbox with an extended principal then run a script that registers a custom element in the sandbox. - var sandbox = Components.utils.Sandbox([frame.contentWindow], { sandboxPrototype: frame.contentWindow }); - sandbox.finishTest = finishTest; - Services.scriptloader.loadSubScript("chrome://mochitests/content/chrome/dom/base/test/chrome/registerElement_ep.js", sandbox); - } - - ]]></script> -</window> diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index 3dfd666f8..928727f81 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -630,7 +630,7 @@ skip-if = toolkit == 'android' #bug 904183 [test_document.all_unqualified.html] [test_document_constructor.html] [test_document_importNode_document.html] -[test_document_register.html] +[test_custom_element.html] [test_domcursor.html] [test_domparser_null_char.html] [test_domparsing.html] diff --git a/dom/base/test/test_document_register.html b/dom/base/test/test_document_register.html index 6cf15a52f..7de235108 100644 --- a/dom/base/test/test_document_register.html +++ b/dom/base/test/test_document_register.html @@ -13,8 +13,10 @@ SimpleTest.waitForExplicitFinish(); function startTests() { - var c = document.getElementById("fooframe").contentDocument.registerElement("x-foo"); - var elem = new c(); + var frame = document.getElementById("fooframe"); + class XFoo extends frame.contentWindow.HTMLElement {}; + frame.contentWindow.customElements.define("x-foo", XFoo); + var elem = new XFoo(); is(elem.tagName, "X-FOO", "Constructor should create an x-foo element."); var anotherElem = $("fooframe").contentDocument.createElement("x-foo"); diff --git a/dom/base/test/test_mutationobservers.html b/dom/base/test/test_mutationobservers.html index 7e4c99423..021bedf1c 100644 --- a/dom/base/test/test_mutationobservers.html +++ b/dom/base/test/test_mutationobservers.html @@ -612,7 +612,7 @@ function testOutsideShadowDOM() { is(records.length, 1); is(records[0].type, "attributes", "Should have got attributes"); observer.disconnect(); - then(testInsideShadowDOM); + then(testMarquee); }); m.observe(div, { attributes: true, @@ -628,32 +628,6 @@ function testOutsideShadowDOM() { div.setAttribute("foo", "bar"); } -function testInsideShadowDOM() { - var m = new M(function(records, observer) { - is(records.length, 4); - is(records[0].type, "childList"); - is(records[1].type, "attributes"); - is(records[2].type, "characterData"); - is(records[3].type, "childList"); - observer.disconnect(); - then(testMarquee); - }); - var sr = div.createShadowRoot(); - m.observe(sr, { - attributes: true, - childList: true, - characterData: true, - subtree: true - }); - - sr.innerHTML = "<div" + ">text</" + "div>"; - sr.firstChild.setAttribute("foo", "bar"); - sr.firstChild.firstChild.data = "text2"; - sr.firstChild.appendChild(document.createElement("div")); - div.setAttribute("foo", "bar2"); - -} - function testMarquee() { var m = new M(function(records, observer) { is(records.length, 1); |