diff options
author | Moonchild <moonchild@palemoon.org> | 2020-04-17 16:02:56 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-17 16:02:56 +0200 |
commit | d5102d6beafc2a2a0cec3cc3ee5f7ebde31ae7bf (patch) | |
tree | 2bfef192cbb748b675ce8308c242a376798e265d | |
parent | 5caf99795aa81e1fc145b8e937b1ee8197ed2486 (diff) | |
parent | f35aa3e15fedf3cd4ad163d60ab74a9537ca5c82 (diff) | |
download | uxp-d5102d6beafc2a2a0cec3cc3ee5f7ebde31ae7bf.tar.gz |
Merge pull request #1518 from MoonchildProductions/shadowdom-merge
Incremental shadowdom-merge
533 files changed, 8846 insertions, 11991 deletions
diff --git a/chrome/nsChromeRegistry.cpp b/chrome/nsChromeRegistry.cpp index 4bd8b4dca5..7b72d29c3a 100644 --- a/chrome/nsChromeRegistry.cpp +++ b/chrome/nsChromeRegistry.cpp @@ -436,23 +436,22 @@ nsresult nsChromeRegistry::RefreshWindow(nsPIDOMWindowOuter* aWindow) NS_ENSURE_SUCCESS(rv, rv); } - int32_t count = document->GetNumberOfStyleSheets(); + size_t count = document->SheetCount(); // Build an array of style sheets we need to reload. nsTArray<RefPtr<StyleSheet>> oldSheets(count); nsTArray<RefPtr<StyleSheet>> newSheets(count); // Iterate over the style sheets. - for (int32_t i = 0; i < count; i++) { + for (size_t i = 0; i < count; i++) { // Get the style sheet - StyleSheet* styleSheet = document->GetStyleSheetAt(i); - oldSheets.AppendElement(styleSheet); + oldSheets.AppendElement(document->SheetAt(i)); } // Iterate over our old sheets and kick off a sync load of the new // sheet if and only if it's a non-inline sheet with a chrome URL. for (StyleSheet* sheet : oldSheets) { - MOZ_ASSERT(sheet, "GetStyleSheetAt shouldn't return nullptr for " + MOZ_ASSERT(sheet, "SheetAt shouldn't return nullptr for " "in-range sheet indexes"); nsIURI* uri = sheet->GetSheetURI(); diff --git a/devtools/client/inspector/computed/test/browser_computed_pseudo-element_01.js b/devtools/client/inspector/computed/test/browser_computed_pseudo-element_01.js index 9ca5451a56..7c18bfe7bf 100644 --- a/devtools/client/inspector/computed/test/browser_computed_pseudo-element_01.js +++ b/devtools/client/inspector/computed/test/browser_computed_pseudo-element_01.js @@ -33,7 +33,7 @@ function* testTopLeft(inspector, view) { let afterElement = children.nodes[children.nodes.length - 1]; yield selectNode(afterElement, inspector); top = getComputedViewPropertyValue(view, "top"); - is(top, "50%", "The computed view shows the correct top"); + is(top, "96px", "The computed view shows the correct top"); left = getComputedViewPropertyValue(view, "left"); - is(left, "50%", "The computed view shows the correct left"); + is(left, "96px", "The computed view shows the correct left"); } diff --git a/dom/animation/EffectCompositor.cpp b/dom/animation/EffectCompositor.cpp index c88cabe90b..49da3c0332 100644 --- a/dom/animation/EffectCompositor.cpp +++ b/dom/animation/EffectCompositor.cpp @@ -356,24 +356,16 @@ EffectCompositor::GetElementToRestyle(dom::Element* aElement, return aElement; } - nsIFrame* primaryFrame = aElement->GetPrimaryFrame(); - if (!primaryFrame) { - return nullptr; - } - nsIFrame* pseudoFrame; if (aPseudoType == CSSPseudoElementType::before) { - pseudoFrame = nsLayoutUtils::GetBeforeFrame(primaryFrame); - } else if (aPseudoType == CSSPseudoElementType::after) { - pseudoFrame = nsLayoutUtils::GetAfterFrame(primaryFrame); - } else { - NS_NOTREACHED("Should not try to get the element to restyle for a pseudo " - "other that :before or :after"); - return nullptr; + return nsLayoutUtils::GetBeforePseudo(aElement); } - if (!pseudoFrame) { - return nullptr; + if (aPseudoType == CSSPseudoElementType::after) { + return nsLayoutUtils::GetAfterPseudo(aElement); } - return pseudoFrame->GetContent()->AsElement(); + + NS_NOTREACHED("Should not try to get the element to restyle for a pseudo " + "other that :before or :after"); + return nullptr; } bool diff --git a/dom/animation/KeyframeEffectReadOnly.cpp b/dom/animation/KeyframeEffectReadOnly.cpp index 639e0b2b0d..164ee02925 100644 --- a/dom/animation/KeyframeEffectReadOnly.cpp +++ b/dom/animation/KeyframeEffectReadOnly.cpp @@ -1105,19 +1105,17 @@ KeyframeEffectReadOnly::GetAnimationFrame() const return nullptr; } - nsIFrame* frame = mTarget->mElement->GetPrimaryFrame(); - if (!frame) { - return nullptr; - } - + nsIFrame* frame; if (mTarget->mPseudoType == CSSPseudoElementType::before) { - frame = nsLayoutUtils::GetBeforeFrame(frame); + frame = nsLayoutUtils::GetBeforeFrame(mTarget->mElement); } else if (mTarget->mPseudoType == CSSPseudoElementType::after) { - frame = nsLayoutUtils::GetAfterFrame(frame); + frame = nsLayoutUtils::GetAfterFrame(mTarget->mElement); } else { + frame = mTarget->mElement->GetPrimaryFrame(); MOZ_ASSERT(mTarget->mPseudoType == CSSPseudoElementType::NotPseudo, "unknown mTarget->mPseudoType"); } + if (!frame) { return nullptr; } diff --git a/dom/archivereader/ArchiveRequest.cpp b/dom/archivereader/ArchiveRequest.cpp index ec16868042..832dec3fac 100644 --- a/dom/archivereader/ArchiveRequest.cpp +++ b/dom/archivereader/ArchiveRequest.cpp @@ -69,10 +69,10 @@ ArchiveRequest::~ArchiveRequest() } nsresult -ArchiveRequest::PreHandleEvent(EventChainPreVisitor& aVisitor) +ArchiveRequest::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.mCanHandle = true; - aVisitor.mParentTarget = nullptr; + aVisitor.SetParentTarget(nullptr, false); return NS_OK; } diff --git a/dom/archivereader/ArchiveRequest.h b/dom/archivereader/ArchiveRequest.h index 3988f1aa15..3081a3e420 100644 --- a/dom/archivereader/ArchiveRequest.h +++ b/dom/archivereader/ArchiveRequest.h @@ -38,7 +38,8 @@ public: ArchiveReader* aReader); // nsIDOMEventTarget - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; public: // This is called by the DOMArchiveRequestEvent diff --git a/dom/base/AnonymousContent.cpp b/dom/base/AnonymousContent.cpp index 1df36b0483..aea923f2bd 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 fd3b59c440..b56c145956 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 71b559392b..df05ef5cb6 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 3f80030d22..39ac8b1955 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 3e22955dd3..bf98cb0236 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 82f2f570d5..7a2b4c5792 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 d8c454ae8f..19156aff75 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 ffff8dce5f..78acdc1466 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 3cde261f02..b25f0b76b6 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 051217c840..58a67b4f28 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 99452df650..2bf969d382 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 c416e50439..eacf568c9e 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 d9a6c4524c..632abb2c8b 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 30c058f0c6..5d7db1fb6b 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 5b8f627cc4..7a5a99dce2 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 3eb2a07908..3e1e2c81bf 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 68a7f0ff47..ccc233ff13 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 c8467e0365..8e9225a641 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 7820047034..3d9b80a989 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 c68bd012eb..df2cc2e24a 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 526c3c9d44..6fbe04d251 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 4edd889082..f1fa046ee4 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 8c88a0aafe..c5e75d6397 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 9fef059bca..d0c2319817 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 d33dc55f84..f164611922 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 fad391b16f..ed368e5e65 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 6769ad3e66..afcc04d9f7 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 d32a00fd30..f055dfc75e 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 c7ef1b3819..0817c8c96b 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 831987a96e..c4e56f3fbe 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 f840781348..a24c8138e0 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 0000000000..6c708a8ba1 --- /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 0000000000..c8957b069b --- /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 7274b5ea5d..42db3179dd 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 dfedc22145..ea5c33a983 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 f9fa9eec5c..c436ce98d7 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 61d97f6348..37d326dbcf 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 78c3f16dd9..f342758408 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 cb0a0ce945..a80dc2995c 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 2ffd6add5f..2bd1f019c6 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 0000000000..b6d34a1a97 --- /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 0000000000..9f09f41efd --- /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 0fb597b306..40d358b38a 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 75ddefdedd..5acb49d4ea 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 9fd27262b3..f8e4c3f184 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 f34370c43d..0ce51c52eb 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 df0dac17de..1a62a84287 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 4f7b71b193..c0d81a41e0 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 64f7485cbb..b58b0e0e3c 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 2a90df7e41..29a4a63490 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 31eb701e35..d73d1c3b68 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 4c4731c116..1f8e8f6a83 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 a8babc603a..d6bdfb3e01 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 39ff60e123..961beb50e2 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 e44e042d57..0b3f70319d 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 2ab5937ace..a923e8f708 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 b05bf827bf..7b280a188d 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 90e511dcbb..6520d905d1 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 34eb6ed383..2f5c3dc0a3 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 01c1944bec..c14087c8a7 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 2804f2d4c0..942a841025 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 e1b1f5a179..af3b186bdc 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 73463ea5e2..d237833682 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 e8818b518e..4d0114cb11 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 73a3a02b1f..35963afd7b 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 c965d5b974..69643762c5 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 c135c4cf85..ea9c5a66ff 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 dcdc632b49..ce0a4e5960 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 368a0422b1..6a9bd7afa4 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 125816c954..b4fda21c1a 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 4ba21dfe64..5b95a1bd3a 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 212110b729..d0cbdb454d 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 d82f5f8990..7fb5357016 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 10ccf4aec3..9885b41a83 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 e7dd9cb5a6..55ffc5965f 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 dfd380fc2a..efea3ee401 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 b6c8430657..c24adc7390 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 1c1f8838fa..d06cbf2bc5 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 4668b36a1a..a37af85ca8 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 a3accd2a76..05ad423103 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 f00b888b9e..0c24fd9737 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 384e56cde6..5fdd24e439 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 d45a2c9750..82e28f645e 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 4b35c749ab..2c0c19edc6 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 03d1187ab1..8cd448d47e 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 c4894d87f1..cb6dfd54b9 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 25c2d35256..b488c85b75 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 488540a826..94b8dbd1d7 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 b839a0ee5c..710ecd88f3 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 0a39ef6636..42874f02bc 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 f12bb8fdc7..71a675e11b 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 765bbd2dfb..c6ee423918 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 9189593c0a..0000000000 --- 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 bf00ed53d2..0000000000 --- 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 b6a160c2e8..0000000000 --- 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 3dfd666f80..928727f81d 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 6cf15a52f2..7de2351084 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 7e4c994235..021bedf1c8 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); diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index f76f14d95f..ee321772e6 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -23,9 +23,9 @@ #include "nsContentCreatorFunctions.h" #include "nsContentUtils.h" #include "nsGlobalWindow.h" +#include "nsHTMLTags.h" #include "nsIDocShell.h" #include "nsIDOMGlobalPropertyInitializer.h" -#include "nsIParserService.h" #include "nsIPermissionManager.h" #include "nsIPrincipal.h" #include "nsIXPConnect.h" @@ -3406,28 +3406,6 @@ GetDesiredProto(JSContext* aCx, const JS::CallArgs& aCallArgs, return true; } -CustomElementReactionsStack* -GetCustomElementReactionsStack(JS::Handle<JSObject*> aObj) -{ - // This might not be the right object, if there are wrappers. Unwrap if we can. - JSObject* obj = js::CheckedUnwrap(aObj); - if (!obj) { - return nullptr; - } - - nsGlobalWindow* window = xpc::WindowGlobalOrNull(obj); - if (!window) { - return nullptr; - } - - DocGroup* docGroup = window->AsInner()->GetDocGroup(); - if (!docGroup) { - return nullptr; - } - - return docGroup->CustomElementReactionsStack(); -} - // https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor already_AddRefed<nsGenericHTMLElement> CreateHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs, @@ -3493,13 +3471,7 @@ CreateHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs, // Step 5. // If the definition is for a customized built-in element, the localName // should be defined in the specification. - nsIParserService* parserService = nsContentUtils::GetParserService(); - if (!parserService) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return nullptr; - } - - tag = parserService->HTMLCaseSensitiveAtomTagToId(definition->mLocalName); + tag = nsHTMLTags::CaseSensitiveAtomTagToId(definition->mLocalName); if (tag == eHTMLTag_userdefined) { aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>(); return nullptr; diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index e583d0e060..356d3aa000 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -3422,12 +3422,6 @@ bool GetDesiredProto(JSContext* aCx, const JS::CallArgs& aCallArgs, JS::MutableHandle<JSObject*> aDesiredProto); -// Get the CustomElementReactionsStack for the docgroup of the global -// of the underlying object of aObj. This can be null if aObj can't -// be CheckUnwrapped, or if the global of the result has no docgroup -// (e.g. because it's not a Window global). -CustomElementReactionsStack* -GetCustomElementReactionsStack(JS::Handle<JSObject*> aObj); // This function is expected to be called from the constructor function for an // HTML element interface; the global/callargs need to be whatever was passed to // that constructor function. diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 8985863e8f..c9a5e9f419 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -7680,14 +7680,14 @@ class CGPerSignatureCall(CGThing): if (idlNode.getExtendedAttribute('CEReactions') is not None and not getter): - cgThings.append(CGGeneric(fill( + cgThings.append(CGGeneric(dedent( """ - CustomElementReactionsStack* reactionsStack = GetCustomElementReactionsStack(${obj}); - Maybe<AutoCEReaction> ceReaction; - if (reactionsStack) { - ceReaction.emplace(reactionsStack, cx); + Maybe<AutoCEReaction> ceReaction; + DocGroup* docGroup = self->GetDocGroup(); + if (docGroup) { + ceReaction.emplace(docGroup->CustomElementReactionsStack(), cx); } - """, obj=objectName))) + """))) # If this is a method that was generated by a maplike/setlike # interface, use the maplike/setlike generator to fill in the body. @@ -13786,6 +13786,8 @@ class CGForwardDeclarations(CGWrapper): builder.add(d.nativeType + "Atoms", isStruct=True) for t in getTypesFromDescriptor(d): builder.forwardDeclareForType(t, config) + if d.hasCEReactions(): + builder.addInMozillaDom("DocGroup") for d in dictionaries: if len(d.members) > 0: @@ -13857,18 +13859,15 @@ class CGBindingRoot(CGThing): iface = desc.interface return any(m.getExtendedAttribute("Deprecated") for m in iface.members + [iface]) - def descriptorHasCEReactions(desc): - iface = desc.interface - return any(m.getExtendedAttribute("CEReactions") for m in iface.members + [iface]) - bindingHeaders["nsIDocument.h"] = any( descriptorDeprecated(d) for d in descriptors) bindingHeaders["mozilla/Preferences.h"] = any( descriptorRequiresPreferences(d) for d in descriptors) bindingHeaders["mozilla/dom/DOMJSProxyHandler.h"] = any( d.concrete and d.proxy for d in descriptors) - bindingHeaders["mozilla/dom/CustomElementRegistry.h"] = any( - descriptorHasCEReactions(d) for d in descriptors) + hasCEReactions = any(d.hasCEReactions() for d in descriptors) + bindingHeaders["mozilla/dom/CustomElementRegistry.h"] = hasCEReactions + bindingHeaders["mozilla/dom/DocGroup.h"] = hasCEReactions def descriptorHasChromeOnly(desc): ctor = desc.interface.ctor() @@ -14704,6 +14703,12 @@ class CGBindingImplClass(CGClass): breakAfterReturnDecl=" ", override=descriptor.wrapperCache, body=self.getWrapObjectBody())) + if descriptor.hasCEReactions(): + self.methodDecls.insert(0, + ClassMethod("GetDocGroup", "DocGroup*", [], + const=True, + breakAfterReturnDecl=" ", + body=self.getGetDocGroupBody())) if wantGetParent: self.methodDecls.insert(0, ClassMethod("GetParentObject", @@ -14724,6 +14729,9 @@ class CGBindingImplClass(CGClass): def getGetParentObjectBody(self): return None + def getGetDocGroupBody(self): + return None + def deps(self): return self._deps @@ -14863,6 +14871,8 @@ class CGExampleRoot(CGThing): self.root = CGNamespace.build(["mozilla", "dom"], self.root) builder = ForwardDeclarationBuilder() + if descriptor.hasCEReactions(): + builder.addInMozillaDom("DocGroup") for member in descriptor.interface.members: if not member.isAttr() and not member.isMethod(): continue @@ -15174,7 +15184,7 @@ class CGJSImplClass(CGBindingImplClass): private: RefPtr<${jsImplName}> mImpl; - nsCOMPtr<nsISupports> mParent; + nsCOMPtr<nsIGlobalObject> mParent; """, isupportsDecl=isupportsDecl, @@ -15253,6 +15263,16 @@ class CGJSImplClass(CGBindingImplClass): def getGetParentObjectBody(self): return "return mParent;\n" + def getGetDocGroupBody(self): + return dedent( + """ + nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mParent); + if (!window) { + return nullptr; + } + return window->GetDocGroup(); + """) + def getCreateFromExistingBody(self): # XXXbz we could try to get parts of this (e.g. the argument # conversions) auto-generated by somehow creating an IDLMethod and diff --git a/dom/bindings/Configuration.py b/dom/bindings/Configuration.py index a56f2f2fd6..2669e3e4cd 100644 --- a/dom/bindings/Configuration.py +++ b/dom/bindings/Configuration.py @@ -631,6 +631,9 @@ class Descriptor(DescriptorProvider): not self.interface.isExposedInWindow()) or self.interface.isExposedInSomeButNotAllWorkers()) + def hasCEReactions(self): + return any(m.getExtendedAttribute("CEReactions") for m in self.interface.members) + def isExposedConditionally(self): return (self.interface.isExposedConditionally() or self.interface.isExposedInSomeButNotAllWorkers()) diff --git a/dom/bindings/test/TestBindingHeader.h b/dom/bindings/test/TestBindingHeader.h index 7d99637001..4e2e10e43e 100644 --- a/dom/bindings/test/TestBindingHeader.h +++ b/dom/bindings/test/TestBindingHeader.h @@ -21,6 +21,7 @@ // this one for it, for ParentDict. Hopefully it won't begin to rely on it in more fundamental ways. namespace mozilla { namespace dom { +class DocGroup; class TestExternalInterface; class Promise; } // namespace dom @@ -46,8 +47,9 @@ public: NS_DECLARE_STATIC_IID_ACCESSOR(NS_RENAMED_INTERFACE_IID) NS_DECL_ISUPPORTS - // We need a GetParentObject to make binding codegen happy + // We need a GetParentObject and GetDocGroup to make binding codegen happy virtual nsISupports* GetParentObject(); + DocGroup* GetDocGroup() const; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsRenamedInterface, NS_RENAMED_INTERFACE_IID) @@ -64,8 +66,9 @@ public: NS_DECLARE_STATIC_IID_ACCESSOR(NS_INDIRECTLY_IMPLEMENTED_INTERFACE_IID) NS_DECL_ISUPPORTS - // We need a GetParentObject to make binding codegen happy + // We need a GetParentObject and GetDocGroup to make binding codegen happy virtual nsISupports* GetParentObject(); + DocGroup* GetDocGroup() const; bool IndirectlyImplementedProperty(); void IndirectlyImplementedProperty(bool); diff --git a/dom/events/ContentEventHandler.cpp b/dom/events/ContentEventHandler.cpp index 935ade23f4..df54a4d44a 100644 --- a/dom/events/ContentEventHandler.cpp +++ b/dom/events/ContentEventHandler.cpp @@ -980,11 +980,7 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange, // Special case like <br contenteditable> if (!mRootContent->HasChildren()) { - nsresult rv = aRange->SetStart(mRootContent, 0); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - rv = aRange->SetEnd(mRootContent, 0); + nsresult rv = aRange->CollapseTo(mRootContent, 0); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -2880,8 +2876,7 @@ ContentEventHandler::AdjustCollapsedRangeMaybeIntoTextNode(nsRange* aRange) return NS_OK; } - nsresult rv = aRange->Set(childNode, offsetInChildNode, - childNode, offsetInChildNode); + nsresult rv = aRange->CollapseTo(childNode, offsetInChildNode); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } diff --git a/dom/events/DOMEventTargetHelper.cpp b/dom/events/DOMEventTargetHelper.cpp index dd9a01d8d2..90ee610594 100644 --- a/dom/events/DOMEventTargetHelper.cpp +++ b/dom/events/DOMEventTargetHelper.cpp @@ -328,10 +328,10 @@ DOMEventTargetHelper::GetEventHandler(nsIAtom* aType, } nsresult -DOMEventTargetHelper::PreHandleEvent(EventChainPreVisitor& aVisitor) +DOMEventTargetHelper::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.mCanHandle = true; - aVisitor.mParentTarget = nullptr; + aVisitor.SetParentTarget(nullptr, false); return NS_OK; } diff --git a/dom/events/DOMEventTargetHelper.h b/dom/events/DOMEventTargetHelper.h index c5a0611c99..b14f054286 100644 --- a/dom/events/DOMEventTargetHelper.h +++ b/dom/events/DOMEventTargetHelper.h @@ -248,10 +248,10 @@ NS_DEFINE_STATIC_IID_ACCESSOR(DOMEventTargetHelper, /* Use this macro to declare functions that forward the behavior of this * interface to another object. - * This macro doesn't forward PreHandleEvent because sometimes subclasses + * This macro doesn't forward GetEventTargetParent because sometimes subclasses * want to override it. */ -#define NS_FORWARD_NSIDOMEVENTTARGET_NOPREHANDLEEVENT(_to) \ +#define NS_FORWARD_NSIDOMEVENTTARGET_NOGETEVENTTARGETPARENT(_to) \ NS_IMETHOD AddEventListener(const nsAString & type, nsIDOMEventListener *listener, bool useCapture, bool wantsUntrusted, uint8_t _argc) { \ return _to AddEventListener(type, listener, useCapture, wantsUntrusted, _argc); \ } \ diff --git a/dom/events/Event.cpp b/dom/events/Event.cpp index 280e40ad57..aff65b40fa 100755 --- a/dom/events/Event.cpp +++ b/dom/events/Event.cpp @@ -11,6 +11,7 @@ #include "mozilla/dom/ShadowRoot.h" #include "mozilla/ContentEvents.h" #include "mozilla/DOMEventTargetHelper.h" +#include "mozilla/EventDispatcher.h" #include "mozilla/EventStateManager.h" #include "mozilla/InternalMutationEvent.h" #include "mozilla/dom/Performance.h" @@ -157,18 +158,11 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Event) tmp->mEvent->mTarget = nullptr; tmp->mEvent->mCurrentTarget = nullptr; tmp->mEvent->mOriginalTarget = nullptr; + tmp->mEvent->mRelatedTarget = nullptr; switch (tmp->mEvent->mClass) { - case eMouseEventClass: - case eMouseScrollEventClass: - case eWheelEventClass: - case eSimpleGestureEventClass: - case ePointerEventClass: - tmp->mEvent->AsMouseEventBase()->relatedTarget = nullptr; - break; case eDragEventClass: { WidgetDragEvent* dragEvent = tmp->mEvent->AsDragEvent(); dragEvent->mDataTransfer = nullptr; - dragEvent->relatedTarget = nullptr; break; } case eClipboardEventClass: @@ -177,9 +171,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Event) case eMutationEventClass: tmp->mEvent->AsMutationEvent()->mRelatedNode = nullptr; break; - case eFocusEventClass: - tmp->mEvent->AsFocusEvent()->mRelatedTarget = nullptr; - break; default: break; } @@ -195,21 +186,12 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Event) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mTarget) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mCurrentTarget) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mOriginalTarget) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mRelatedTarget) switch (tmp->mEvent->mClass) { - case eMouseEventClass: - case eMouseScrollEventClass: - case eWheelEventClass: - case eSimpleGestureEventClass: - case ePointerEventClass: - NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->relatedTarget"); - cb.NoteXPCOMChild(tmp->mEvent->AsMouseEventBase()->relatedTarget); - break; case eDragEventClass: { WidgetDragEvent* dragEvent = tmp->mEvent->AsDragEvent(); NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->mDataTransfer"); cb.NoteXPCOMChild(dragEvent->mDataTransfer); - NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->relatedTarget"); - cb.NoteXPCOMChild(dragEvent->relatedTarget); break; } case eClipboardEventClass: @@ -220,10 +202,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Event) NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->mRelatedNode"); cb.NoteXPCOMChild(tmp->mEvent->AsMutationEvent()->mRelatedNode); break; - case eFocusEventClass: - NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->mRelatedTarget"); - cb.NoteXPCOMChild(tmp->mEvent->AsFocusEvent()->mRelatedTarget); - break; default: break; } @@ -298,6 +276,12 @@ Event::GetCurrentTarget() const return mEvent->GetCurrentDOMEventTarget(); } +void +Event::ComposedPath(nsTArray<RefPtr<EventTarget>>& aPath) +{ + EventDispatcher::GetComposedPathFor(mEvent, aPath); +} + NS_IMETHODIMP Event::GetCurrentTarget(nsIDOMEventTarget** aCurrentTarget) { diff --git a/dom/events/Event.h b/dom/events/Event.h index 0817aa809e..4f1fcc8270 100755 --- a/dom/events/Event.h +++ b/dom/events/Event.h @@ -157,6 +157,8 @@ public: EventTarget* GetTarget() const; EventTarget* GetCurrentTarget() const; + void ComposedPath(nsTArray<RefPtr<EventTarget>>& aPath); + uint16_t EventPhase() const; // xpidl implementation diff --git a/dom/events/EventDispatcher.cpp b/dom/events/EventDispatcher.cpp index 1d4dfd7d9f..1094c08c20 100644 --- a/dom/events/EventDispatcher.cpp +++ b/dom/events/EventDispatcher.cpp @@ -130,13 +130,6 @@ static bool IsEventTargetChrome(EventTarget* aEventTarget, return isChrome; } - -#define NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH (1 << 0) -#define NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT (1 << 1) -#define NS_TARGET_CHAIN_MAY_HAVE_MANAGER (1 << 2) -#define NS_TARGET_CHAIN_CHECKED_IF_CHROME (1 << 3) -#define NS_TARGET_CHAIN_IS_CHROME_CONTENT (1 << 4) - // EventTargetChainItem represents a single item in the event target chain. class EventTargetChainItem { @@ -144,8 +137,7 @@ private: explicit EventTargetChainItem(EventTarget* aTarget); public: EventTargetChainItem() - : mFlags(0) - , mItemFlags(0) + : mItemFlags(0) { } @@ -153,7 +145,8 @@ public: EventTarget* aTarget, EventTargetChainItem* aChild = nullptr) { - MOZ_ASSERT(!aChild || &aChain.ElementAt(aChain.Length() - 1) == aChild); + // The last item which can handle the event must be aChild. + MOZ_ASSERT(GetLastCanHandleEventTarget(aChain) == aChild); return new (aChain.AppendElement()) EventTargetChainItem(aTarget); } @@ -165,6 +158,38 @@ public: aChain.RemoveElementAt(lastIndex); } + static EventTargetChainItem* GetFirstCanHandleEventTarget( + nsTArray<EventTargetChainItem>& aChain) + { + return &aChain[GetFirstCanHandleEventTargetIdx(aChain)]; + } + + static uint32_t GetFirstCanHandleEventTargetIdx(nsTArray<EventTargetChainItem>& aChain) + { + // aChain[i].PreHandleEventOnly() = true only when the target element wants + // PreHandleEvent and set mCanHandle=false. So we find the first element + // which can handle the event. + for (uint32_t i = 0; i < aChain.Length(); ++i) { + if (!aChain[i].PreHandleEventOnly()) { + return i; + } + } + MOZ_ASSERT(false); + return 0; + } + + static EventTargetChainItem* GetLastCanHandleEventTarget( + nsTArray<EventTargetChainItem>& aChain) + { + // Fine the last item which can handle the event. + for (int32_t i = aChain.Length() - 1; i >= 0; --i) { + if (!aChain[i].PreHandleEventOnly()) { + return &aChain[i]; + } + } + return nullptr; + } + bool IsValid() { NS_WARNING_ASSERTION(!!(mTarget), "Event target is not valid!"); @@ -183,44 +208,82 @@ public: void SetForceContentDispatch(bool aForce) { - if (aForce) { - mFlags |= NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH; - } else { - mFlags &= ~NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH; - } + mFlags.mForceContentDispatch = aForce; } bool ForceContentDispatch() { - return !!(mFlags & NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH); + return mFlags.mForceContentDispatch; } void SetWantsWillHandleEvent(bool aWants) { - if (aWants) { - mFlags |= NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT; - } else { - mFlags &= ~NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT; - } + mFlags.mWantsWillHandleEvent = aWants; } bool WantsWillHandleEvent() { - return !!(mFlags & NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT); + return mFlags.mWantsWillHandleEvent; + } + + void SetWantsPreHandleEvent(bool aWants) + { + mFlags.mWantsPreHandleEvent = aWants; + } + + bool WantsPreHandleEvent() + { + return mFlags.mWantsPreHandleEvent; + } + + void SetPreHandleEventOnly(bool aWants) + { + mFlags.mPreHandleEventOnly = aWants; + } + + bool PreHandleEventOnly() + { + return mFlags.mPreHandleEventOnly; + } + + void SetRootOfClosedTree(bool aSet) + { + mFlags.mRootOfClosedTree = aSet; + } + + bool IsRootOfClosedTree() + { + return mFlags.mRootOfClosedTree; + } + + void SetIsSlotInClosedTree(bool aSet) + { + mFlags.mIsSlotInClosedTree = aSet; + } + + bool IsSlotInClosedTree() + { + return mFlags.mIsSlotInClosedTree; + } + + void SetIsChromeHandler(bool aSet) + { + mFlags.mIsChromeHandler = aSet; + } + + bool IsChromeHandler() + { + return mFlags.mIsChromeHandler; } void SetMayHaveListenerManager(bool aMayHave) { - if (aMayHave) { - mFlags |= NS_TARGET_CHAIN_MAY_HAVE_MANAGER; - } else { - mFlags &= ~NS_TARGET_CHAIN_MAY_HAVE_MANAGER; - } + mFlags.mMayHaveManager = aMayHave; } bool MayHaveListenerManager() { - return !!(mFlags & NS_TARGET_CHAIN_MAY_HAVE_MANAGER); + return mFlags.mMayHaveManager; } EventTarget* CurrentTarget() @@ -240,10 +303,15 @@ public: ELMCreationDetector& aCd); /** - * Resets aVisitor object and calls PreHandleEvent. + * Resets aVisitor object and calls GetEventTargetParent. * Copies mItemFlags and mItemData to the current EventTargetChainItem. */ - void PreHandleEvent(EventChainPreVisitor& aVisitor); + void GetEventTargetParent(EventChainPreVisitor& aVisitor); + + /** + * Calls PreHandleEvent for those items which called SetWantsPreHandleEvent. + */ + void PreHandleEvent(EventChainVisitor& aVisitor); /** * If the current item in the event target chain has an event listener @@ -288,7 +356,37 @@ public: private: nsCOMPtr<EventTarget> mTarget; - uint16_t mFlags; + + class EventTargetChainFlags + { + public: + explicit EventTargetChainFlags() + { + SetRawFlags(0); + } + // Cached flags for each EventTargetChainItem which are set when calling + // GetEventTargetParent to create event target chain. They are used to + // manage or speedup event dispatching. + bool mForceContentDispatch : 1; + bool mWantsWillHandleEvent : 1; + bool mMayHaveManager : 1; + bool mChechedIfChrome : 1; + bool mIsChromeContent : 1; + bool mWantsPreHandleEvent : 1; + bool mPreHandleEventOnly : 1; + bool mRootOfClosedTree : 1; + bool mIsSlotInClosedTree : 1; + bool mIsChromeHandler : 1; + private: + typedef uint32_t RawFlags; + void SetRawFlags(RawFlags aRawFlags) + { + static_assert(sizeof(EventTargetChainFlags) <= sizeof(RawFlags), + "EventTargetChainFlags must not be bigger than the RawFlags"); + memcpy(this, &aRawFlags, sizeof(EventTargetChainFlags)); + } + } mFlags; + uint16_t mItemFlags; nsCOMPtr<nsISupports> mItemData; // Event retargeting must happen whenever mNewTarget is non-null. @@ -298,37 +396,50 @@ private: bool IsCurrentTargetChrome() { - if (!(mFlags & NS_TARGET_CHAIN_CHECKED_IF_CHROME)) { - mFlags |= NS_TARGET_CHAIN_CHECKED_IF_CHROME; + if (!mFlags.mChechedIfChrome) { + mFlags.mChechedIfChrome = true; if (IsEventTargetChrome(mTarget)) { - mFlags |= NS_TARGET_CHAIN_IS_CHROME_CONTENT; + mFlags.mIsChromeContent = true; } } - return !!(mFlags & NS_TARGET_CHAIN_IS_CHROME_CONTENT); + return mFlags.mIsChromeContent; } }; EventTargetChainItem::EventTargetChainItem(EventTarget* aTarget) : mTarget(aTarget) - , mFlags(0) , mItemFlags(0) { MOZ_ASSERT(!aTarget || mTarget == aTarget->GetTargetForEventTargetChain()); } void -EventTargetChainItem::PreHandleEvent(EventChainPreVisitor& aVisitor) +EventTargetChainItem::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.Reset(); - Unused << mTarget->PreHandleEvent(aVisitor); + Unused << mTarget->GetEventTargetParent(aVisitor); SetForceContentDispatch(aVisitor.mForceContentDispatch); SetWantsWillHandleEvent(aVisitor.mWantsWillHandleEvent); SetMayHaveListenerManager(aVisitor.mMayHaveListenerManager); + SetWantsPreHandleEvent(aVisitor.mWantsPreHandleEvent); + SetPreHandleEventOnly(aVisitor.mWantsPreHandleEvent && !aVisitor.mCanHandle); + SetRootOfClosedTree(aVisitor.mRootOfClosedTree); mItemFlags = aVisitor.mItemFlags; mItemData = aVisitor.mItemData; } void +EventTargetChainItem::PreHandleEvent(EventChainVisitor& aVisitor) +{ + if (!WantsPreHandleEvent()) { + return; + } + aVisitor.mItemFlags = mItemFlags; + aVisitor.mItemData = mItemData; + Unused << mTarget->PreHandleEvent(aVisitor); +} + +void EventTargetChainItem::PostHandleEvent(EventChainPostVisitor& aVisitor) { aVisitor.mItemFlags = mItemFlags; @@ -346,12 +457,17 @@ EventTargetChainItem::HandleEventTargetChain( // Save the target so that it can be restored later. nsCOMPtr<EventTarget> firstTarget = aVisitor.mEvent->mTarget; uint32_t chainLength = aChain.Length(); + uint32_t firstCanHandleEventTargetIdx = + EventTargetChainItem::GetFirstCanHandleEventTargetIdx(aChain); // Capture aVisitor.mEvent->mFlags.mInCapturePhase = true; aVisitor.mEvent->mFlags.mInBubblingPhase = false; - for (uint32_t i = chainLength - 1; i > 0; --i) { + for (uint32_t i = chainLength - 1; i > firstCanHandleEventTargetIdx; --i) { EventTargetChainItem& item = aChain[i]; + if (item.PreHandleEventOnly()) { + continue; + } if ((!aVisitor.mEvent->mFlags.mNoContentDispatch || item.ForceContentDispatch()) && !aVisitor.mEvent->PropagationStopped()) { @@ -373,7 +489,7 @@ EventTargetChainItem::HandleEventTargetChain( // Target aVisitor.mEvent->mFlags.mInBubblingPhase = true; - EventTargetChainItem& targetItem = aChain[0]; + EventTargetChainItem& targetItem = aChain[firstCanHandleEventTargetIdx]; if (!aVisitor.mEvent->PropagationStopped() && (!aVisitor.mEvent->mFlags.mNoContentDispatch || targetItem.ForceContentDispatch())) { @@ -385,8 +501,11 @@ EventTargetChainItem::HandleEventTargetChain( // Bubble aVisitor.mEvent->mFlags.mInCapturePhase = false; - for (uint32_t i = 1; i < chainLength; ++i) { + for (uint32_t i = firstCanHandleEventTargetIdx + 1; i < chainLength; ++i) { EventTargetChainItem& item = aChain[i]; + if (item.PreHandleEventOnly()) { + continue; + } EventTarget* newTarget = item.GetNewTarget(); if (newTarget) { // Item is at anonymous boundary. Need to retarget for the current item @@ -471,6 +590,28 @@ EventTargetChainItemForChromeTarget(nsTArray<EventTargetChainItem>& aChain, return etci; } +/* static */ EventTargetChainItem* +MayRetargetToChromeIfCanNotHandleEvent( + nsTArray<EventTargetChainItem>& aChain, EventChainPreVisitor& aPreVisitor, + EventTargetChainItem* aTargetEtci, EventTargetChainItem* aChildEtci, + nsINode* aContent) +{ + if (!aPreVisitor.mWantsPreHandleEvent) { + // Keep EventTargetChainItem if we need to call PreHandleEvent on it. + EventTargetChainItem::DestroyLast(aChain, aTargetEtci); + } + if (aPreVisitor.mAutomaticChromeDispatch && aContent) { + // Event target couldn't handle the event. Try to propagate to chrome. + EventTargetChainItem* chromeTargetEtci = + EventTargetChainItemForChromeTarget(aChain, aContent, aChildEtci); + if (chromeTargetEtci) { + chromeTargetEtci->GetEventTargetParent(aPreVisitor); + return chromeTargetEtci; + } + } + return nullptr; +} + /* static */ nsresult EventDispatcher::Dispatch(nsISupports* aTarget, nsPresContext* aPresContext, @@ -593,7 +734,6 @@ EventDispatcher::Dispatch(nsISupports* aTarget, // Create the event target chain item for the event target. EventTargetChainItem* targetEtci = EventTargetChainItem::Create(chain, target->GetTargetForEventTargetChain()); - MOZ_ASSERT(&chain[0] == targetEtci); if (!targetEtci->IsValid()) { EventTargetChainItem::DestroyLast(chain, targetEtci); return NS_ERROR_FAILURE; @@ -631,37 +771,43 @@ EventDispatcher::Dispatch(nsISupports* aTarget, aEvent->mFlags.mIsBeingDispatched = true; // Create visitor object and start event dispatching. - // PreHandleEvent for the original target. + // GetEventTargetParent for the original target. nsEventStatus status = aEventStatus ? *aEventStatus : nsEventStatus_eIgnore; EventChainPreVisitor preVisitor(aPresContext, aEvent, aDOMEvent, status, isInAnon); - targetEtci->PreHandleEvent(preVisitor); - - if (!preVisitor.mCanHandle && preVisitor.mAutomaticChromeDispatch && content) { - // Event target couldn't handle the event. Try to propagate to chrome. - EventTargetChainItem::DestroyLast(chain, targetEtci); - targetEtci = EventTargetChainItemForChromeTarget(chain, content); - NS_ENSURE_STATE(targetEtci); - MOZ_ASSERT(&chain[0] == targetEtci); - targetEtci->PreHandleEvent(preVisitor); - } - if (preVisitor.mCanHandle) { + targetEtci->GetEventTargetParent(preVisitor); + + if (!preVisitor.mCanHandle) { + targetEtci = MayRetargetToChromeIfCanNotHandleEvent(chain, preVisitor, + targetEtci, nullptr, + content); + } + if (!preVisitor.mCanHandle) { + // The original target and chrome target (mAutomaticChromeDispatch=true) + // can not handle the event but we still have to call their PreHandleEvent. + for (uint32_t i = 0; i < chain.Length(); ++i) { + chain[i].PreHandleEvent(preVisitor); + } + } else { // At least the original target can handle the event. // Setting the retarget to the |target| simplifies retargeting code. nsCOMPtr<EventTarget> t = do_QueryInterface(aEvent->mTarget); targetEtci->SetNewTarget(t); EventTargetChainItem* topEtci = targetEtci; targetEtci = nullptr; - while (preVisitor.mParentTarget) { - EventTarget* parentTarget = preVisitor.mParentTarget; + while (preVisitor.GetParentTarget()) { + EventTarget* parentTarget = preVisitor.GetParentTarget(); EventTargetChainItem* parentEtci = - EventTargetChainItem::Create(chain, preVisitor.mParentTarget, topEtci); + EventTargetChainItem::Create(chain, parentTarget, topEtci); if (!parentEtci->IsValid()) { EventTargetChainItem::DestroyLast(chain, parentEtci); rv = NS_ERROR_FAILURE; break; } + parentEtci->SetIsSlotInClosedTree(preVisitor.mParentIsSlotInClosedTree); + parentEtci->SetIsChromeHandler(preVisitor.mParentIsChromeHandler); + // Item needs event retargetting. if (preVisitor.mEventTargetAtParent) { // Need to set the target of the event @@ -670,29 +816,22 @@ EventDispatcher::Dispatch(nsISupports* aTarget, parentEtci->SetNewTarget(preVisitor.mEventTargetAtParent); } - parentEtci->PreHandleEvent(preVisitor); + parentEtci->GetEventTargetParent(preVisitor); if (preVisitor.mCanHandle) { topEtci = parentEtci; } else { - EventTargetChainItem::DestroyLast(chain, parentEtci); - parentEtci = nullptr; - if (preVisitor.mAutomaticChromeDispatch && content) { - // Even if the current target can't handle the event, try to - // propagate to chrome. - nsCOMPtr<nsINode> disabledTarget = do_QueryInterface(parentTarget); - if (disabledTarget) { - parentEtci = EventTargetChainItemForChromeTarget(chain, - disabledTarget, - topEtci); - if (parentEtci) { - parentEtci->PreHandleEvent(preVisitor); - if (preVisitor.mCanHandle) { - chain[0].SetNewTarget(parentTarget); - topEtci = parentEtci; - continue; - } - } - } + nsCOMPtr<nsINode> disabledTarget = do_QueryInterface(parentTarget); + parentEtci = MayRetargetToChromeIfCanNotHandleEvent(chain, + preVisitor, + parentEtci, + topEtci, + disabledTarget); + if (parentEtci && preVisitor.mCanHandle) { + EventTargetChainItem* item = + EventTargetChainItem::GetFirstCanHandleEventTarget(chain); + item->SetNewTarget(parentTarget); + topEtci = parentEtci; + continue; } break; } @@ -706,11 +845,19 @@ EventDispatcher::Dispatch(nsISupports* aTarget, targets[i] = chain[i].CurrentTarget()->GetTargetForDOMEvent(); } } else { - // Event target chain is created. Handle the chain. + // Event target chain is created. PreHandle the chain. + for (uint32_t i = 0; i < chain.Length(); ++i) { + chain[i].PreHandleEvent(preVisitor); + } + // Handle the chain. EventChainPostVisitor postVisitor(preVisitor); + MOZ_RELEASE_ASSERT(!aEvent->mPath); + aEvent->mPath = &chain; EventTargetChainItem::HandleEventTargetChain(chain, postVisitor, aCallback, cd); + aEvent->mPath = nullptr; + preVisitor.mEventStatus = postVisitor.mEventStatus; // If the DOM event was created during event flow. if (!preVisitor.mDOMEvent && postVisitor.mDOMEvent) { @@ -1022,4 +1169,64 @@ EventDispatcher::CreateEvent(EventTarget* aOwner, return nullptr; } +// static +void +EventDispatcher::GetComposedPathFor(WidgetEvent* aEvent, + nsTArray<RefPtr<EventTarget>>& aPath) +{ + nsTArray<EventTargetChainItem>* path = aEvent->mPath; + if (!path || path->IsEmpty() || !aEvent->mCurrentTarget) { + return; + } + + EventTarget* currentTarget = + aEvent->mCurrentTarget->GetTargetForEventTargetChain(); + if (!currentTarget) { + return; + } + + AutoTArray<EventTarget*, 128> reversedComposedPath; + bool hasSeenCurrentTarget = false; + uint32_t hiddenSubtreeLevel = 0; + for (uint32_t i = path->Length(); i; ) { + --i; + + EventTargetChainItem& item = path->ElementAt(i); + if (item.PreHandleEventOnly()) { + continue; + } + + if (!hasSeenCurrentTarget && currentTarget == item.CurrentTarget()) { + hasSeenCurrentTarget = true; + } else if (hasSeenCurrentTarget && item.IsRootOfClosedTree()) { + ++hiddenSubtreeLevel; + } + + if (hiddenSubtreeLevel == 0) { + reversedComposedPath.AppendElement(item.CurrentTarget()); + } + + if (item.IsSlotInClosedTree() && hiddenSubtreeLevel > 0) { + --hiddenSubtreeLevel; + } + + if (item.IsChromeHandler()) { + if (hasSeenCurrentTarget) { + // The current behavior is to include only EventTargets from + // either chrome side of event path or content side, not from both. + break; + } + + // Need to start all over to collect the composed path on content side. + reversedComposedPath.Clear(); + } + } + + aPath.SetCapacity(reversedComposedPath.Length()); + for (uint32_t i = reversedComposedPath.Length(); i; ) { + --i; + aPath.AppendElement(reversedComposedPath[i]->GetTargetForDOMEvent()); + } +} + } // namespace mozilla diff --git a/dom/events/EventDispatcher.h b/dom/events/EventDispatcher.h index 3c754033db..8a34e6bf78 100644 --- a/dom/events/EventDispatcher.h +++ b/dom/events/EventDispatcher.h @@ -31,14 +31,14 @@ class EventTarget; * About event dispatching: * When either EventDispatcher::Dispatch or * EventDispatcher::DispatchDOMEvent is called an event target chain is - * created. EventDispatcher creates the chain by calling PreHandleEvent + * created. EventDispatcher creates the chain by calling GetEventTargetParent * on each event target and the creation continues until either the mCanHandle * member of the EventChainPreVisitor object is false or the mParentTarget * does not point to a new target. The event target chain is created in the * heap. * * If the event needs retargeting, mEventTargetAtParent must be set in - * PreHandleEvent. + * GetEventTargetParent. * * The capture, target and bubble phases of the event dispatch are handled * by iterating through the event target chain. Iteration happens twice, @@ -86,7 +86,7 @@ public: /** * Bits for items in the event target chain. - * Set in PreHandleEvent() and used in PostHandleEvent(). + * Set in GetEventTargetParent() and used in PostHandleEvent(). * * @note These bits are different for each item in the event target chain. * It is up to the Pre/PostHandleEvent implementation to decide how to @@ -98,7 +98,7 @@ public: /** * Data for items in the event target chain. - * Set in PreHandleEvent() and used in PostHandleEvent(). + * Set in GetEventTargetParent() and used in PostHandleEvent(). * * @note This data is different for each item in the event target chain. * It is up to the Pre/PostHandleEvent implementation to decide how to @@ -123,6 +123,10 @@ public: , mOriginalTargetIsInAnon(aIsInAnon) , mWantsWillHandleEvent(false) , mMayHaveListenerManager(true) + , mWantsPreHandleEvent(false) + , mRootOfClosedTree(false) + , mParentIsSlotInClosedTree(false) + , mParentIsChromeHandler(false) , mParentTarget(nullptr) , mEventTargetAtParent(nullptr) { @@ -137,13 +141,30 @@ public: mForceContentDispatch = false; mWantsWillHandleEvent = false; mMayHaveListenerManager = true; + mWantsPreHandleEvent = false; + mRootOfClosedTree = false; + mParentIsSlotInClosedTree = false; + mParentIsChromeHandler = false; mParentTarget = nullptr; mEventTargetAtParent = nullptr; } + dom::EventTarget* GetParentTarget() + { + return mParentTarget; + } + + void SetParentTarget(dom::EventTarget* aParentTarget, bool aIsChromeHandler) + { + mParentTarget = aParentTarget; + if (mParentTarget) { + mParentIsChromeHandler = aIsChromeHandler; + } + } + /** - * Member that must be set in PreHandleEvent by event targets. If set to false, - * indicates that this event target will not be handling the event and + * Member that must be set in GetEventTargetParent by event targets. If set to + * false, indicates that this event target will not be handling the event and * construction of the event target chain is complete. The target that sets * mCanHandle to false is NOT included in the event target chain. */ @@ -170,7 +191,7 @@ public: /** * true if the original target of the event is inside anonymous content. - * This is set before calling PreHandleEvent on event targets. + * This is set before calling GetEventTargetParent on event targets. */ bool mOriginalTargetIsInAnon; @@ -182,27 +203,45 @@ public: /** * If it is known that the current target doesn't have a listener manager - * when PreHandleEvent is called, set this to false. + * when GetEventTargetParent is called, set this to false. */ bool mMayHaveListenerManager; /** + * Whether or not nsIDOMEventTarget::PreHandleEvent will be called. Default is + * false; + */ + bool mWantsPreHandleEvent; + + /** + * True if the current target is either closed ShadowRoot or root of + * chrome only access tree (for example native anonymous content). + */ + bool mRootOfClosedTree; + + /** + * True if mParentTarget is HTMLSlotElement in a closed shadow tree and the + * current target is assigned to that slot. + */ + bool mParentIsSlotInClosedTree; + + /** + * True if mParentTarget is a chrome handler in the event path. + */ + bool mParentIsChromeHandler; + +private: + /** * Parent item in the event target chain. */ dom::EventTarget* mParentTarget; +public: /** * If the event needs to be retargeted, this is the event target, * which should be used when the event is handled at mParentTarget. */ dom::EventTarget* mEventTargetAtParent; - - /** - * An array of destination insertion points that need to be inserted - * into the event path of nodes that are distributed by the - * web components distribution algorithm. - */ - nsTArray<nsIContent*> mDestInsertionPoints; }; class EventChainPostVisitor : public mozilla::EventChainVisitor @@ -280,6 +319,9 @@ public: WidgetEvent* aEvent, const nsAString& aEventType); + static void GetComposedPathFor(WidgetEvent* aEvent, + nsTArray<RefPtr<dom::EventTarget>>& aPath); + /** * Called at shutting down. */ diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index e16d68c81a..cc3bd3f5aa 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -3484,6 +3484,12 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext, // make sure to fire the enter and exit_synth events after the // eDragExit event, otherwise we'll clean up too early GenerateDragDropEnterExit(presContext, aEvent->AsDragEvent()); + if (ContentChild* child = ContentChild::GetSingleton()) { + // SendUpdateDropEffect to prevent nsIDragService from waiting for + // response of forwarded dragexit event. + child->SendUpdateDropEffect(nsIDragService::DRAGDROP_ACTION_NONE, + nsIDragService::DRAGDROP_ACTION_NONE); + } break; case eBeforeKeyUp: @@ -3900,13 +3906,13 @@ CreateMouseOrPointerWidgetEvent(WidgetMouseEvent* aMouseEvent, newPointerEvent->mWidth = sourcePointer->mWidth; newPointerEvent->mHeight = sourcePointer->mHeight; newPointerEvent->inputSource = sourcePointer->inputSource; - newPointerEvent->relatedTarget = aRelatedContent; + newPointerEvent->mRelatedTarget = aRelatedContent; aNewEvent = newPointerEvent.forget(); } else { aNewEvent = new WidgetMouseEvent(aMouseEvent->IsTrusted(), aMessage, aMouseEvent->mWidget, WidgetMouseEvent::eReal); - aNewEvent->relatedTarget = aRelatedContent; + aNewEvent->mRelatedTarget = aRelatedContent; } aNewEvent->mRefPoint = aMouseEvent->mRefPoint; aNewEvent->mModifiers = aMouseEvent->mModifiers; @@ -4446,6 +4452,19 @@ EventStateManager::GenerateDragDropEnterExit(nsPresContext* aPresContext, FireDragEnterOrExit(sLastDragOverFrame->PresContext(), aDragEvent, eDragExit, targetContent, lastContent, sLastDragOverFrame); + nsIContent* target = sLastDragOverFrame ? sLastDragOverFrame.GetFrame()->GetContent() : nullptr; + if (IsRemoteTarget(target)) { + // Dragging something and moving from web content to chrome only + // fires dragexit and dragleave to xul:browser. We have to forward + // dragexit to sLastDragOverFrame when its content is a remote + // target. We don't forward dragleave since it's generated from + // dragexit. + WidgetDragEvent remoteEvent(aDragEvent->IsTrusted(), eDragExit, + aDragEvent->mWidget); + remoteEvent.AssignDragEventData(*aDragEvent, true); + nsEventStatus remoteStatus = nsEventStatus_eIgnore; + HandleCrossProcessEvent(&remoteEvent, &remoteStatus); + } } FireDragEnterOrExit(aPresContext, aDragEvent, eDragEnter, @@ -4502,14 +4521,11 @@ EventStateManager::FireDragEnterOrExit(nsPresContext* aPresContext, nsIContent* aTargetContent, nsWeakFrame& aTargetFrame) { + MOZ_ASSERT(aMessage == eDragLeave || aMessage == eDragExit || + aMessage == eDragEnter); nsEventStatus status = nsEventStatus_eIgnore; WidgetDragEvent event(aDragEvent->IsTrusted(), aMessage, aDragEvent->mWidget); - event.mRefPoint = aDragEvent->mRefPoint; - event.mModifiers = aDragEvent->mModifiers; - event.buttons = aDragEvent->buttons; - event.relatedTarget = aRelatedTarget; - event.inputSource = aDragEvent->inputSource; - + event.AssignDragEventData(*aDragEvent, true); mCurrentTargetContent = aTargetContent; if (aTargetContent != aRelatedTarget) { @@ -4527,10 +4543,7 @@ EventStateManager::FireDragEnterOrExit(nsPresContext* aPresContext, // collect any changes to moz cursor settings stored in the event's // data transfer. - if (aMessage == eDragLeave || aMessage == eDragExit || - aMessage == eDragEnter) { - UpdateDragDataTransfer(&event); - } + UpdateDragDataTransfer(&event); } // Finally dispatch the event to the frame diff --git a/dom/events/EventStates.h b/dom/events/EventStates.h index 2672d2897d..3397110bae 100644 --- a/dom/events/EventStates.h +++ b/dom/events/EventStates.h @@ -292,14 +292,39 @@ private: #define NS_EVENT_STATE_RTL NS_DEFINE_EVENT_STATE_MACRO(43) // Element is highlighted (devtools inspector) #define NS_EVENT_STATE_DEVTOOLS_HIGHLIGHTED NS_DEFINE_EVENT_STATE_MACRO(45) -// Element is an unresolved custom element candidate -#define NS_EVENT_STATE_UNRESOLVED NS_DEFINE_EVENT_STATE_MACRO(46) +// States for tracking the state of the "dir" attribute for HTML elements. We +// use these to avoid having to get "dir" attributes all the time during +// selector matching for some parts of the UA stylesheet. +// +// These states are externally managed, because we also don't want to keep +// getting "dir" attributes in IntrinsicState. +// +// Element is HTML and has a "dir" attibute. This state might go away depending +// on how https://github.com/whatwg/html/issues/2769 gets resolved. The value +// could be anything. +#define NS_EVENT_STATE_HAS_DIR_ATTR NS_DEFINE_EVENT_STATE_MACRO(46) +// Element is HTML, has a "dir" attribute, and the attribute's value is +// case-insensitively equal to "ltr". +#define NS_EVENT_STATE_DIR_ATTR_LTR NS_DEFINE_EVENT_STATE_MACRO(47) +// Element is HTML, has a "dir" attribute, and the attribute's value is +// case-insensitively equal to "rtl". +#define NS_EVENT_STATE_DIR_ATTR_RTL NS_DEFINE_EVENT_STATE_MACRO(48) +// Element is HTML, and is either a <bdi> element with no valid-valued "dir" +// attribute or any HTML element which has a "dir" attribute whose value is +// "auto". +#define NS_EVENT_STATE_DIR_ATTR_LIKE_AUTO NS_DEFINE_EVENT_STATE_MACRO(49) +// Free bit NS_DEFINE_EVENT_STATE_MACRO(50) // Element is transitioning for rules changed by style editor -#define NS_EVENT_STATE_STYLEEDITOR_TRANSITIONING NS_DEFINE_EVENT_STATE_MACRO(47) +#define NS_EVENT_STATE_STYLEEDITOR_TRANSITIONING NS_DEFINE_EVENT_STATE_MACRO(51) // Content shows its placeholder -#define NS_EVENT_STATE_PLACEHOLDERSHOWN NS_DEFINE_EVENT_STATE_MACRO(48) +#define NS_EVENT_STATE_PLACEHOLDERSHOWN NS_DEFINE_EVENT_STATE_MACRO(52) // Element has focus-within. -#define NS_EVENT_STATE_FOCUS_WITHIN NS_DEFINE_EVENT_STATE_MACRO(49) +#define NS_EVENT_STATE_FOCUS_WITHIN NS_DEFINE_EVENT_STATE_MACRO(53) + +#define DIR_ATTR_STATES (NS_EVENT_STATE_HAS_DIR_ATTR | \ + NS_EVENT_STATE_DIR_ATTR_LTR | \ + NS_EVENT_STATE_DIR_ATTR_RTL | \ + NS_EVENT_STATE_DIR_ATTR_LIKE_AUTO) // Event state that is used for values that need to be parsed but do nothing. #define NS_EVENT_STATE_IGNORE NS_DEFINE_EVENT_STATE_MACRO(63) @@ -310,11 +335,10 @@ private: #define DIRECTION_STATES (NS_EVENT_STATE_LTR | NS_EVENT_STATE_RTL) -#define ESM_MANAGED_STATES (NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_FOCUS | \ +#define ESM_MANAGED_STATES (DIR_ATTR_STATES | NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_FOCUS | \ NS_EVENT_STATE_HOVER | NS_EVENT_STATE_DRAGOVER | \ NS_EVENT_STATE_URLTARGET | NS_EVENT_STATE_FOCUSRING | \ - NS_EVENT_STATE_FULL_SCREEN | NS_EVENT_STATE_UNRESOLVED | \ - NS_EVENT_STATE_FOCUS_WITHIN) + NS_EVENT_STATE_FULL_SCREEN | NS_EVENT_STATE_FOCUS_WITHIN) #define INTRINSIC_STATES (~ESM_MANAGED_STATES) diff --git a/dom/events/MouseEvent.cpp b/dom/events/MouseEvent.cpp index 5e540ac5be..415e626c55 100644 --- a/dom/events/MouseEvent.cpp +++ b/dom/events/MouseEvent.cpp @@ -79,7 +79,7 @@ MouseEvent::InitMouseEvent(const nsAString& aType, case ePointerEventClass: case eSimpleGestureEventClass: { WidgetMouseEventBase* mouseEventBase = mEvent->AsMouseEventBase(); - mouseEventBase->relatedTarget = aRelatedTarget; + mouseEventBase->mRelatedTarget = aRelatedTarget; mouseEventBase->button = aButton; mouseEventBase->InitBasicModifiers(aCtrlKey, aAltKey, aShiftKey, aMetaKey); mClientPoint.x = aClientX; @@ -295,8 +295,7 @@ MouseEvent::GetRelatedTarget() case eDragEventClass: case ePointerEventClass: case eSimpleGestureEventClass: - relatedTarget = - do_QueryInterface(mEvent->AsMouseEventBase()->relatedTarget); + relatedTarget = mEvent->AsMouseEventBase()->mRelatedTarget; break; default: break; diff --git a/dom/events/test/mochitest.ini b/dom/events/test/mochitest.ini index 0397487bb8..27e8e7150e 100644 --- a/dom/events/test/mochitest.ini +++ b/dom/events/test/mochitest.ini @@ -185,3 +185,4 @@ skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM [test_bug687787.html] [test_bug1298970.html] [test_bug1304044.html] +[test_bug1305458.html] diff --git a/dom/events/test/test_bug1305458.html b/dom/events/test/test_bug1305458.html new file mode 100644 index 0000000000..df65959a97 --- /dev/null +++ b/dom/events/test/test_bug1305458.html @@ -0,0 +1,50 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1305458 +--> +<head> + <title>Test for Bug 1305458</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <style> + input[type=number] { + -moz-appearance: textfield; + } + input[type=number]:focus, + input[type=number]:hover { + -moz-appearance: number-input; + } + </style> +</head> +<body onload="doTest()"> + <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1305458">Mozilla Bug 1305458</a> + <input id="test_input" type="number"> + <div id="test_div">bar</div> + <script> + SimpleTest.waitForExplicitFinish(); + var change_count = 0; + function doTest() { + let input = document.getElementById("test_input"); + let div = document.getElementById("test_div"); + input.addEventListener("change", () => { + ++change_count; + }, false); + // mouse hover + input.focus(); + synthesizeMouse(input, 1, 1, {type: "mousemove"}); + synthesizeKey("1", {}); + input.blur(); + is(change_count, 1, "input should fire change when blur"); + + input.focus(); + synthesizeMouse(div, 1, 1, {type: "mousemove"}); + synthesizeKey("1", {}); + input.blur(); + is(change_count, 2, "input should fire change when blur"); + SimpleTest.finish(); + } + </script> +</body> +</html> diff --git a/dom/html/HTMLAnchorElement.cpp b/dom/html/HTMLAnchorElement.cpp index a6cfacc53d..0759af3395 100644 --- a/dom/html/HTMLAnchorElement.cpp +++ b/dom/html/HTMLAnchorElement.cpp @@ -14,6 +14,7 @@ #include "nsContentUtils.h" #include "nsGkAtoms.h" #include "nsHTMLDNSPrefetch.h" +#include "nsAttrValueOrString.h" #include "nsIDocument.h" #include "nsIPresShell.h" #include "nsPresContext.h" @@ -252,9 +253,9 @@ HTMLAnchorElement::IsHTMLFocusable(bool aWithMouse, } nsresult -HTMLAnchorElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +HTMLAnchorElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { - return PreHandleEventForAnchors(aVisitor); + return GetEventTargetParentForAnchors(aVisitor); } nsresult @@ -372,84 +373,37 @@ HTMLAnchorElement::GetHrefURI() const } nsresult -HTMLAnchorElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) +HTMLAnchorElement::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString* aValue, + bool aNotify) { - bool reset = false; - if (aName == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) { - // If we do not have a cached URI, we have some value here so we must reset - // our link state after calling the parent. - if (!Link::HasCachedURI()) { - reset = true; - } - // However, if we have a cached URI, we'll want to see if the value changed. - else { - nsAutoString val; - GetHref(val); - if (!val.Equals(aValue)) { - reset = true; - } - } - if (reset) { + if (aNamespaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::href) { CancelDNSPrefetch(HTML_ANCHOR_DNS_PREFETCH_DEFERRED, HTML_ANCHOR_DNS_PREFETCH_REQUESTED); } } - nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, - aValue, aNotify); - - // The ordering of the parent class's SetAttr call and Link::ResetLinkState - // is important here! The attribute is not set until SetAttr returns, and - // we will need the updated attribute value because notifying the document - // that content states have changed will call IntrinsicState, which will try - // to get updated information about the visitedness from Link. - if (reset) { - Link::ResetLinkState(!!aNotify, true); - if (IsInComposedDoc()) { - TryDNSPrefetch(); - } - } - - return rv; + return nsGenericHTMLElement::BeforeSetAttr(aNamespaceID, aName, aValue, + aNotify); } nsresult -HTMLAnchorElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, - bool aNotify) +HTMLAnchorElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { - bool href = - (aAttribute == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID); - - if (href) { - CancelDNSPrefetch(HTML_ANCHOR_DNS_PREFETCH_DEFERRED, - HTML_ANCHOR_DNS_PREFETCH_REQUESTED); - } - - nsresult rv = nsGenericHTMLElement::UnsetAttr(aNameSpaceID, aAttribute, - aNotify); - - // The ordering of the parent class's UnsetAttr call and Link::ResetLinkState - // is important here! The attribute is not unset until UnsetAttr returns, and - // we will need the updated attribute value because notifying the document - // that content states have changed will call IntrinsicState, which will try - // to get updated information about the visitedness from Link. - if (href) { - Link::ResetLinkState(!!aNotify, false); + if (aNamespaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::href) { + Link::ResetLinkState(aNotify, !!aValue); + if (aValue && IsInComposedDoc()) { + TryDNSPrefetch(); + } + } } - return rv; -} - -bool -HTMLAnchorElement::ParseAttribute(int32_t aNamespaceID, - nsIAtom* aAttribute, - const nsAString& aValue, - nsAttrValue& aResult) -{ - return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue, - aResult); + return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName, + aValue, aOldValue, aNotify); } EventStates diff --git a/dom/html/HTMLAnchorElement.h b/dom/html/HTMLAnchorElement.h index 2cb04ad939..0875559644 100644 --- a/dom/html/HTMLAnchorElement.h +++ b/dom/html/HTMLAnchorElement.h @@ -58,27 +58,21 @@ public: bool aNullParent = true) override; virtual bool IsHTMLFocusable(bool aWithMouse, bool *aIsFocusable, int32_t *aTabIndex) override; - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; virtual nsresult PostHandleEvent( EventChainPostVisitor& aVisitor) override; virtual bool IsLink(nsIURI** aURI) const override; virtual void GetLinkTarget(nsAString& aTarget) override; virtual already_AddRefed<nsIURI> GetHrefURI() const override; - nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAString& aValue, bool aNotify) - { - return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify); - } - virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) override; - virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, - bool aNotify) override; - virtual bool ParseAttribute(int32_t aNamespaceID, - nsIAtom* aAttribute, - const nsAString& aValue, - nsAttrValue& aResult) override; + virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString* aValue, + bool aNotify) override; + virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; diff --git a/dom/html/HTMLAreaElement.cpp b/dom/html/HTMLAreaElement.cpp index 098081b8b0..4c85774f45 100644 --- a/dom/html/HTMLAreaElement.cpp +++ b/dom/html/HTMLAreaElement.cpp @@ -81,9 +81,9 @@ HTMLAreaElement::SetTarget(const nsAString& aValue) } nsresult -HTMLAreaElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +HTMLAreaElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { - return PreHandleEventForAnchors(aVisitor); + return GetEventTargetParentForAnchors(aVisitor); } nsresult @@ -153,42 +153,22 @@ HTMLAreaElement::UnbindFromTree(bool aDeep, bool aNullParent) } nsresult -HTMLAreaElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) -{ - nsresult rv = - nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue, aNotify); - - // The ordering of the parent class's SetAttr call and Link::ResetLinkState - // is important here! The attribute is not set until SetAttr returns, and - // we will need the updated attribute value because notifying the document - // that content states have changed will call IntrinsicState, which will try - // to get updated information about the visitedness from Link. - if (aName == nsGkAtoms::href && aNameSpaceID == kNameSpaceID_None) { - Link::ResetLinkState(!!aNotify, true); +HTMLAreaElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) +{ + if (aNamespaceID == kNameSpaceID_None) { + // This must happen after the attribute is set. We will need the updated + // attribute value because notifying the document that content states have + // changed will call IntrinsicState, which will try to get updated + // information about the visitedness from Link. + if (aName == nsGkAtoms::href) { + Link::ResetLinkState(aNotify, !!aValue); + } } - return rv; -} - -nsresult -HTMLAreaElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, - bool aNotify) -{ - nsresult rv = nsGenericHTMLElement::UnsetAttr(aNameSpaceID, aAttribute, - aNotify); - - // The ordering of the parent class's UnsetAttr call and Link::ResetLinkState - // is important here! The attribute is not unset until UnsetAttr returns, and - // we will need the updated attribute value because notifying the document - // that content states have changed will call IntrinsicState, which will try - // to get updated information about the visitedness from Link. - if (aAttribute == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) { - Link::ResetLinkState(!!aNotify, false); - } - - return rv; + return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName, aValue, + aOldValue, aNotify); } #define IMPL_URI_PART(_part) \ diff --git a/dom/html/HTMLAreaElement.h b/dom/html/HTMLAreaElement.h index 650c0fd8fd..d408934c99 100644 --- a/dom/html/HTMLAreaElement.h +++ b/dom/html/HTMLAreaElement.h @@ -44,7 +44,8 @@ public: // nsIDOMHTMLAreaElement NS_DECL_NSIDOMHTMLAREAELEMENT - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; virtual nsresult PostHandleEvent(EventChainPostVisitor& aVisitor) override; virtual bool IsLink(nsIURI** aURI) const override; virtual void GetLinkTarget(nsAString& aTarget) override; @@ -55,16 +56,6 @@ public: bool aCompileEventHandlers) override; virtual void UnbindFromTree(bool aDeep = true, bool aNullParent = true) override; - nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAString& aValue, bool aNotify) - { - return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify); - } - virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) override; - virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, - bool aNotify) override; virtual nsresult Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) const override; @@ -185,6 +176,11 @@ protected: virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; + RefPtr<nsDOMTokenList > mRelList; }; diff --git a/dom/html/HTMLBodyElement.cpp b/dom/html/HTMLBodyElement.cpp index 6230cb6ca0..112d48dd15 100644 --- a/dom/html/HTMLBodyElement.cpp +++ b/dom/html/HTMLBodyElement.cpp @@ -27,180 +27,8 @@ namespace dom { //---------------------------------------------------------------------- -BodyRule::BodyRule(HTMLBodyElement* aPart) - : mPart(aPart) -{ -} - -BodyRule::~BodyRule() -{ -} - -NS_IMPL_ISUPPORTS(BodyRule, nsIStyleRule) - -/* virtual */ void -BodyRule::MapRuleInfoInto(nsRuleData* aData) -{ - if (!(aData->mSIDs & NS_STYLE_INHERIT_BIT(Margin)) || !mPart) - return; // We only care about margins. - - int32_t bodyMarginWidth = -1; - int32_t bodyMarginHeight = -1; - int32_t bodyTopMargin = -1; - int32_t bodyBottomMargin = -1; - int32_t bodyLeftMargin = -1; - int32_t bodyRightMargin = -1; - - // check the mode (fortunately, the ruleData has a presContext for us to use!) - NS_ASSERTION(aData->mPresContext, "null presContext in ruleNode was unexpected"); - nsCompatibility mode = aData->mPresContext->CompatibilityMode(); - - - const nsAttrValue* value; - if (mPart->GetAttrCount() > 0) { - // if marginwidth/marginheight are set, reflect them as 'margin' - value = mPart->GetParsedAttr(nsGkAtoms::marginwidth); - if (value && value->Type() == nsAttrValue::eInteger) { - bodyMarginWidth = value->GetIntegerValue(); - if (bodyMarginWidth < 0) bodyMarginWidth = 0; - nsCSSValue* marginLeft = aData->ValueForMarginLeft(); - if (marginLeft->GetUnit() == eCSSUnit_Null) - marginLeft->SetFloatValue((float)bodyMarginWidth, eCSSUnit_Pixel); - nsCSSValue* marginRight = aData->ValueForMarginRight(); - if (marginRight->GetUnit() == eCSSUnit_Null) - marginRight->SetFloatValue((float)bodyMarginWidth, eCSSUnit_Pixel); - } - - value = mPart->GetParsedAttr(nsGkAtoms::marginheight); - if (value && value->Type() == nsAttrValue::eInteger) { - bodyMarginHeight = value->GetIntegerValue(); - if (bodyMarginHeight < 0) bodyMarginHeight = 0; - nsCSSValue* marginTop = aData->ValueForMarginTop(); - if (marginTop->GetUnit() == eCSSUnit_Null) - marginTop->SetFloatValue((float)bodyMarginHeight, eCSSUnit_Pixel); - nsCSSValue* marginBottom = aData->ValueForMarginBottom(); - if (marginBottom->GetUnit() == eCSSUnit_Null) - marginBottom->SetFloatValue((float)bodyMarginHeight, eCSSUnit_Pixel); - } - - // topmargin (IE-attribute) - value = mPart->GetParsedAttr(nsGkAtoms::topmargin); - if (value && value->Type() == nsAttrValue::eInteger) { - bodyTopMargin = value->GetIntegerValue(); - if (bodyTopMargin < 0) bodyTopMargin = 0; - nsCSSValue* marginTop = aData->ValueForMarginTop(); - if (marginTop->GetUnit() == eCSSUnit_Null) - marginTop->SetFloatValue((float)bodyTopMargin, eCSSUnit_Pixel); - } - - // bottommargin (IE-attribute) - value = mPart->GetParsedAttr(nsGkAtoms::bottommargin); - if (value && value->Type() == nsAttrValue::eInteger) { - bodyBottomMargin = value->GetIntegerValue(); - if (bodyBottomMargin < 0) bodyBottomMargin = 0; - nsCSSValue* marginBottom = aData->ValueForMarginBottom(); - if (marginBottom->GetUnit() == eCSSUnit_Null) - marginBottom->SetFloatValue((float)bodyBottomMargin, eCSSUnit_Pixel); - } - - // leftmargin (IE-attribute) - value = mPart->GetParsedAttr(nsGkAtoms::leftmargin); - if (value && value->Type() == nsAttrValue::eInteger) { - bodyLeftMargin = value->GetIntegerValue(); - if (bodyLeftMargin < 0) bodyLeftMargin = 0; - nsCSSValue* marginLeft = aData->ValueForMarginLeft(); - if (marginLeft->GetUnit() == eCSSUnit_Null) - marginLeft->SetFloatValue((float)bodyLeftMargin, eCSSUnit_Pixel); - } - - // rightmargin (IE-attribute) - value = mPart->GetParsedAttr(nsGkAtoms::rightmargin); - if (value && value->Type() == nsAttrValue::eInteger) { - bodyRightMargin = value->GetIntegerValue(); - if (bodyRightMargin < 0) bodyRightMargin = 0; - nsCSSValue* marginRight = aData->ValueForMarginRight(); - if (marginRight->GetUnit() == eCSSUnit_Null) - marginRight->SetFloatValue((float)bodyRightMargin, eCSSUnit_Pixel); - } - - } - - // if marginwidth or marginheight is set in the <frame> and not set in the <body> - // reflect them as margin in the <body> - if (bodyMarginWidth == -1 || bodyMarginHeight == -1) { - nsCOMPtr<nsIDocShell> docShell(aData->mPresContext->GetDocShell()); - if (docShell) { - nscoord frameMarginWidth=-1; // default value - nscoord frameMarginHeight=-1; // default value - docShell->GetMarginWidth(&frameMarginWidth); // -1 indicates not set - docShell->GetMarginHeight(&frameMarginHeight); - if ((frameMarginWidth >= 0) && (bodyMarginWidth == -1)) { // set in <frame> & not in <body> - if (eCompatibility_NavQuirks == mode) { - if ((bodyMarginHeight == -1) && (0 > frameMarginHeight)) // nav quirk - frameMarginHeight = 0; - } - } - if ((frameMarginHeight >= 0) && (bodyMarginHeight == -1)) { // set in <frame> & not in <body> - if (eCompatibility_NavQuirks == mode) { - if ((bodyMarginWidth == -1) && (0 > frameMarginWidth)) // nav quirk - frameMarginWidth = 0; - } - } - - if ((bodyMarginWidth == -1) && (frameMarginWidth >= 0)) { - nsCSSValue* marginLeft = aData->ValueForMarginLeft(); - if (marginLeft->GetUnit() == eCSSUnit_Null) - marginLeft->SetFloatValue((float)frameMarginWidth, eCSSUnit_Pixel); - nsCSSValue* marginRight = aData->ValueForMarginRight(); - if (marginRight->GetUnit() == eCSSUnit_Null) - marginRight->SetFloatValue((float)frameMarginWidth, eCSSUnit_Pixel); - } - - if ((bodyMarginHeight == -1) && (frameMarginHeight >= 0)) { - nsCSSValue* marginTop = aData->ValueForMarginTop(); - if (marginTop->GetUnit() == eCSSUnit_Null) - marginTop->SetFloatValue((float)frameMarginHeight, eCSSUnit_Pixel); - nsCSSValue* marginBottom = aData->ValueForMarginBottom(); - if (marginBottom->GetUnit() == eCSSUnit_Null) - marginBottom->SetFloatValue((float)frameMarginHeight, eCSSUnit_Pixel); - } - } - } -} - -/* virtual */ bool -BodyRule::MightMapInheritedStyleData() -{ - return false; -} - -/* virtual */ bool -BodyRule::GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty, - nsCSSValue* aValue) -{ - MOZ_ASSERT(false, "GetDiscretelyAnimatedCSSValue is not implemented yet"); - return false; -} - -#ifdef DEBUG -/* virtual */ void -BodyRule::List(FILE* out, int32_t aIndent) const -{ - nsAutoCString indent; - for (int32_t index = aIndent; --index >= 0; ) { - indent.AppendLiteral(" "); - } - fprintf_stderr(out, "%s[body rule] {}\n", indent.get()); -} -#endif - -//---------------------------------------------------------------------- - HTMLBodyElement::~HTMLBodyElement() { - if (mContentStyleRule) { - mContentStyleRule->mPart = nullptr; - } } JSObject* @@ -348,17 +176,6 @@ HTMLBodyElement::ParseAttribute(int32_t aNamespaceID, } void -HTMLBodyElement::UnbindFromTree(bool aDeep, bool aNullParent) -{ - if (mContentStyleRule) { - mContentStyleRule->mPart = nullptr; - mContentStyleRule = nullptr; - } - - nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); -} - -void HTMLBodyElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes, nsRuleData* aData) { @@ -413,22 +230,6 @@ HTMLBodyElement::GetAttributeMappingFunction() const return &MapAttributesIntoRule; } -NS_IMETHODIMP -HTMLBodyElement::WalkContentStyleRules(nsRuleWalker* aRuleWalker) -{ - nsGenericHTMLElement::WalkContentStyleRules(aRuleWalker); - - if (!mContentStyleRule && IsInUncomposedDoc()) { - // XXXbz should this use OwnerDoc() or GetComposedDoc()? - // sXBL/XBL2 issue! - mContentStyleRule = new BodyRule(this); - } - if (aRuleWalker && mContentStyleRule) { - aRuleWalker->Forward(mContentStyleRule); - } - return NS_OK; -} - NS_IMETHODIMP_(bool) HTMLBodyElement::IsAttributeMapped(const nsIAtom* aAttribute) const { @@ -437,12 +238,12 @@ HTMLBodyElement::IsAttributeMapped(const nsIAtom* aAttribute) const { &nsGkAtoms::vlink }, { &nsGkAtoms::alink }, { &nsGkAtoms::text }, - // These aren't mapped through attribute mapping, but they are - // mapped through a style rule, so it is attribute dependent style. - // XXXldb But we don't actually replace the body rule when we have - // dynamic changes... { &nsGkAtoms::marginwidth }, { &nsGkAtoms::marginheight }, + { &nsGkAtoms::topmargin }, + { &nsGkAtoms::rightmargin }, + { &nsGkAtoms::bottommargin }, + { &nsGkAtoms::leftmargin }, { nullptr }, }; @@ -491,6 +292,37 @@ HTMLBodyElement::IsEventAttributeName(nsIAtom *aName) EventNameType_HTMLBodyOrFramesetOnly); } +nsresult +HTMLBodyElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) +{ + nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent, + aBindingParent, + aCompileEventHandlers); + NS_ENSURE_SUCCESS(rv, rv); + return mAttrsAndChildren.ForceMapped(this, OwnerDoc()); +} + +nsresult +HTMLBodyElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) +{ + nsresult rv = nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, + aName, aValue, aOldValue, + aNotify); + NS_ENSURE_SUCCESS(rv, rv); + // if the last mapped attribute was removed, don't clear the + // nsMappedAttributes, our style can still depend on the containing frame element + if (!aValue && IsAttributeMapped(aName)) { + nsresult rv = mAttrsAndChildren.ForceMapped(this, OwnerDoc()); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + #define EVENT(name_, id_, type_, struct_) /* nothing; handled by the superclass */ // nsGenericHTMLElement::GetOnError returns // already_AddRefed<EventHandlerNonNull> while other getters return diff --git a/dom/html/HTMLBodyElement.h b/dom/html/HTMLBodyElement.h index 436dc4cba3..6a888c71b7 100644 --- a/dom/html/HTMLBodyElement.h +++ b/dom/html/HTMLBodyElement.h @@ -15,28 +15,6 @@ namespace mozilla { namespace dom { class OnBeforeUnloadEventHandlerNonNull; -class HTMLBodyElement; - -class BodyRule: public nsIStyleRule -{ - virtual ~BodyRule(); - -public: - explicit BodyRule(HTMLBodyElement* aPart); - - NS_DECL_ISUPPORTS - - // nsIStyleRule interface - virtual void MapRuleInfoInto(nsRuleData* aRuleData) override; - virtual bool MightMapInheritedStyleData() override; - virtual bool GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty, - nsCSSValue* aValue) override; -#ifdef DEBUG - virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override; -#endif - - HTMLBodyElement* mPart; // not ref-counted, cleared by content -}; class HTMLBodyElement final : public nsGenericHTMLElement, public nsIDOMHTMLBodyElement @@ -125,23 +103,29 @@ public: nsIAtom* aAttribute, const nsAString& aValue, nsAttrValue& aResult) override; - virtual void UnbindFromTree(bool aDeep = true, - bool aNullParent = true) override; virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() const override; - NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker) override; NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; virtual already_AddRefed<nsIEditor> GetAssociatedEditor() override; virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; virtual bool IsEventAttributeName(nsIAtom* aName) override; + virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) override; + /** + * Called when an attribute has just been changed + */ + virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; + protected: virtual ~HTMLBodyElement(); virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override; - RefPtr<BodyRule> mContentStyleRule; - private: static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes, nsRuleData* aData); diff --git a/dom/html/HTMLButtonElement.cpp b/dom/html/HTMLButtonElement.cpp index 435aa9f7fd..1a76aac10a 100644 --- a/dom/html/HTMLButtonElement.cpp +++ b/dom/html/HTMLButtonElement.cpp @@ -207,7 +207,7 @@ HTMLButtonElement::IsDisabledForEvents(EventMessage aMessage) } nsresult -HTMLButtonElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +HTMLButtonElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.mCanHandle = false; if (IsDisabledForEvents(aVisitor.mEvent->mMessage)) { @@ -235,7 +235,7 @@ HTMLButtonElement::PreHandleEvent(EventChainPreVisitor& aVisitor) } } - return nsGenericHTMLElement::PreHandleEvent(aVisitor); + return nsGenericHTMLElement::GetEventTargetParent(aVisitor); } nsresult @@ -423,7 +423,7 @@ HTMLButtonElement::DoneCreatingElement() nsresult HTMLButtonElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsAttrValueOrString* aValue, + const nsAttrValueOrString* aValue, bool aNotify) { if (aNotify && aName == nsGkAtoms::disabled && @@ -437,7 +437,8 @@ HTMLButtonElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, nsresult HTMLButtonElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { if (aNameSpaceID == kNameSpaceID_None) { if (aName == nsGkAtoms::type) { @@ -448,12 +449,12 @@ HTMLButtonElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, if (aName == nsGkAtoms::type || aName == nsGkAtoms::disabled) { UpdateBarredFromConstraintValidation(); - UpdateState(aNotify); } } return nsGenericHTMLFormElementWithState::AfterSetAttr(aNameSpaceID, aName, - aValue, aNotify); + aValue, aOldValue, + aNotify); } NS_IMETHODIMP diff --git a/dom/html/HTMLButtonElement.h b/dom/html/HTMLButtonElement.h index ecd9e03d73..8bab9fd48e 100644 --- a/dom/html/HTMLButtonElement.h +++ b/dom/html/HTMLButtonElement.h @@ -57,7 +57,8 @@ public: virtual void FieldSetDisabledChanged(bool aNotify) override; // nsIDOMEventTarget - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; virtual nsresult PostHandleEvent( EventChainPostVisitor& aVisitor) override; @@ -80,13 +81,15 @@ public: * Called when an attribute is about to be changed */ virtual nsresult BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsAttrValueOrString* aValue, + const nsAttrValueOrString* aValue, bool aNotify) override; /** * Called when an attribute has just been changed */ - nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) override; + virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; virtual bool ParseAttribute(int32_t aNamespaceID, nsIAtom* aAttribute, const nsAString& aValue, diff --git a/dom/html/HTMLCanvasElement.cpp b/dom/html/HTMLCanvasElement.cpp index 4b5deab187..ba1fbedbb0 100644 --- a/dom/html/HTMLCanvasElement.cpp +++ b/dom/html/HTMLCanvasElement.cpp @@ -446,38 +446,37 @@ NS_IMPL_UINT_ATTR_DEFAULT_VALUE(HTMLCanvasElement, Height, height, DEFAULT_CANVA NS_IMPL_BOOL_ATTR(HTMLCanvasElement, MozOpaque, moz_opaque) nsresult -HTMLCanvasElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) -{ - nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue, - aNotify); - if (NS_SUCCEEDED(rv) && mCurrentContext && - aNameSpaceID == kNameSpaceID_None && - (aName == nsGkAtoms::width || aName == nsGkAtoms::height || aName == nsGkAtoms::moz_opaque)) - { - ErrorResult dummy; - rv = UpdateContext(nullptr, JS::NullHandleValue, dummy); - NS_ENSURE_SUCCESS(rv, rv); - } +HTMLCanvasElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) +{ + AfterMaybeChangeAttr(aNamespaceID, aName, aNotify); - return rv; + return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName, aValue, + aOldValue, aNotify); } nsresult -HTMLCanvasElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName, - bool aNotify) +HTMLCanvasElement::OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString& aValue, + bool aNotify) { - nsresult rv = nsGenericHTMLElement::UnsetAttr(aNameSpaceID, aName, aNotify); - if (NS_SUCCEEDED(rv) && mCurrentContext && - aNameSpaceID == kNameSpaceID_None && - (aName == nsGkAtoms::width || aName == nsGkAtoms::height || aName == nsGkAtoms::moz_opaque)) - { + AfterMaybeChangeAttr(aNamespaceID, aName, aNotify); + + return nsGenericHTMLElement::OnAttrSetButNotChanged(aNamespaceID, aName, + aValue, aNotify); +} + +void +HTMLCanvasElement::AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName, + bool aNotify) +{ + if (mCurrentContext && aNamespaceID == kNameSpaceID_None && + (aName == nsGkAtoms::width || aName == nsGkAtoms::height || + aName == nsGkAtoms::moz_opaque)) { ErrorResult dummy; - rv = UpdateContext(nullptr, JS::NullHandleValue, dummy); - NS_ENSURE_SUCCESS(rv, rv); + UpdateContext(nullptr, JS::NullHandleValue, dummy); } - return rv; } void @@ -574,7 +573,7 @@ HTMLCanvasElement::CopyInnerTo(Element* aDest) return rv; } -nsresult HTMLCanvasElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +nsresult HTMLCanvasElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { if (aVisitor.mEvent->mClass == eMouseEventClass) { WidgetMouseEventBase* evt = (WidgetMouseEventBase*)aVisitor.mEvent; @@ -592,7 +591,7 @@ nsresult HTMLCanvasElement::PreHandleEvent(EventChainPreVisitor& aVisitor) aVisitor.mCanHandle = true; } } - return nsGenericHTMLElement::PreHandleEvent(aVisitor); + return nsGenericHTMLElement::GetEventTargetParent(aVisitor); } nsChangeHint diff --git a/dom/html/HTMLCanvasElement.h b/dom/html/HTMLCanvasElement.h index e77db6ff1a..822f053978 100644 --- a/dom/html/HTMLCanvasElement.h +++ b/dom/html/HTMLCanvasElement.h @@ -295,24 +295,11 @@ public: nsAttrValue& aResult) override; nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute, int32_t aModType) const override; - // SetAttr override. C++ is stupid, so have to override both - // overloaded methods. - nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAString& aValue, bool aNotify) - { - return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify); - } - virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) override; - - virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName, - bool aNotify) override; - virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; nsresult CopyInnerTo(mozilla::dom::Element* aDest); - virtual nsresult PreHandleEvent(mozilla::EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + mozilla::EventChainPreVisitor& aVisitor) override; /* * Helpers called by various users of Canvas @@ -372,6 +359,14 @@ protected: nsISupports** aResult); void CallPrintCallback(); + virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; + virtual nsresult OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString& aValue, + bool aNotify) override; + AsyncCanvasRenderer* GetAsyncCanvasRenderer(); bool mResetLayer; @@ -405,6 +400,18 @@ public: CanvasContextType GetCurrentContextType() { return mCurrentContextType; } + +private: + /** + * This function is called by AfterSetAttr and OnAttrSetButNotChanged. + * This function will be called by AfterSetAttr whether the attribute is being + * set or unset. + * + * @param aNamespaceID the namespace of the attr being set + * @param aName the localname of the attribute being set + * @param aNotify Whether we plan to notify document observers. + */ + void AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName, bool aNotify); }; class HTMLCanvasPrintState final : public nsWrapperCache diff --git a/dom/html/HTMLContentElement.cpp b/dom/html/HTMLContentElement.cpp deleted file mode 100644 index 01c0158a03..0000000000 --- a/dom/html/HTMLContentElement.cpp +++ /dev/null @@ -1,393 +0,0 @@ -/* -*- 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 "mozilla/dom/HTMLContentElement.h" -#include "mozilla/dom/HTMLContentElementBinding.h" -#include "mozilla/dom/HTMLUnknownElement.h" -#include "mozilla/dom/NodeListBinding.h" -#include "mozilla/dom/ShadowRoot.h" -#include "mozilla/css/StyleRule.h" -#include "nsGkAtoms.h" -#include "nsStyleConsts.h" -#include "nsIAtom.h" -#include "nsCSSRuleProcessor.h" -#include "nsRuleData.h" -#include "nsRuleProcessorData.h" -#include "nsRuleWalker.h" -#include "nsCSSParser.h" -#include "nsDocument.h" - -// Expand NS_IMPL_NS_NEW_HTML_ELEMENT(Content) to add check for web components -// being enabled. -nsGenericHTMLElement* -NS_NewHTMLContentElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, - mozilla::dom::FromParser aFromParser) -{ - // When this check is removed, remove the nsDocument.h and - // HTMLUnknownElement.h includes. Also remove nsINode::IsHTMLContentElement. - // - // We have to jump through some hoops to be able to produce both NodeInfo* and - // already_AddRefed<NodeInfo>& for our callees. - RefPtr<mozilla::dom::NodeInfo> nodeInfo(aNodeInfo); - if (!nsDocument::IsWebComponentsEnabled(nodeInfo)) { - already_AddRefed<mozilla::dom::NodeInfo> nodeInfoArg(nodeInfo.forget()); - return new mozilla::dom::HTMLUnknownElement(nodeInfoArg); - } - - already_AddRefed<mozilla::dom::NodeInfo> nodeInfoArg(nodeInfo.forget()); - return new mozilla::dom::HTMLContentElement(nodeInfoArg); -} - -using namespace mozilla::dom; - -HTMLContentElement::HTMLContentElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) - : nsGenericHTMLElement(aNodeInfo), mValidSelector(true), mIsInsertionPoint(false) -{ -} - -HTMLContentElement::~HTMLContentElement() -{ -} - -NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLContentElement, - nsGenericHTMLElement, - mMatchedNodes) - -NS_IMPL_ADDREF_INHERITED(HTMLContentElement, Element) -NS_IMPL_RELEASE_INHERITED(HTMLContentElement, Element) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLContentElement) -NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement) - -NS_IMPL_ELEMENT_CLONE(HTMLContentElement) - -JSObject* -HTMLContentElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) -{ - return HTMLContentElementBinding::Wrap(aCx, this, aGivenProto); -} - -nsresult -HTMLContentElement::BindToTree(nsIDocument* aDocument, - nsIContent* aParent, - nsIContent* aBindingParent, - bool aCompileEventHandlers) -{ - RefPtr<ShadowRoot> oldContainingShadow = GetContainingShadow(); - - nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent, - aBindingParent, - aCompileEventHandlers); - NS_ENSURE_SUCCESS(rv, rv); - - ShadowRoot* containingShadow = GetContainingShadow(); - if (containingShadow && !oldContainingShadow) { - nsINode* parentNode = nsINode::GetParentNode(); - while (parentNode && parentNode != containingShadow) { - if (parentNode->IsHTMLContentElement()) { - // Content element in fallback content is not an insertion point. - return NS_OK; - } - parentNode = parentNode->GetParentNode(); - } - - // If the content element is being inserted into a ShadowRoot, - // add this element to the list of insertion points. - mIsInsertionPoint = true; - containingShadow->AddInsertionPoint(this); - containingShadow->SetInsertionPointChanged(); - } - - return NS_OK; -} - -void -HTMLContentElement::UnbindFromTree(bool aDeep, bool aNullParent) -{ - RefPtr<ShadowRoot> oldContainingShadow = GetContainingShadow(); - - nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); - - if (oldContainingShadow && !GetContainingShadow() && mIsInsertionPoint) { - oldContainingShadow->RemoveInsertionPoint(this); - - // Remove all the matched nodes now that the - // insertion point is no longer an insertion point. - ClearMatchedNodes(); - oldContainingShadow->SetInsertionPointChanged(); - - mIsInsertionPoint = false; - } -} - -void -HTMLContentElement::AppendMatchedNode(nsIContent* aContent) -{ - mMatchedNodes.AppendElement(aContent); - nsTArray<nsIContent*>& destInsertionPoint = aContent->DestInsertionPoints(); - destInsertionPoint.AppendElement(this); - - if (mMatchedNodes.Length() == 1) { - // Fallback content gets dropped so we need to updated fallback - // content distribution. - UpdateFallbackDistribution(); - } -} - -void -HTMLContentElement::UpdateFallbackDistribution() -{ - for (nsIContent* child = nsINode::GetFirstChild(); - child; - child = child->GetNextSibling()) { - nsTArray<nsIContent*>& destInsertionPoint = child->DestInsertionPoints(); - destInsertionPoint.Clear(); - if (mMatchedNodes.IsEmpty()) { - destInsertionPoint.AppendElement(this); - } - } -} - -void -HTMLContentElement::RemoveMatchedNode(nsIContent* aContent) -{ - mMatchedNodes.RemoveElement(aContent); - ShadowRoot::RemoveDestInsertionPoint(this, aContent->DestInsertionPoints()); - - if (mMatchedNodes.IsEmpty()) { - // Fallback content is activated so we need to update fallback - // content distribution. - UpdateFallbackDistribution(); - } -} - -void -HTMLContentElement::InsertMatchedNode(uint32_t aIndex, nsIContent* aContent) -{ - mMatchedNodes.InsertElementAt(aIndex, aContent); - nsTArray<nsIContent*>& destInsertionPoint = aContent->DestInsertionPoints(); - destInsertionPoint.AppendElement(this); - - if (mMatchedNodes.Length() == 1) { - // Fallback content gets dropped so we need to updated fallback - // content distribution. - UpdateFallbackDistribution(); - } -} - -void -HTMLContentElement::ClearMatchedNodes() -{ - for (uint32_t i = 0; i < mMatchedNodes.Length(); i++) { - ShadowRoot::RemoveDestInsertionPoint(this, mMatchedNodes[i]->DestInsertionPoints()); - } - - mMatchedNodes.Clear(); - - UpdateFallbackDistribution(); -} - -static bool -IsValidContentSelectors(nsCSSSelector* aSelector) -{ - nsCSSSelector* currentSelector = aSelector; - while (currentSelector) { - // Blacklist invalid selector fragments. - if (currentSelector->IsPseudoElement() || - currentSelector->mPseudoClassList || - currentSelector->mNegations || - currentSelector->mOperator) { - return false; - } - - currentSelector = currentSelector->mNext; - } - - return true; -} - -nsresult -HTMLContentElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) -{ - nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, - aValue, aNotify); - NS_ENSURE_SUCCESS(rv, rv); - - if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::select) { - // Select attribute was updated, the insertion point may match different - // elements. - nsIDocument* doc = OwnerDoc(); - nsCSSParser parser(doc->CSSLoader()); - - mValidSelector = true; - mSelectorList = nullptr; - - nsresult rv = parser.ParseSelectorString(aValue, - doc->GetDocumentURI(), - // Bug 11240 - 0, // XXX get the line number! - getter_Transfers(mSelectorList)); - - // We don't want to return an exception if parsing failed because - // the spec does not define it as an exception case. - if (NS_SUCCEEDED(rv)) { - // Ensure that all the selectors are valid - nsCSSSelectorList* selectors = mSelectorList; - while (selectors) { - if (!IsValidContentSelectors(selectors->mSelectors)) { - // If we have an invalid selector, we can not match anything. - mValidSelector = false; - mSelectorList = nullptr; - break; - } - selectors = selectors->mNext; - } - } - - ShadowRoot* containingShadow = GetContainingShadow(); - if (containingShadow) { - containingShadow->DistributeAllNodes(); - } - } - - return NS_OK; -} - -nsresult -HTMLContentElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, - bool aNotify) -{ - nsresult rv = nsGenericHTMLElement::UnsetAttr(aNameSpaceID, - aAttribute, aNotify); - NS_ENSURE_SUCCESS(rv, rv); - - if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::select) { - // The select attribute was removed. This insertion point becomes - // a universal selector. - mValidSelector = true; - mSelectorList = nullptr; - - ShadowRoot* containingShadow = GetContainingShadow(); - if (containingShadow) { - containingShadow->DistributeAllNodes(); - } - } - - return NS_OK; -} - -bool -HTMLContentElement::Match(nsIContent* aContent) -{ - if (!mValidSelector) { - return false; - } - - if (mSelectorList) { - nsIDocument* doc = OwnerDoc(); - ShadowRoot* containingShadow = GetContainingShadow(); - nsIContent* host = containingShadow->GetHost(); - - TreeMatchContext matchingContext(false, nsRuleWalker::eRelevantLinkUnvisited, - doc, TreeMatchContext::eNeverMatchVisited); - doc->FlushPendingLinkUpdates(); - matchingContext.SetHasSpecifiedScope(); - matchingContext.AddScopeElement(host->AsElement()); - - if (!aContent->IsElement()) { - return false; - } - - return nsCSSRuleProcessor::SelectorListMatches(aContent->AsElement(), - matchingContext, - mSelectorList); - } - - return true; -} - -already_AddRefed<DistributedContentList> -HTMLContentElement::GetDistributedNodes() -{ - RefPtr<DistributedContentList> list = new DistributedContentList(this); - return list.forget(); -} - -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DistributedContentList, mParent, - mDistributedNodes) - -NS_INTERFACE_TABLE_HEAD(DistributedContentList) - NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY - NS_INTERFACE_TABLE(DistributedContentList, nsINodeList, nsIDOMNodeList) - NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(DistributedContentList) -NS_INTERFACE_MAP_END - -NS_IMPL_CYCLE_COLLECTING_ADDREF(DistributedContentList) -NS_IMPL_CYCLE_COLLECTING_RELEASE(DistributedContentList) - -DistributedContentList::DistributedContentList(HTMLContentElement* aHostElement) - : mParent(aHostElement) -{ - MOZ_COUNT_CTOR(DistributedContentList); - - if (aHostElement->IsInsertionPoint()) { - if (aHostElement->MatchedNodes().IsEmpty()) { - // Fallback content. - nsINode* contentNode = aHostElement; - for (nsIContent* content = contentNode->GetFirstChild(); - content; - content = content->GetNextSibling()) { - mDistributedNodes.AppendElement(content); - } - } else { - mDistributedNodes.AppendElements(aHostElement->MatchedNodes()); - } - } -} - -DistributedContentList::~DistributedContentList() -{ - MOZ_COUNT_DTOR(DistributedContentList); -} - -nsIContent* -DistributedContentList::Item(uint32_t aIndex) -{ - return mDistributedNodes.SafeElementAt(aIndex); -} - -NS_IMETHODIMP -DistributedContentList::Item(uint32_t aIndex, nsIDOMNode** aReturn) -{ - nsIContent* item = Item(aIndex); - if (!item) { - return NS_ERROR_FAILURE; - } - - return CallQueryInterface(item, aReturn); -} - -NS_IMETHODIMP -DistributedContentList::GetLength(uint32_t* aLength) -{ - *aLength = mDistributedNodes.Length(); - return NS_OK; -} - -int32_t -DistributedContentList::IndexOf(nsIContent* aContent) -{ - return mDistributedNodes.IndexOf(aContent); -} - -JSObject* -DistributedContentList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) -{ - return NodeListBinding::Wrap(aCx, this, aGivenProto); -} - diff --git a/dom/html/HTMLContentElement.h b/dom/html/HTMLContentElement.h deleted file mode 100644 index f019e56cda..0000000000 --- a/dom/html/HTMLContentElement.h +++ /dev/null @@ -1,135 +0,0 @@ -/* -*- 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_HTMLContentElement_h__ -#define mozilla_dom_HTMLContentElement_h__ - -#include "nsAutoPtr.h" -#include "nsINodeList.h" -#include "nsGenericHTMLElement.h" - -struct nsCSSSelectorList; - -namespace mozilla { -namespace dom { - -class DistributedContentList; - -class HTMLContentElement final : public nsGenericHTMLElement -{ -public: - explicit HTMLContentElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); - - // nsISupports - NS_DECL_ISUPPORTS_INHERITED - - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLContentElement, - nsGenericHTMLElement) - - static HTMLContentElement* FromContent(nsIContent* aContent) - { - if (aContent->IsHTMLContentElement()) { - return static_cast<HTMLContentElement*>(aContent); - } - - return nullptr; - } - - virtual bool IsHTMLContentElement() const override { return true; } - - virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; - - virtual nsIDOMNode* AsDOMNode() override { return this; } - - virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, - nsIContent* aBindingParent, - bool aCompileEventHandlers) override; - - virtual void UnbindFromTree(bool aDeep = true, - bool aNullParent = true) override; - - /** - * Returns whether if the selector of this insertion point - * matches the provided content. - */ - bool Match(nsIContent* aContent); - bool IsInsertionPoint() const { return mIsInsertionPoint; } - nsCOMArray<nsIContent>& MatchedNodes() { return mMatchedNodes; } - void AppendMatchedNode(nsIContent* aContent); - void RemoveMatchedNode(nsIContent* aContent); - void InsertMatchedNode(uint32_t aIndex, nsIContent* aContent); - void ClearMatchedNodes(); - - virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) override; - - virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, - bool aNotify) override; - - // WebIDL methods. - already_AddRefed<DistributedContentList> GetDistributedNodes(); - void GetSelect(nsAString& aSelect) - { - Element::GetAttr(kNameSpaceID_None, nsGkAtoms::select, aSelect); - } - void SetSelect(const nsAString& aSelect) - { - Element::SetAttr(kNameSpaceID_None, nsGkAtoms::select, aSelect, true); - } - -protected: - virtual ~HTMLContentElement(); - - virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override; - - /** - * Updates the destination insertion points of the fallback - * content of this insertion point. If there are nodes matched - * to this insertion point, then destination insertion points - * of fallback are cleared, otherwise, this insertion point - * is a destination insertion point. - */ - void UpdateFallbackDistribution(); - - /** - * An array of nodes from the ShadowRoot host that match the - * content insertion selector. - */ - nsCOMArray<nsIContent> mMatchedNodes; - - nsAutoPtr<nsCSSSelectorList> mSelectorList; - bool mValidSelector; - bool mIsInsertionPoint; -}; - -class DistributedContentList : public nsINodeList -{ -public: - explicit DistributedContentList(HTMLContentElement* aHostElement); - - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DistributedContentList) - - // 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 JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; -protected: - virtual ~DistributedContentList(); - RefPtr<HTMLContentElement> mParent; - nsCOMArray<nsIContent> mDistributedNodes; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_HTMLContentElement_h__ - diff --git a/dom/html/HTMLDetailsElement.cpp b/dom/html/HTMLDetailsElement.cpp index 8619b14508..9d4dd89c2a 100644 --- a/dom/html/HTMLDetailsElement.cpp +++ b/dom/html/HTMLDetailsElement.cpp @@ -45,7 +45,7 @@ HTMLDetailsElement::GetAttributeChangeHint(const nsIAtom* aAttribute, nsresult HTMLDetailsElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsAttrValueOrString* aValue, bool aNotify) + const nsAttrValueOrString* aValue, bool aNotify) { if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::open) { bool setOpen = aValue != nullptr; diff --git a/dom/html/HTMLDetailsElement.h b/dom/html/HTMLDetailsElement.h index 6adf567bf8..4575ed888d 100644 --- a/dom/html/HTMLDetailsElement.h +++ b/dom/html/HTMLDetailsElement.h @@ -38,7 +38,8 @@ public: int32_t aModType) const override; nsresult BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsAttrValueOrString* aValue, bool aNotify) override; + const nsAttrValueOrString* aValue, + bool aNotify) override; // HTMLDetailsElement WebIDL bool Open() const { return GetBoolAttr(nsGkAtoms::open); } diff --git a/dom/html/HTMLFieldSetElement.cpp b/dom/html/HTMLFieldSetElement.cpp index d72fd1061a..f546cad5b4 100644 --- a/dom/html/HTMLFieldSetElement.cpp +++ b/dom/html/HTMLFieldSetElement.cpp @@ -70,7 +70,7 @@ HTMLFieldSetElement::IsDisabledForEvents(EventMessage aMessage) // nsIContent nsresult -HTMLFieldSetElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +HTMLFieldSetElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { // Do not process any DOM events if the element is disabled. aVisitor.mCanHandle = false; @@ -78,12 +78,13 @@ HTMLFieldSetElement::PreHandleEvent(EventChainPreVisitor& aVisitor) return NS_OK; } - return nsGenericHTMLFormElement::PreHandleEvent(aVisitor); + return nsGenericHTMLFormElement::GetEventTargetParent(aVisitor); } nsresult HTMLFieldSetElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::disabled && nsINode::GetFirstChild()) { @@ -100,7 +101,7 @@ HTMLFieldSetElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, } return nsGenericHTMLFormElement::AfterSetAttr(aNameSpaceID, aName, - aValue, aNotify); + aValue, aOldValue, aNotify); } // nsIDOMHTMLFieldSetElement diff --git a/dom/html/HTMLFieldSetElement.h b/dom/html/HTMLFieldSetElement.h index 96fff4582b..2c0e9cc149 100644 --- a/dom/html/HTMLFieldSetElement.h +++ b/dom/html/HTMLFieldSetElement.h @@ -40,9 +40,12 @@ public: NS_DECL_NSIDOMHTMLFIELDSETELEMENT // nsIContent - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) override; + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; virtual nsresult InsertChildAt(nsIContent* aChild, uint32_t aIndex, bool aNotify) override; diff --git a/dom/html/HTMLFormElement.cpp b/dom/html/HTMLFormElement.cpp index 6bea19a2bf..58cba6863a 100644 --- a/dom/html/HTMLFormElement.cpp +++ b/dom/html/HTMLFormElement.cpp @@ -189,32 +189,37 @@ HTMLFormElement::GetElements(nsIDOMHTMLCollection** aElements) } nsresult -HTMLFormElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) -{ - if ((aName == nsGkAtoms::action || aName == nsGkAtoms::target) && - aNameSpaceID == kNameSpaceID_None) { - if (mPendingSubmission) { - // aha, there is a pending submission that means we're in - // the script and we need to flush it. let's tell it - // that the event was ignored to force the flush. - // the second argument is not playing a role at all. - FlushPendingSubmission(); +HTMLFormElement::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString* aValue, bool aNotify) +{ + if (aNamespaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::action || aName == nsGkAtoms::target) { + // This check is mostly to preserve previous behavior. + if (aValue) { + if (mPendingSubmission) { + // aha, there is a pending submission that means we're in + // the script and we need to flush it. let's tell it + // that the event was ignored to force the flush. + // the second argument is not playing a role at all. + FlushPendingSubmission(); + } + // Don't forget we've notified the password manager already if the + // page sets the action/target in the during submit. (bug 343182) + bool notifiedObservers = mNotifiedObservers; + ForgetCurrentSubmission(); + mNotifiedObservers = notifiedObservers; + } } - // Don't forget we've notified the password manager already if the - // page sets the action/target in the during submit. (bug 343182) - bool notifiedObservers = mNotifiedObservers; - ForgetCurrentSubmission(); - mNotifiedObservers = notifiedObservers; } - return nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue, - aNotify); + + return nsGenericHTMLElement::BeforeSetAttr(aNamespaceID, aName, aValue, + aNotify); } nsresult HTMLFormElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { if (aName == nsGkAtoms::novalidate && aNameSpaceID == kNameSpaceID_None) { // Update all form elements states because they might be [no longer] @@ -230,7 +235,8 @@ HTMLFormElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, } } - return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue, aNotify); + return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue, + aOldValue, aNotify); } NS_IMPL_STRING_ATTR(HTMLFormElement, AcceptCharset, acceptcharset) @@ -489,7 +495,7 @@ HTMLFormElement::UnbindFromTree(bool aDeep, bool aNullParent) } nsresult -HTMLFormElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +HTMLFormElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.mWantsWillHandleEvent = true; if (aVisitor.mEvent->mOriginalTarget == static_cast<nsIContent*>(this)) { @@ -513,7 +519,7 @@ HTMLFormElement::PreHandleEvent(EventChainPreVisitor& aVisitor) mGeneratingReset = true; } } - return nsGenericHTMLElement::PreHandleEvent(aVisitor); + return nsGenericHTMLElement::GetEventTargetParent(aVisitor); } nsresult diff --git a/dom/html/HTMLFormElement.h b/dom/html/HTMLFormElement.h index b3e836f5f2..f06db4b51e 100644 --- a/dom/html/HTMLFormElement.h +++ b/dom/html/HTMLFormElement.h @@ -93,7 +93,8 @@ public: nsIAtom* aAttribute, const nsAString& aValue, nsAttrValue& aResult) override; - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; virtual nsresult WillHandleEvent( EventChainPostVisitor& aVisitor) override; virtual nsresult PostHandleEvent( @@ -104,16 +105,13 @@ public: bool aCompileEventHandlers) override; virtual void UnbindFromTree(bool aDeep = true, bool aNullParent = true) override; - nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAString& aValue, bool aNotify) - { - return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify); - } - virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) override; + virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString* aValue, + bool aNotify) override; virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) override; + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; /** * Forget all information about the current submission (and the fact that we diff --git a/dom/html/HTMLFrameSetElement.cpp b/dom/html/HTMLFrameSetElement.cpp index 6d39caa19a..861dbfe9fc 100644 --- a/dom/html/HTMLFrameSetElement.cpp +++ b/dom/html/HTMLFrameSetElement.cpp @@ -9,6 +9,7 @@ #include "mozilla/dom/EventHandlerBinding.h" #include "nsGlobalWindow.h" #include "mozilla/UniquePtrExtensions.h" +#include "nsAttrValueOrString.h" NS_IMPL_NS_NEW_HTML_ELEMENT(FrameSet) @@ -65,43 +66,45 @@ HTMLFrameSetElement::GetRows(nsAString& aRows) } nsresult -HTMLFrameSetElement::SetAttr(int32_t aNameSpaceID, - nsIAtom* aAttribute, - nsIAtom* aPrefix, - const nsAString& aValue, - bool aNotify) +HTMLFrameSetElement::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString* aValue, + bool aNotify) { - nsresult rv; /* The main goal here is to see whether the _number_ of rows or - * columns has changed. If it has, we need to reframe; otherwise - * we want to reflow. So we set mCurrentRowColHint here, then call - * nsGenericHTMLElement::SetAttr, which will end up calling - * GetAttributeChangeHint and notifying layout with that hint. - * Once nsGenericHTMLElement::SetAttr returns, we want to go back to our - * normal hint, which is NS_STYLE_HINT_REFLOW. + * columns has changed. If it has, we need to reframe; otherwise + * we want to reflow. + * Ideally, the style hint would be changed back to reflow after the reframe + * has been performed. Unfortunately, however, the reframe will be performed + * by the call to nsNodeUtils::AttributeChanged, which occurs *after* + * AfterSetAttr is called, leaving us with no convenient way of changing the + * value back to reflow afterwards. However, nsNodeUtils::AttributeChanged is + * effectively the only consumer of this value, so as long as we always set + * the value correctly here, we should be fine. */ - if (aAttribute == nsGkAtoms::rows && aNameSpaceID == kNameSpaceID_None) { - int32_t oldRows = mNumRows; - ParseRowCol(aValue, mNumRows, &mRowSpecs); + mCurrentRowColHint = NS_STYLE_HINT_REFLOW; + if (aNamespaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::rows) { + if (aValue) { + int32_t oldRows = mNumRows; + ParseRowCol(aValue->String(), mNumRows, &mRowSpecs); - if (mNumRows != oldRows) { - mCurrentRowColHint = nsChangeHint_ReconstructFrame; - } - } else if (aAttribute == nsGkAtoms::cols && - aNameSpaceID == kNameSpaceID_None) { - int32_t oldCols = mNumCols; - ParseRowCol(aValue, mNumCols, &mColSpecs); + if (mNumRows != oldRows) { + mCurrentRowColHint = nsChangeHint_ReconstructFrame; + } + } + } else if (aName == nsGkAtoms::cols) { + if (aValue) { + int32_t oldCols = mNumCols; + ParseRowCol(aValue->String(), mNumCols, &mColSpecs); - if (mNumCols != oldCols) { - mCurrentRowColHint = nsChangeHint_ReconstructFrame; + if (mNumCols != oldCols) { + mCurrentRowColHint = nsChangeHint_ReconstructFrame; + } + } } } - rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aAttribute, aPrefix, - aValue, aNotify); - mCurrentRowColHint = NS_STYLE_HINT_REFLOW; - - return rv; + return nsGenericHTMLElement::BeforeSetAttr(aNamespaceID, aName, aValue, aNotify); } nsresult diff --git a/dom/html/HTMLFrameSetElement.h b/dom/html/HTMLFrameSetElement.h index b6bbe5d957..fe3bc2341c 100644 --- a/dom/html/HTMLFrameSetElement.h +++ b/dom/html/HTMLFrameSetElement.h @@ -100,17 +100,6 @@ public: #undef WINDOW_EVENT_HELPER #undef EVENT - // These override the SetAttr methods in nsGenericHTMLElement (need - // both here to silence compiler warnings). - nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAString& aValue, bool aNotify) - { - return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify); - } - virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) override; - /** * GetRowSpec is used to get the "rows" spec. * @param out int32_t aNumValues The number of row sizes specified. @@ -143,6 +132,10 @@ protected: virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override; + virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString* aValue, + bool aNotify) override; + private: nsresult ParseRowCol(const nsAString& aValue, int32_t& aNumSpecs, diff --git a/dom/html/HTMLIFrameElement.cpp b/dom/html/HTMLIFrameElement.cpp index 0d5a209c04..2b66cbd268 100644 --- a/dom/html/HTMLIFrameElement.cpp +++ b/dom/html/HTMLIFrameElement.cpp @@ -182,54 +182,49 @@ HTMLIFrameElement::GetAttributeMappingFunction() const } nsresult -HTMLIFrameElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) -{ - nsresult rv = nsGenericHTMLFrameElement::SetAttr(aNameSpaceID, aName, - aPrefix, aValue, aNotify); - NS_ENSURE_SUCCESS(rv, rv); - - if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::srcdoc) { - // Don't propagate error here. The attribute was successfully set, that's - // what we should reflect. - LoadSrc(); - } - - return NS_OK; -} - -nsresult HTMLIFrameElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, const nsAttrValue* aValue, - bool aNotify) + const nsAttrValue* aOldValue, bool aNotify) { - if (aName == nsGkAtoms::sandbox && - aNameSpaceID == kNameSpaceID_None && mFrameLoader) { - // If we have an nsFrameLoader, apply the new sandbox flags. - // Since this is called after the setter, the sandbox flags have - // alreay been updated. - mFrameLoader->ApplySandboxFlags(GetSandboxFlags()); + AfterMaybeChangeAttr(aNameSpaceID, aName, aNotify); + + if (aNameSpaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::sandbox) { + if (mFrameLoader) { + // If we have an nsFrameLoader, apply the new sandbox flags. + // Since this is called after the setter, the sandbox flags have + // alreay been updated. + mFrameLoader->ApplySandboxFlags(GetSandboxFlags()); + } + } } return nsGenericHTMLFrameElement::AfterSetAttr(aNameSpaceID, aName, aValue, - aNotify); + aOldValue, aNotify); } nsresult -HTMLIFrameElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, - bool aNotify) +HTMLIFrameElement::OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString& aValue, + bool aNotify) { - // Invoke on the superclass. - nsresult rv = nsGenericHTMLFrameElement::UnsetAttr(aNameSpaceID, aAttribute, aNotify); - NS_ENSURE_SUCCESS(rv, rv); - - if (aNameSpaceID == kNameSpaceID_None && - aAttribute == nsGkAtoms::srcdoc) { - // Fall back to the src attribute, if any - LoadSrc(); - } + AfterMaybeChangeAttr(aNamespaceID, aName, aNotify); + + return nsGenericHTMLFrameElement::OnAttrSetButNotChanged(aNamespaceID, aName, + aValue, aNotify); +} - return NS_OK; +void +HTMLIFrameElement::AfterMaybeChangeAttr(int32_t aNamespaceID, + nsIAtom* aName, + bool aNotify) +{ + if (aNamespaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::srcdoc) { + // Don't propagate errors from LoadSrc. The attribute was successfully + // set/unset, that's what we should reflect. + LoadSrc(); + } + } } uint32_t diff --git a/dom/html/HTMLIFrameElement.h b/dom/html/HTMLIFrameElement.h index 5bfda24700..e94ae0bad6 100644 --- a/dom/html/HTMLIFrameElement.h +++ b/dom/html/HTMLIFrameElement.h @@ -46,20 +46,6 @@ public: virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; - nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAString& aValue, bool aNotify) - { - return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify); - } - virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) override; - virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, - bool aNotify) override; - virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, - bool aNotify) override; - uint32_t GetSandboxFlags(); // Web IDL binding methods @@ -196,11 +182,30 @@ protected: virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; + virtual nsresult OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString& aValue, + bool aNotify) override; + private: static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes, nsRuleData* aData); static const DOMTokenListSupportedToken sSupportedSandboxTokens[]; + + /** + * This function is called by AfterSetAttr and OnAttrSetButNotChanged. + * This function will be called by AfterSetAttr whether the attribute is being + * set or unset. + * + * @param aNamespaceID the namespace of the attr being set + * @param aName the localname of the attribute being set + * @param aNotify Whether we plan to notify document observers. + */ + void AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName, bool aNotify); }; } // namespace dom diff --git a/dom/html/HTMLImageElement.cpp b/dom/html/HTMLImageElement.cpp index fab1cdef4b..444c352e2c 100644 --- a/dom/html/HTMLImageElement.cpp +++ b/dom/html/HTMLImageElement.cpp @@ -112,6 +112,7 @@ private: HTMLImageElement::HTMLImageElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) : nsGenericHTMLElement(aNodeInfo) , mForm(nullptr) + , mForceReload(false) , mInDocResponsiveContent(false) , mCurrentDensity(1.0) { @@ -369,9 +370,12 @@ HTMLImageElement::GetAttributeMappingFunction() const nsresult HTMLImageElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsAttrValueOrString* aValue, + const nsAttrValueOrString* aValue, bool aNotify) { + if (aValue) { + BeforeMaybeChangeAttr(aNameSpaceID, aName, *aValue, aNotify); + } if (aNameSpaceID == kNameSpaceID_None && mForm && (aName == nsGkAtoms::name || aName == nsGkAtoms::id)) { @@ -391,8 +395,13 @@ HTMLImageElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, nsresult HTMLImageElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { + if (aValue) { + AfterMaybeChangeAttr(aNameSpaceID, aName, aNotify); + } + if (aNameSpaceID == kNameSpaceID_None && mForm && (aName == nsGkAtoms::name || aName == nsGkAtoms::id) && aValue && !aValue->IsEmptyString()) { @@ -433,67 +442,26 @@ HTMLImageElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, } return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, - aValue, aNotify); + aValue, aOldValue, aNotify); } nsresult -HTMLImageElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +HTMLImageElement::OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString& aValue, + bool aNotify) { - // We handle image element with attribute ismap in its corresponding frame - // element. Set mMultipleActionsPrevented here to prevent the click event - // trigger the behaviors in Element::PostHandleEventForLinks - WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent(); - if (mouseEvent && mouseEvent->IsLeftClickEvent() && IsMap()) { - mouseEvent->mFlags.mMultipleActionsPrevented = true; - } - return nsGenericHTMLElement::PreHandleEvent(aVisitor); -} - -bool -HTMLImageElement::IsHTMLFocusable(bool aWithMouse, - bool *aIsFocusable, int32_t *aTabIndex) -{ - int32_t tabIndex = TabIndex(); - - if (IsInUncomposedDoc()) { - nsAutoString usemap; - GetUseMap(usemap); - // XXXbz which document should this be using? sXBL/XBL2 issue! I - // think that OwnerDoc() is right, since we don't want to - // assume stuff about the document we're bound to. - if (OwnerDoc()->FindImageMap(usemap)) { - if (aTabIndex) { - // Use tab index on individual map areas - *aTabIndex = (sTabFocusModel & eTabFocus_linksMask)? 0 : -1; - } - // Image map is not focusable itself, but flag as tabbable - // so that image map areas get walked into. - *aIsFocusable = false; - - return false; - } - } - - if (aTabIndex) { - // Can be in tab order if tabindex >=0 and form controls are tabbable. - *aTabIndex = (sTabFocusModel & eTabFocus_formElementsMask)? tabIndex : -1; - } + BeforeMaybeChangeAttr(aNamespaceID, aName, aValue, aNotify); + AfterMaybeChangeAttr(aNamespaceID, aName, aNotify); - *aIsFocusable = -#ifdef XP_MACOSX - (!aWithMouse || nsFocusManager::sMouseFocusesFormControl) && -#endif - (tabIndex >= 0 || HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)); - - return false; + return nsGenericHTMLElement::OnAttrSetButNotChanged(aNamespaceID, aName, + aValue, aNotify); } -nsresult -HTMLImageElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) +void +HTMLImageElement::BeforeMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString& aValue, + bool aNotify) { - bool forceReload = false; // We need to force our image to reload. This must be done here, not in // AfterSetAttr or BeforeSetAttr, because we want to do it even if the attr is // being set to its existing value, which is normally optimized away as a @@ -504,16 +472,19 @@ HTMLImageElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, // spec. // // Both cases handle unsetting src in AfterSetAttr - if (aNameSpaceID == kNameSpaceID_None && + // + // Much of this should probably happen in AfterMaybeChangeAttr. + // See Bug 1370705 + if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::src) { if (InResponsiveMode()) { if (mResponsiveSelector && mResponsiveSelector->Content() == this) { - mResponsiveSelector->SetDefaultSource(aValue); + mResponsiveSelector->SetDefaultSource(aValue.String()); } QueueImageLoadTask(true); - } else if (aNotify) { + } else if (aNotify && OwnerDoc()->IsCurrentActiveDocument()) { // If aNotify is false, we are coming from the parser or some such place; // we'll get bound after all the attributes have been set, so we'll do the // sync image load from BindToTree. Skip the LoadImage call in that case. @@ -528,23 +499,23 @@ HTMLImageElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, // the state gets in Element's attr-setting happen around this // LoadImage call, we could start passing false instead of aNotify // here. - LoadImage(aValue, true, aNotify, eImageLoadType_Normal); + LoadImage(aValue.String(), true, aNotify, eImageLoadType_Normal); mNewRequestsWillNeedAnimationReset = false; } - } else if (aNameSpaceID == kNameSpaceID_None && + } else if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::crossorigin && aNotify) { nsAttrValue attrValue; - ParseCORSValue(aValue, attrValue); + ParseCORSValue(aValue.String(), attrValue); if (GetCORSMode() != AttrValueToCORSMode(&attrValue)) { // Force a new load of the image with the new cross origin policy. - forceReload = true; + mForceReload = true; } } else if (aName == nsGkAtoms::referrerpolicy && - aNameSpaceID == kNameSpaceID_None && + aNamespaceID == kNameSpaceID_None && aNotify) { - ReferrerPolicy referrerPolicy = AttributeReferrerPolicyFromString(aValue); + ReferrerPolicy referrerPolicy = AttributeReferrerPolicyFromString(aValue.String()); if (!InResponsiveMode() && referrerPolicy != RP_Unset && referrerPolicy != GetImageReferrerPolicy()) { @@ -553,22 +524,28 @@ HTMLImageElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, // the attribute will neither trigger a reload nor update the referrer // policy of the loading channel (whether it has previously completed or // not). Force a new load of the image with the new referrerpolicy. - forceReload = true; + mForceReload = true; } } - nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, - aValue, aNotify); + return; +} +void +HTMLImageElement::AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName, + bool aNotify) +{ // Because we load image synchronously in non-responsive-mode, we need to do // reload after the attribute has been set if the reload is triggerred by // cross origin changing. - if (forceReload) { + if (mForceReload) { + mForceReload = false; + if (InResponsiveMode()) { // per spec, full selection runs when this changes, even though // it doesn't directly affect the source selection QueueImageLoadTask(true); - } else { + } else if (OwnerDoc()->IsCurrentActiveDocument()) { // Bug 1076583 - We still use the older synchronous algorithm in // non-responsive mode. Force a new load of the image with the // new cross origin policy @@ -576,7 +553,59 @@ HTMLImageElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, } } - return rv; + return; +} + +nsresult +HTMLImageElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) +{ + // We handle image element with attribute ismap in its corresponding frame + // element. Set mMultipleActionsPrevented here to prevent the click event + // trigger the behaviors in Element::PostHandleEventForLinks + WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent(); + if (mouseEvent && mouseEvent->IsLeftClickEvent() && IsMap()) { + mouseEvent->mFlags.mMultipleActionsPrevented = true; + } + return nsGenericHTMLElement::GetEventTargetParent(aVisitor); +} + +bool +HTMLImageElement::IsHTMLFocusable(bool aWithMouse, + bool *aIsFocusable, int32_t *aTabIndex) +{ + int32_t tabIndex = TabIndex(); + + if (IsInUncomposedDoc()) { + nsAutoString usemap; + GetUseMap(usemap); + // XXXbz which document should this be using? sXBL/XBL2 issue! I + // think that OwnerDoc() is right, since we don't want to + // assume stuff about the document we're bound to. + if (OwnerDoc()->FindImageMap(usemap)) { + if (aTabIndex) { + // Use tab index on individual map areas + *aTabIndex = (sTabFocusModel & eTabFocus_linksMask)? 0 : -1; + } + // Image map is not focusable itself, but flag as tabbable + // so that image map areas get walked into. + *aIsFocusable = false; + + return false; + } + } + + if (aTabIndex) { + // Can be in tab order if tabindex >=0 and form controls are tabbable. + *aTabIndex = (sTabFocusModel & eTabFocus_formElementsMask)? tabIndex : -1; + } + + *aIsFocusable = +#ifdef XP_MACOSX + (!aWithMouse || nsFocusManager::sMouseFocusesFormControl) && +#endif + (tabIndex >= 0 || HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)); + + return false; } nsresult diff --git a/dom/html/HTMLImageElement.h b/dom/html/HTMLImageElement.h index 62323e8016..bb4a098824 100644 --- a/dom/html/HTMLImageElement.h +++ b/dom/html/HTMLImageElement.h @@ -70,21 +70,11 @@ public: NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() const override; - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; bool IsHTMLFocusable(bool aWithMouse, bool *aIsFocusable, int32_t *aTabIndex) override; - // SetAttr override. C++ is stupid, so have to override both - // overloaded methods. - nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAString& aValue, bool aNotify) - { - return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify); - } - virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) override; - virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsIContent* aBindingParent, bool aCompileEventHandlers) override; @@ -343,11 +333,17 @@ protected: void UpdateFormOwner(); virtual nsresult BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsAttrValueOrString* aValue, + const nsAttrValueOrString* aValue, bool aNotify) override; virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) override; + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; + + virtual nsresult OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString& aValue, + bool aNotify) override; // This is a weak reference that this element and the HTMLFormElement // cooperate in maintaining. @@ -362,6 +358,36 @@ private: static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes, nsRuleData* aData); + /** + * This function is called by BeforeSetAttr and OnAttrSetButNotChanged. + * It will not be called if the value is being unset. + * + * @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. + */ + void BeforeMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString& aValue, + bool aNotify); + /** + * This function is called by AfterSetAttr and OnAttrSetButNotChanged. + * It will not be called if the value is being unset. + * + * @param aNamespaceID the namespace of the attr being set + * @param aName the localname of the attribute being set + * @param aNotify Whether we plan to notify document observers. + */ + void AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName, + bool aNotify); + /** + * Used by BeforeMaybeChangeAttr and AfterMaybeChangeAttr to keep track of + * whether a reload needs to be forced after an attribute change that is + * currently in progress. + */ + bool mForceReload; + bool mInDocResponsiveContent; RefPtr<ImageLoadTask> mPendingImageLoadTask; diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index 0b879bb9b4..7708a60acf 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -147,6 +147,8 @@ namespace dom { #define NS_CONTROL_TYPE(bits) ((bits) & ~( \ NS_OUTER_ACTIVATE_EVENT | NS_ORIGINAL_CHECKED_VALUE | NS_NO_CONTENT_DISPATCH | \ NS_ORIGINAL_INDETERMINATE_VALUE)) +#define NS_PRE_HANDLE_BLUR_EVENT (1 << 13) +#define NS_PRE_HANDLE_INPUT_EVENT (1 << 14) // whether textfields should be selected once focused: // -1: no, 1: yes, 0: uninitialized @@ -172,15 +174,18 @@ static const nsAttrValue::EnumTable kInputTypeTable[] = { { "search", NS_FORM_INPUT_SEARCH }, { "submit", NS_FORM_INPUT_SUBMIT }, { "tel", NS_FORM_INPUT_TEL }, - { "text", NS_FORM_INPUT_TEXT }, { "time", NS_FORM_INPUT_TIME }, { "url", NS_FORM_INPUT_URL }, { "week", NS_FORM_INPUT_WEEK }, + // "text" must be last for ParseAttribute to work right. If you add things + // before it, please update kInputDefaultType. + { "text", NS_FORM_INPUT_TEXT }, { nullptr, 0 } }; // Default type is 'text'. -static const nsAttrValue::EnumTable* kInputDefaultType = &kInputTypeTable[18]; +static const nsAttrValue::EnumTable* kInputDefaultType = + &kInputTypeTable[ArrayLength(kInputTypeTable) - 2]; static const uint8_t NS_INPUT_INPUTMODE_AUTO = 0; static const uint8_t NS_INPUT_INPUTMODE_NUMERIC = 1; @@ -1235,7 +1240,7 @@ HTMLInputElement::Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) co nsresult HTMLInputElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsAttrValueOrString* aValue, + const nsAttrValueOrString* aValue, bool aNotify) { if (aNameSpaceID == kNameSpaceID_None) { @@ -1259,10 +1264,6 @@ HTMLInputElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, } } else if (aNotify && aName == nsGkAtoms::disabled) { mDisabledChanged = true; - } else if (aName == nsGkAtoms::dir && - AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir, - nsGkAtoms::_auto, eIgnoreCase)) { - SetDirectionIfAuto(false, aNotify); } else if (mType == NS_FORM_INPUT_RADIO && aName == nsGkAtoms::required) { nsCOMPtr<nsIRadioGroupContainer> container = GetRadioGroupContainer(); @@ -1282,7 +1283,8 @@ HTMLInputElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, nsresult HTMLInputElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { if (aNameSpaceID == kNameSpaceID_None) { // @@ -1322,36 +1324,15 @@ HTMLInputElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, } if (aName == nsGkAtoms::type) { + uint8_t newType; if (!aValue) { - // We're now a text input. Note that we have to handle this manually, - // since removing an attribute (which is what happened, since aValue is - // null) doesn't call ParseAttribute. - HandleTypeChange(kInputDefaultType->value); - } - - UpdateBarredFromConstraintValidation(); - - if (mType != NS_FORM_INPUT_IMAGE) { - // We're no longer an image input. Cancel our image requests, if we have - // any. Note that doing this when we already weren't an image is ok -- - // just does nothing. - CancelImageRequests(aNotify); - } else if (aNotify) { - // We just got switched to be an image input; we should see - // whether we have an image to load; - nsAutoString src; - if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) { - LoadImage(src, false, aNotify, eImageLoadType_Normal); - } + // We're now a text input. + newType = kInputDefaultType->value; + } else { + newType = aValue->GetEnumValue(); } - - if (mType == NS_FORM_INPUT_PASSWORD && IsInComposedDoc()) { - AsyncEventDispatcher* dispatcher = - new AsyncEventDispatcher(this, - NS_LITERAL_STRING("DOMInputPasswordAdded"), - true, - true); - dispatcher->PostDOMEvent(); + if (newType != mType) { + HandleTypeChange(newType, aNotify); } } @@ -1434,7 +1415,7 @@ HTMLInputElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, "HTML5 spec does not allow underflow for type=range"); } else if (aName == nsGkAtoms::dir && aValue && aValue->Equals(nsGkAtoms::_auto, eIgnoreCase)) { - SetDirectionIfAuto(true, aNotify); + SetDirectionFromValue(aNotify); } else if (aName == nsGkAtoms::lang) { if (mType == NS_FORM_INPUT_NUMBER) { // Update the value that is displayed to the user to the new locale: @@ -1450,12 +1431,11 @@ HTMLInputElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, // Clear the cached @autocomplete attribute state. mAutocompleteAttrState = nsContentUtils::eAutocompleteAttrState_Unknown; } - - UpdateState(aNotify); } return nsGenericHTMLFormElementWithState::AfterSetAttr(aNameSpaceID, aName, - aValue, aNotify); + aValue, aOldValue, + aNotify); } // nsIDOMHTMLInputElement @@ -3738,7 +3718,7 @@ HTMLInputElement::IsDisabledForEvents(EventMessage aMessage) } nsresult -HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +HTMLInputElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { // Do not process any DOM events if the element is disabled aVisitor.mCanHandle = false; @@ -3755,7 +3735,7 @@ HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor) //FIXME Allow submission etc. also when there is no prescontext, Bug 329509. if (!aVisitor.mPresContext) { - return nsGenericHTMLElement::PreHandleEvent(aVisitor); + return nsGenericHTMLFormElementWithState::GetEventTargetParent(aVisitor); } // // Web pages expect the value of a radio button or checkbox to be set @@ -3865,23 +3845,16 @@ HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor) // Fire onchange (if necessary), before we do the blur, bug 357684. if (aVisitor.mEvent->mMessage == eBlur) { - // Experimental mobile types rely on the system UI to prevent users to not - // set invalid values but we have to be extra-careful. Especially if the - // option has been enabled on desktop. - if (IsExperimentalMobileType(mType)) { - nsAutoString aValue; - GetValueInternal(aValue); - nsresult rv = - SetValueInternal(aValue, nsTextEditorState::eSetValue_Internal); - NS_ENSURE_SUCCESS(rv, rv); - } - FireChangeEventIfNeeded(); + // We set NS_PRE_HANDLE_BLUR_EVENT here and handle it in PreHandleEvent to + // prevent breaking event target chain creation. + aVisitor.mWantsPreHandleEvent = true; + aVisitor.mItemFlags |= NS_PRE_HANDLE_BLUR_EVENT; } if (mType == NS_FORM_INPUT_RANGE && (aVisitor.mEvent->mMessage == eFocus || aVisitor.mEvent->mMessage == eBlur)) { - // Just as nsGenericHTMLFormElementWithState::PreHandleEvent calls + // Just as nsGenericHTMLFormElementWithState::GetEventTargetParent calls // nsIFormControlFrame::SetFocus, we handle focus here. nsIFrame* frame = GetPrimaryFrame(); if (frame) { @@ -3969,10 +3942,11 @@ HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor) } } - nsresult rv = nsGenericHTMLFormElementWithState::PreHandleEvent(aVisitor); + nsresult rv = nsGenericHTMLFormElementWithState::GetEventTargetParent(aVisitor); - // We do this after calling the base class' PreHandleEvent so that - // nsIContent::PreHandleEvent doesn't reset any change we make to mCanHandle. + // We do this after calling the base class' GetEventTargetParent so that + // nsIContent::GetEventTargetParent doesn't reset any change we make to + // mCanHandle. if (mType == NS_FORM_INPUT_NUMBER && aVisitor.mEvent->IsTrusted() && aVisitor.mEvent->mOriginalTarget != this) { @@ -3987,18 +3961,10 @@ HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor) } if (textControl && aVisitor.mEvent->mOriginalTarget == textControl) { if (aVisitor.mEvent->mMessage == eEditorInput) { - // Propogate the anon text control's new value to our HTMLInputElement: - nsAutoString value; - numberControlFrame->GetValueOfAnonTextControl(value); - numberControlFrame->HandlingInputEvent(true); - nsWeakFrame weakNumberControlFrame(numberControlFrame); - rv = SetValueInternal(value, - nsTextEditorState::eSetValue_BySetUserInput | - nsTextEditorState::eSetValue_Notify); - NS_ENSURE_SUCCESS(rv, rv); - if (weakNumberControlFrame.IsAlive()) { - numberControlFrame->HandlingInputEvent(false); - } + aVisitor.mWantsPreHandleEvent = true; + // We set NS_PRE_HANDLE_INPUT_EVENT here and handle it in PreHandleEvent + // to prevent breaking event target chain creation. + aVisitor.mItemFlags |= NS_PRE_HANDLE_INPUT_EVENT; } else if (aVisitor.mEvent->mMessage == eFormChange) { // We cancel the DOM 'change' event that is fired for any change to our @@ -4037,6 +4003,50 @@ HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor) return rv; } +nsresult +HTMLInputElement::PreHandleEvent(EventChainVisitor& aVisitor) +{ + if (!aVisitor.mPresContext) { + return nsGenericHTMLFormElementWithState::PreHandleEvent(aVisitor); + } + nsresult rv; + if (aVisitor.mItemFlags & NS_PRE_HANDLE_BLUR_EVENT) { + MOZ_ASSERT(aVisitor.mEvent->mMessage == eBlur); + // Experimental mobile types rely on the system UI to prevent users to not + // set invalid values but we have to be extra-careful. Especially if the + // option has been enabled on desktop. + if (IsExperimentalMobileType(mType)) { + nsAutoString aValue; + GetValueInternal(aValue); + nsresult rv = + SetValueInternal(aValue, nsTextEditorState::eSetValue_Internal); + NS_ENSURE_SUCCESS(rv, rv); + } + FireChangeEventIfNeeded(); + } + rv = nsGenericHTMLFormElementWithState::PreHandleEvent(aVisitor); + if (aVisitor.mItemFlags & NS_PRE_HANDLE_INPUT_EVENT) { + nsNumberControlFrame* numberControlFrame = do_QueryFrame(GetPrimaryFrame()); + MOZ_ASSERT(aVisitor.mEvent->mMessage == eEditorInput); + MOZ_ASSERT(numberControlFrame); + MOZ_ASSERT(numberControlFrame->GetAnonTextControl() == + aVisitor.mEvent->mOriginalTarget); + // Propogate the anon text control's new value to our HTMLInputElement: + nsAutoString value; + numberControlFrame->GetValueOfAnonTextControl(value); + numberControlFrame->HandlingInputEvent(true); + nsWeakFrame weakNumberControlFrame(numberControlFrame); + rv = SetValueInternal(value, + nsTextEditorState::eSetValue_BySetUserInput | + nsTextEditorState::eSetValue_Notify); + NS_ENSURE_SUCCESS(rv, rv); + if (weakNumberControlFrame.IsAlive()) { + numberControlFrame->HandlingInputEvent(false); + } + } + return rv; +} + void HTMLInputElement::StartRangeThumbDrag(WidgetGUIEvent* aEvent) { @@ -4961,7 +4971,9 @@ HTMLInputElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, } // Set direction based on value if dir=auto - SetDirectionIfAuto(HasDirAuto(), false); + if (HasDirAuto()) { + SetDirectionFromValue(false); + } // An element can't suffer from value missing if it is not in a document. // We have to check if we suffer from that as we are now in a document. @@ -5015,14 +5027,23 @@ HTMLInputElement::UnbindFromTree(bool aDeep, bool aNullParent) } void -HTMLInputElement::HandleTypeChange(uint8_t aNewType) +HTMLInputElement::HandleTypeChange(uint8_t aNewType, bool aNotify) { - if (mType == NS_FORM_INPUT_RANGE && mIsDraggingRange) { + uint8_t oldType = mType; + MOZ_ASSERT(oldType != aNewType); + + if (aNewType == NS_FORM_INPUT_FILE || oldType == NS_FORM_INPUT_FILE) { + // Strictly speaking, we only need to clear files on going _to_ or _from_ + // the NS_FORM_INPUT_FILE type, not both, since we'll never confuse values + // and filenames. But this is safer. + ClearFiles(false); + } + + if (oldType == NS_FORM_INPUT_RANGE && mIsDraggingRange) { CancelRangeThumbDrag(false); } ValueModeType aOldValueMode = GetValueMode(); - uint8_t oldType = mType; nsAutoString aOldValue; if (aOldValueMode == VALUE_MODE_VALUE) { @@ -5103,6 +5124,30 @@ HTMLInputElement::HandleTypeChange(uint8_t aNewType) UpdateAllValidityStates(false); UpdateApzAwareFlag(); + + UpdateBarredFromConstraintValidation(); + + if (oldType == NS_FORM_INPUT_IMAGE) { + // We're no longer an image input. Cancel our image requests, if we have + // any. + CancelImageRequests(aNotify); + } else if (aNotify && mType == NS_FORM_INPUT_IMAGE) { + // We just got switched to be an image input; we should see + // whether we have an image to load; + nsAutoString src; + if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) { + LoadImage(src, false, aNotify, eImageLoadType_Normal); + } + } + + if (mType == NS_FORM_INPUT_PASSWORD && IsInComposedDoc()) { + AsyncEventDispatcher* dispatcher = + new AsyncEventDispatcher(this, + NS_LITERAL_STRING("DOMInputPasswordAdded"), + true, + true); + dispatcher->PostDOMEvent(); + } } void @@ -5813,42 +5858,34 @@ HTMLInputElement::ParseAttribute(int32_t aNamespaceID, const nsAString& aValue, nsAttrValue& aResult) { + // We can't make these static_asserts because kInputDefaultType and + // kInputTypeTable aren't constexpr. + MOZ_ASSERT(kInputDefaultType->value == NS_FORM_INPUT_TEXT, + "Someone forgot to update kInputDefaultType when adding a new " + "input type."); + MOZ_ASSERT(kInputTypeTable[ArrayLength(kInputTypeTable) - 1].tag == nullptr, + "Last entry in the table must be the nullptr guard"); + MOZ_ASSERT(kInputTypeTable[ArrayLength(kInputTypeTable) - 2].value == + NS_FORM_INPUT_TEXT, + "Next to last entry in the table must be the \"text\" entry"); + if (aNamespaceID == kNameSpaceID_None) { if (aAttribute == nsGkAtoms::type) { - // XXX ARG!! This is major evilness. ParseAttribute - // shouldn't set members. Override SetAttr instead - int32_t newType; - bool success = aResult.ParseEnumValue(aValue, kInputTypeTable, false); - if (success) { - newType = aResult.GetEnumValue(); - if ((newType == NS_FORM_INPUT_NUMBER && !IsInputNumberEnabled()) || - (newType == NS_FORM_INPUT_COLOR && !IsInputColorEnabled()) || - (IsDateTimeInputType(newType) && !IsDateTimeTypeSupported(newType))) { - newType = kInputDefaultType->value; - aResult.SetTo(newType, &aValue); - } - } else { - newType = kInputDefaultType->value; + aResult.ParseEnumValue(aValue, kInputTypeTable, false, kInputDefaultType); + int32_t newType = aResult.GetEnumValue(); + if ((IsExperimentalMobileType(newType) && + !IsExperimentalFormsEnabled()) || + (newType == NS_FORM_INPUT_NUMBER && !IsInputNumberEnabled()) || + (newType == NS_FORM_INPUT_COLOR && !IsInputColorEnabled()) || + (IsDateTimeInputType(newType) && + !IsDateTimeTypeSupported(newType))) { + // There's no public way to set an nsAttrValue to an enum value, but we + // can just re-parse with a table that doesn't have any types other than + // "text" in it. + aResult.ParseEnumValue(aValue, kInputDefaultType, false, kInputDefaultType); } - if (newType != mType) { - // Make sure to do the check for newType being NS_FORM_INPUT_FILE and - // the corresponding SetValueInternal() call _before_ we set mType. - // That way the logic in SetValueInternal() will work right (that logic - // makes assumptions about our frame based on mType, but we won't have - // had time to recreate frames yet -- that happens later in the - // SetAttr() process). - if (newType == NS_FORM_INPUT_FILE || mType == NS_FORM_INPUT_FILE) { - // This call isn't strictly needed any more since we'll never - // confuse values and filenames. However it's there for backwards - // compat. - ClearFiles(false); - } - - HandleTypeChange(newType); - } - - return success; + return true; } if (aAttribute == nsGkAtoms::width) { return aResult.ParseSpecialIntValue(aValue); @@ -6654,17 +6691,12 @@ HTMLInputElement::SetDefaultValueAsValue() } void -HTMLInputElement::SetDirectionIfAuto(bool aAuto, bool aNotify) +HTMLInputElement::SetDirectionFromValue(bool aNotify) { - if (aAuto) { - SetHasDirAuto(); - if (IsSingleLineTextControl(true)) { - nsAutoString value; - GetValue(value); - SetDirectionalityFromValue(this, value, aNotify); - } - } else { - ClearHasDirAuto(); + if (IsSingleLineTextControl(true)) { + nsAutoString value; + GetValue(value); + SetDirectionalityFromValue(this, value, aNotify); } } @@ -8441,7 +8473,7 @@ HTMLInputElement::OnValueChanged(bool aNotify, bool aWasInteractiveUserChange) UpdateAllValidityStates(aNotify); if (HasDirAuto()) { - SetDirectionIfAuto(true, aNotify); + SetDirectionFromValue(aNotify); } // :placeholder-shown pseudo-class may change when the value changes. diff --git a/dom/html/HTMLInputElement.h b/dom/html/HTMLInputElement.h index 98a5904437..55bb59ec98 100644 --- a/dom/html/HTMLInputElement.h +++ b/dom/html/HTMLInputElement.h @@ -185,7 +185,9 @@ public: NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() const override; - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; + virtual nsresult PreHandleEvent(EventChainVisitor& aVisitor) override; virtual nsresult PostHandleEvent( EventChainPostVisitor& aVisitor) override; void PostHandleEventForRangeThumb(EventChainPostVisitor& aVisitor); @@ -956,13 +958,15 @@ protected: * Called when an attribute is about to be changed */ virtual nsresult BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsAttrValueOrString* aValue, + const nsAttrValueOrString* aValue, bool aNotify) override; /** * Called when an attribute has just been changed */ virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) override; + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; /** * Dispatch a select event. Returns true if the event was not cancelled. @@ -1098,7 +1102,7 @@ protected: /** * Manages the internal data storage across type changes. */ - void HandleTypeChange(uint8_t aNewType); + void HandleTypeChange(uint8_t aNewType, bool aNotify); /** * Sanitize the value of the element depending of its current type. @@ -1118,7 +1122,7 @@ protected: */ nsresult SetDefaultValueAsValue(); - virtual void SetDirectionIfAuto(bool aAuto, bool aNotify); + void SetDirectionFromValue(bool aNotify); /** * Return if an element should have a specific validity UI diff --git a/dom/html/HTMLLegendElement.cpp b/dom/html/HTMLLegendElement.cpp index 1a593d7698..3f329de42d 100644 --- a/dom/html/HTMLLegendElement.cpp +++ b/dom/html/HTMLLegendElement.cpp @@ -71,21 +71,6 @@ HTMLLegendElement::GetAttributeChangeHint(const nsIAtom* aAttribute, } nsresult -HTMLLegendElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) -{ - return nsGenericHTMLElement::SetAttr(aNameSpaceID, aAttribute, - aPrefix, aValue, aNotify); -} -nsresult -HTMLLegendElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, - bool aNotify) -{ - return nsGenericHTMLElement::UnsetAttr(aNameSpaceID, aAttribute, aNotify); -} - -nsresult HTMLLegendElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsIContent* aBindingParent, bool aCompileEventHandlers) diff --git a/dom/html/HTMLLegendElement.h b/dom/html/HTMLLegendElement.h index e2d71d62dd..0b476c1194 100644 --- a/dom/html/HTMLLegendElement.h +++ b/dom/html/HTMLLegendElement.h @@ -42,16 +42,6 @@ public: nsAttrValue& aResult) override; virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute, int32_t aModType) const override; - nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAString& aValue, bool aNotify) - { - return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify); - } - virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) override; - virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, - bool aNotify) override; virtual nsresult Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) const override; diff --git a/dom/html/HTMLLinkElement.cpp b/dom/html/HTMLLinkElement.cpp index 8afe767bdb..b05b92d7aa 100644 --- a/dom/html/HTMLLinkElement.cpp +++ b/dom/html/HTMLLinkElement.cpp @@ -333,7 +333,7 @@ HTMLLinkElement::UpdateImport() nsresult HTMLLinkElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsAttrValueOrString* aValue, bool aNotify) + const nsAttrValueOrString* aValue, bool aNotify) { if (aNameSpaceID == kNameSpaceID_None && (aName == nsGkAtoms::href || aName == nsGkAtoms::rel)) { @@ -348,7 +348,8 @@ HTMLLinkElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, nsresult HTMLLinkElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { // It's safe to call ResetLinkState here because our new attr value has // already been set or unset. ResetLinkState needs the updated attribute @@ -417,13 +418,13 @@ HTMLLinkElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, } return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue, - aNotify); + aOldValue, aNotify); } nsresult -HTMLLinkElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +HTMLLinkElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { - return PreHandleEventForAnchors(aVisitor); + return GetEventTargetParentForAnchors(aVisitor); } nsresult diff --git a/dom/html/HTMLLinkElement.h b/dom/html/HTMLLinkElement.h index 421b149e96..acac955cb2 100644 --- a/dom/html/HTMLLinkElement.h +++ b/dom/html/HTMLLinkElement.h @@ -46,7 +46,8 @@ public: void UpdateImport(); // nsIDOMEventTarget - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; virtual nsresult PostHandleEvent( EventChainPostVisitor& aVisitor) override; @@ -61,10 +62,11 @@ public: virtual void UnbindFromTree(bool aDeep = true, bool aNullParent = true) override; virtual nsresult BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsAttrValueOrString* aValue, + const nsAttrValueOrString* aValue, bool aNotify) override; virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) override; virtual bool IsLink(nsIURI** aURI) const override; virtual already_AddRefed<nsIURI> GetHrefURI() const override; diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index bc63eab512..cbb86edace 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -3726,77 +3726,74 @@ int32_t HTMLMediaElement::TabIndexDefault() return 0; } -nsresult HTMLMediaElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) -{ - nsresult rv = - nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue, - aNotify); - if (NS_FAILED(rv)) - return rv; - if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::src) { - DoLoad(); - } - if (aNotify && aNameSpaceID == kNameSpaceID_None) { - if (aName == nsGkAtoms::autoplay) { - StopSuspendingAfterFirstFrame(); - CheckAutoplayDataReady(); - // This attribute can affect AddRemoveSelfReference - AddRemoveSelfReference(); - UpdatePreloadAction(); +nsresult +HTMLMediaElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) +{ + if (aNameSpaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::src) { + mSrcMediaSource = nullptr; + if (aValue) { + nsString srcStr = aValue->GetStringValue(); + nsCOMPtr<nsIURI> uri; + NewURIFromString(srcStr, getter_AddRefs(uri)); + if (uri && IsMediaSourceURI(uri)) { + nsresult rv = + NS_GetSourceForMediaSourceURI(uri, getter_AddRefs(mSrcMediaSource)); + if (NS_FAILED(rv)) { + nsAutoString spec; + GetCurrentSrc(spec); + const char16_t* params[] = { spec.get() }; + ReportLoadError("MediaLoadInvalidURI", params, ArrayLength(params)); + } + } + } + } else if (aName == nsGkAtoms::autoplay) { + if (aNotify) { + if (aValue) { + StopSuspendingAfterFirstFrame(); + CheckAutoplayDataReady(); + } + // This attribute can affect AddRemoveSelfReference + AddRemoveSelfReference(); + UpdatePreloadAction(); + } } else if (aName == nsGkAtoms::preload) { UpdatePreloadAction(); } } - return rv; + // Since AfterMaybeChangeAttr may call DoLoad, make sure that it is called + // *after* any possible changes to mSrcMediaSource. + if (aValue) { + AfterMaybeChangeAttr(aNameSpaceID, aName, aNotify); + } + + return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, + aValue, aOldValue, aNotify); } -nsresult HTMLMediaElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttr, - bool aNotify) +nsresult +HTMLMediaElement::OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString& aValue, + bool aNotify) { - nsresult rv = nsGenericHTMLElement::UnsetAttr(aNameSpaceID, aAttr, aNotify); - if (NS_FAILED(rv)) - return rv; - if (aNotify && aNameSpaceID == kNameSpaceID_None) { - if (aAttr == nsGkAtoms::autoplay) { - // This attribute can affect AddRemoveSelfReference - AddRemoveSelfReference(); - UpdatePreloadAction(); - } else if (aAttr == nsGkAtoms::preload) { - UpdatePreloadAction(); - } - } + AfterMaybeChangeAttr(aNamespaceID, aName, aNotify); - return rv; + return nsGenericHTMLElement::OnAttrSetButNotChanged(aNamespaceID, aName, + aValue, aNotify); } -nsresult -HTMLMediaElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) -{ - if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::src) { - mSrcMediaSource = nullptr; - if (aValue) { - nsString srcStr = aValue->GetStringValue(); - nsCOMPtr<nsIURI> uri; - NewURIFromString(srcStr, getter_AddRefs(uri)); - if (uri && IsMediaSourceURI(uri)) { - nsresult rv = - NS_GetSourceForMediaSourceURI(uri, getter_AddRefs(mSrcMediaSource)); - if (NS_FAILED(rv)) { - nsAutoString spec; - GetCurrentSrc(spec); - const char16_t* params[] = { spec.get() }; - ReportLoadError("MediaLoadInvalidURI", params, ArrayLength(params)); - } - } +void +HTMLMediaElement::AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName, + bool aNotify) +{ + if (aNamespaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::src) { + DoLoad(); } } - - return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, - aValue, aNotify); } nsresult HTMLMediaElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, @@ -5634,7 +5631,7 @@ nsIContent* HTMLMediaElement::GetNextSource() "Should only iterate over direct children"); #endif - int32_t startOffset = 0; + uint32_t startOffset = 0; rv = mSourcePointer->GetStartOffset(&startOffset); NS_ENSURE_SUCCESS(rv, nullptr); diff --git a/dom/html/HTMLMediaElement.h b/dom/html/HTMLMediaElement.h index 44c666f951..0d8d5d4e07 100644 --- a/dom/html/HTMLMediaElement.h +++ b/dom/html/HTMLMediaElement.h @@ -132,21 +132,6 @@ public: nsIAtom* aAttribute, const nsAString& aValue, nsAttrValue& aResult) override; - // SetAttr override. C++ is stupid, so have to override both - // overloaded methods. - nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAString& aValue, bool aNotify) - { - return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify); - } - virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) override; - virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttr, - bool aNotify) override; - virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, - bool aNotify) override; virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsIContent* aBindingParent, @@ -1314,6 +1299,14 @@ protected: already_AddRefed<Promise> CreateDOMPromise(ErrorResult& aRv) const; + virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; + virtual nsresult OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString& aValue, + bool aNotify) override; + // The current decoder. Load() has been called on this decoder. // At most one of mDecoder and mSrcStream can be non-null. RefPtr<MediaDecoder> mDecoder; @@ -1716,6 +1709,17 @@ private: // We keep track of these because the load algorithm resolves/rejects all // already-dispatched pending play promises. nsTArray<nsResolveOrRejectPendingPlayPromisesRunner*> mPendingPlayPromisesRunners; + +private: + /** + * This function is called by AfterSetAttr and OnAttrSetButNotChanged. + * It will not be called if the value is being unset. + * + * @param aNamespaceID the namespace of the attr being set + * @param aName the localname of the attribute being set + * @param aNotify Whether we plan to notify document observers. + */ + void AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName, bool aNotify); }; } // namespace dom diff --git a/dom/html/HTMLMenuItemElement.cpp b/dom/html/HTMLMenuItemElement.cpp index ca8bb4feb5..3245a16b1d 100644 --- a/dom/html/HTMLMenuItemElement.cpp +++ b/dom/html/HTMLMenuItemElement.cpp @@ -252,7 +252,7 @@ HTMLMenuItemElement::SetChecked(bool aChecked) } nsresult -HTMLMenuItemElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +HTMLMenuItemElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { if (aVisitor.mEvent->mMessage == eMouseClick) { @@ -283,7 +283,7 @@ HTMLMenuItemElement::PreHandleEvent(EventChainPreVisitor& aVisitor) aVisitor.mItemFlags |= mType; } - return nsGenericHTMLElement::PreHandleEvent(aVisitor); + return nsGenericHTMLElement::GetEventTargetParent(aVisitor); } nsresult @@ -380,7 +380,8 @@ HTMLMenuItemElement::GetText(nsAString& aText) nsresult HTMLMenuItemElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { if (aNameSpaceID == kNameSpaceID_None) { if ((aName == nsGkAtoms::radiogroup || aName == nsGkAtoms::type) && @@ -404,7 +405,7 @@ HTMLMenuItemElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, } return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue, - aNotify); + aOldValue, aNotify); } void diff --git a/dom/html/HTMLMenuItemElement.h b/dom/html/HTMLMenuItemElement.h index 3c19b1170e..cb824edcc5 100644 --- a/dom/html/HTMLMenuItemElement.h +++ b/dom/html/HTMLMenuItemElement.h @@ -36,7 +36,8 @@ public: // nsIDOMHTMLMenuItemElement NS_DECL_NSIDOMHTMLMENUITEMELEMENT - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; virtual nsresult PostHandleEvent( EventChainPostVisitor& aVisitor) override; @@ -124,7 +125,9 @@ protected: protected: virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) override; + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; void WalkRadioGroup(Visitor* aVisitor); diff --git a/dom/html/HTMLMetaElement.cpp b/dom/html/HTMLMetaElement.cpp index 47effa2bc5..833c3177a7 100644 --- a/dom/html/HTMLMetaElement.cpp +++ b/dom/html/HTMLMetaElement.cpp @@ -60,7 +60,8 @@ HTMLMetaElement::SetMetaReferrer(nsIDocument* aDocument) nsresult HTMLMetaElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { if (aNameSpaceID == kNameSpaceID_None) { nsIDocument *document = GetUncomposedDoc(); @@ -82,7 +83,7 @@ HTMLMetaElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, } return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue, - aNotify); + aOldValue, aNotify); } nsresult diff --git a/dom/html/HTMLMetaElement.h b/dom/html/HTMLMetaElement.h index d3f05826d4..0b9117e557 100644 --- a/dom/html/HTMLMetaElement.h +++ b/dom/html/HTMLMetaElement.h @@ -33,7 +33,9 @@ public: bool aNullParent = true) override; virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) override; + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; void CreateAndDispatchEvent(nsIDocument* aDoc, const nsAString& aEventName); diff --git a/dom/html/HTMLObjectElement.cpp b/dom/html/HTMLObjectElement.cpp index 65f4078892..cd2f3f37a7 100644 --- a/dom/html/HTMLObjectElement.cpp +++ b/dom/html/HTMLObjectElement.cpp @@ -298,41 +298,45 @@ HTMLObjectElement::UnbindFromTree(bool aDeep, nsresult -HTMLObjectElement::SetAttr(int32_t aNameSpaceID, nsIAtom *aName, - nsIAtom *aPrefix, const nsAString &aValue, - bool aNotify) +HTMLObjectElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { - nsresult rv = nsGenericHTMLFormElement::SetAttr(aNameSpaceID, aName, aPrefix, - aValue, aNotify); + nsresult rv = AfterMaybeChangeAttr(aNamespaceID, aName, aNotify); NS_ENSURE_SUCCESS(rv, rv); - // if aNotify is false, we are coming from the parser or some such place; - // we'll get bound after all the attributes have been set, so we'll do the - // object load from BindToTree/DoneAddingChildren. - // Skip the LoadObject call in that case. - // We also don't want to start loading the object when we're not yet in - // a document, just in case that the caller wants to set additional - // attributes before inserting the node into the document. - if (aNotify && IsInComposedDoc() && mIsDoneAddingChildren && - aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::data) { - return LoadObject(aNotify, true); - } - - return NS_OK; + return nsGenericHTMLFormElement::AfterSetAttr(aNamespaceID, aName, aValue, + aOldValue, aNotify); } nsresult -HTMLObjectElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, - bool aNotify) +HTMLObjectElement::OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString& aValue, + bool aNotify) { - nsresult rv = nsGenericHTMLFormElement::UnsetAttr(aNameSpaceID, - aAttribute, aNotify); + nsresult rv = AfterMaybeChangeAttr(aNamespaceID, aName, aNotify); NS_ENSURE_SUCCESS(rv, rv); - // See comment in SetAttr - if (aNotify && IsInComposedDoc() && mIsDoneAddingChildren && - aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::data) { - return LoadObject(aNotify, true); + return nsGenericHTMLFormElement::OnAttrSetButNotChanged(aNamespaceID, aName, + aValue, aNotify); +} + +nsresult +HTMLObjectElement::AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName, + bool aNotify) +{ + if (aNamespaceID == kNameSpaceID_None) { + // if aNotify is false, we are coming from the parser or some such place; + // we'll get bound after all the attributes have been set, so we'll do the + // object load from BindToTree/DoneAddingChildren. + // Skip the LoadObject call in that case. + // We also don't want to start loading the object when we're not yet in + // a document, just in case that the caller wants to set additional + // attributes before inserting the node into the document. + if (aNotify && IsInComposedDoc() && mIsDoneAddingChildren && + aName == nsGkAtoms::data) { + return LoadObject(aNotify, true); + } } return NS_OK; diff --git a/dom/html/HTMLObjectElement.h b/dom/html/HTMLObjectElement.h index 5226154da9..6ce09e1cc4 100644 --- a/dom/html/HTMLObjectElement.h +++ b/dom/html/HTMLObjectElement.h @@ -59,11 +59,6 @@ public: bool aCompileEventHandlers) override; virtual void UnbindFromTree(bool aDeep = true, bool aNullParent = true) override; - virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom *aName, - nsIAtom *aPrefix, const nsAString &aValue, - bool aNotify) override; - virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, - bool aNotify) override; virtual bool IsHTMLFocusable(bool aWithMouse, bool *aIsFocusable, int32_t *aTabIndex) override; virtual IMEState GetDesiredIMEState() override; @@ -245,6 +240,15 @@ public: return GetContentDocument(aSubjectPrincipal); } + protected: + virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; + virtual nsresult OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString& aValue, + bool aNotify) override; + private: /** * Calls LoadObject with the correct arguments to start the plugin load. @@ -269,6 +273,18 @@ private: static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes, nsRuleData* aData); + /** + * This function is called by AfterSetAttr and OnAttrSetButNotChanged. + * This function will be called by AfterSetAttr whether the attribute is being + * set or unset. + * + * @param aNamespaceID the namespace of the attr being set + * @param aName the localname of the attribute being set + * @param aNotify Whether we plan to notify document observers. + */ + nsresult AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName, + bool aNotify); + bool mIsDoneAddingChildren; }; diff --git a/dom/html/HTMLOptGroupElement.cpp b/dom/html/HTMLOptGroupElement.cpp index 9e738961d8..63af4e39f3 100644 --- a/dom/html/HTMLOptGroupElement.cpp +++ b/dom/html/HTMLOptGroupElement.cpp @@ -48,7 +48,7 @@ NS_IMPL_STRING_ATTR(HTMLOptGroupElement, Label, label) nsresult -HTMLOptGroupElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +HTMLOptGroupElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.mCanHandle = false; // Do not process any DOM events if the element is disabled @@ -65,7 +65,7 @@ HTMLOptGroupElement::PreHandleEvent(EventChainPreVisitor& aVisitor) } } - return nsGenericHTMLElement::PreHandleEvent(aVisitor); + return nsGenericHTMLElement::GetEventTargetParent(aVisitor); } Element* @@ -101,7 +101,8 @@ HTMLOptGroupElement::RemoveChildAt(uint32_t aIndex, bool aNotify) nsresult HTMLOptGroupElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::disabled) { // All our children <option> have their :disabled state depending on our @@ -116,7 +117,7 @@ HTMLOptGroupElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, } return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue, - aNotify); + aOldValue, aNotify); } EventStates diff --git a/dom/html/HTMLOptGroupElement.h b/dom/html/HTMLOptGroupElement.h index e46a6a953f..6853e51ed7 100644 --- a/dom/html/HTMLOptGroupElement.h +++ b/dom/html/HTMLOptGroupElement.h @@ -35,14 +35,17 @@ public: virtual void RemoveChildAt(uint32_t aIndex, bool aNotify) override; // nsIContent - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; virtual EventStates IntrinsicState() const override; virtual nsresult Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) const override; virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) override; + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; virtual nsIDOMNode* AsDOMNode() override { return this; } diff --git a/dom/html/HTMLOptionElement.cpp b/dom/html/HTMLOptionElement.cpp index 7b1e3ca2d7..410c5713ea 100644 --- a/dom/html/HTMLOptionElement.cpp +++ b/dom/html/HTMLOptionElement.cpp @@ -181,7 +181,7 @@ HTMLOptionElement::GetAttributeChangeHint(const nsIAtom* aAttribute, nsresult HTMLOptionElement::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, - nsAttrValueOrString* aValue, + const nsAttrValueOrString* aValue, bool aNotify) { nsresult rv = nsGenericHTMLElement::BeforeSetAttr(aNamespaceID, aName, @@ -244,7 +244,8 @@ HTMLOptionElement::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, nsresult HTMLOptionElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::value && Selected()) { @@ -258,7 +259,7 @@ HTMLOptionElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, } return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, - aValue, aNotify); + aValue, aOldValue, aNotify); } NS_IMETHODIMP diff --git a/dom/html/HTMLOptionElement.h b/dom/html/HTMLOptionElement.h index 4b5e192ff5..965cdeb8f2 100644 --- a/dom/html/HTMLOptionElement.h +++ b/dom/html/HTMLOptionElement.h @@ -47,10 +47,12 @@ public: int32_t aModType) const override; virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, - nsAttrValueOrString* aValue, + const nsAttrValueOrString* aValue, bool aNotify) override; virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) override; + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; void SetSelectedInternal(bool aValue, bool aNotify); diff --git a/dom/html/HTMLOptionsCollection.cpp b/dom/html/HTMLOptionsCollection.cpp index 67de97fc4c..da8a9d5718 100644 --- a/dom/html/HTMLOptionsCollection.cpp +++ b/dom/html/HTMLOptionsCollection.cpp @@ -239,6 +239,12 @@ HTMLOptionsCollection::GetParentObject() return mSelect; } +DocGroup* +HTMLOptionsCollection::GetDocGroup() const +{ + return mSelect ? mSelect->GetDocGroup() : nullptr; +} + NS_IMETHODIMP HTMLOptionsCollection::NamedItem(const nsAString& aName, nsIDOMNode** aReturn) diff --git a/dom/html/HTMLOptionsCollection.h b/dom/html/HTMLOptionsCollection.h index 496919555e..9dd1561f6e 100644 --- a/dom/html/HTMLOptionsCollection.h +++ b/dom/html/HTMLOptionsCollection.h @@ -23,6 +23,7 @@ class nsIDOMHTMLOptionElement; namespace mozilla { namespace dom { +class DocGroup; class HTMLElementOrLong; class HTMLOptionElementOrHTMLOptGroupElement; class HTMLSelectElement; @@ -62,6 +63,7 @@ public: virtual Element* GetElementAt(uint32_t aIndex) override; virtual nsINode* GetParentObject() override; + DocGroup* GetDocGroup() const; NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(HTMLOptionsCollection, nsIHTMLCollection) diff --git a/dom/html/HTMLScriptElement.cpp b/dom/html/HTMLScriptElement.cpp index 095b9b77de..a1bea94d97 100644 --- a/dom/html/HTMLScriptElement.cpp +++ b/dom/html/HTMLScriptElement.cpp @@ -232,13 +232,14 @@ HTMLScriptElement::SetNoModule(bool aValue, ErrorResult& aRv) nsresult HTMLScriptElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { if (nsGkAtoms::async == aName && kNameSpaceID_None == aNamespaceID) { mForceAsync = false; } return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName, aValue, - aNotify); + aOldValue, aNotify); } NS_IMETHODIMP diff --git a/dom/html/HTMLScriptElement.h b/dom/html/HTMLScriptElement.h index 19ceb414f9..bd446fa97d 100644 --- a/dom/html/HTMLScriptElement.h +++ b/dom/html/HTMLScriptElement.h @@ -57,7 +57,9 @@ public: // Element virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) override; + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; // WebIDL void SetText(const nsAString& aValue, ErrorResult& rv); diff --git a/dom/html/HTMLSelectElement.cpp b/dom/html/HTMLSelectElement.cpp index 9ba0a1efe9..36dac0852b 100644 --- a/dom/html/HTMLSelectElement.cpp +++ b/dom/html/HTMLSelectElement.cpp @@ -1269,12 +1269,25 @@ HTMLSelectElement::UnbindFromTree(bool aDeep, bool aNullParent) nsresult HTMLSelectElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsAttrValueOrString* aValue, + const nsAttrValueOrString* aValue, bool aNotify) { - if (aNotify && aName == nsGkAtoms::disabled && - aNameSpaceID == kNameSpaceID_None) { - mDisabledChanged = true; + if (aNameSpaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::disabled) { + if (aNotify) { + mDisabledChanged = true; + } + } else if (aName == nsGkAtoms::multiple) { + if (!aValue && aNotify && mSelectedIndex >= 0) { + // We're changing from being a multi-select to a single-select. + // Make sure we only have one option selected before we do that. + // Note that this needs to come before we really unset the attr, + // since SetOptionsSelectedByIndex does some bail-out type + // optimization for cases when the select is not multiple that + // would lead to only a single option getting deselected. + SetSelectedIndexInternal(mSelectedIndex, aNotify); + } + } } return nsGenericHTMLFormElementWithState::BeforeSetAttr(aNameSpaceID, aName, @@ -1283,7 +1296,8 @@ HTMLSelectElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, nsresult HTMLSelectElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { if (aNameSpaceID == kNameSpaceID_None) { if (aName == nsGkAtoms::disabled) { @@ -1293,44 +1307,18 @@ HTMLSelectElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, } else if (aName == nsGkAtoms::autocomplete) { // Clear the cached @autocomplete attribute state mAutocompleteAttrState = nsContentUtils::eAutocompleteAttrState_Unknown; + } else if (aName == nsGkAtoms::multiple) { + if (!aValue && aNotify) { + // We might have become a combobox; make sure _something_ gets + // selected in that case + CheckSelectSomething(aNotify); + } } - - UpdateState(aNotify); } return nsGenericHTMLFormElementWithState::AfterSetAttr(aNameSpaceID, aName, - aValue, aNotify); -} - -nsresult -HTMLSelectElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, - bool aNotify) -{ - if (aNotify && aNameSpaceID == kNameSpaceID_None && - aAttribute == nsGkAtoms::multiple) { - // We're changing from being a multi-select to a single-select. - // Make sure we only have one option selected before we do that. - // Note that this needs to come before we really unset the attr, - // since SetOptionsSelectedByIndex does some bail-out type - // optimization for cases when the select is not multiple that - // would lead to only a single option getting deselected. - if (mSelectedIndex >= 0) { - SetSelectedIndexInternal(mSelectedIndex, aNotify); - } - } - - nsresult rv = nsGenericHTMLFormElementWithState::UnsetAttr(aNameSpaceID, aAttribute, - aNotify); - NS_ENSURE_SUCCESS(rv, rv); - - if (aNotify && aNameSpaceID == kNameSpaceID_None && - aAttribute == nsGkAtoms::multiple) { - // We might have become a combobox; make sure _something_ gets - // selected in that case - CheckSelectSomething(aNotify); - } - - return rv; + aValue, aOldValue, + aNotify); } void @@ -1442,14 +1430,14 @@ HTMLSelectElement::IsDisabledForEvents(EventMessage aMessage) } nsresult -HTMLSelectElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +HTMLSelectElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.mCanHandle = false; if (IsDisabledForEvents(aVisitor.mEvent->mMessage)) { return NS_OK; } - return nsGenericHTMLFormElementWithState::PreHandleEvent(aVisitor); + return nsGenericHTMLFormElementWithState::GetEventTargetParent(aVisitor); } nsresult diff --git a/dom/html/HTMLSelectElement.h b/dom/html/HTMLSelectElement.h index dc1075cd7d..298b1a8d16 100644 --- a/dom/html/HTMLSelectElement.h +++ b/dom/html/HTMLSelectElement.h @@ -278,7 +278,8 @@ public: virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; // nsIContent - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; virtual nsresult PostHandleEvent( EventChainPostVisitor& aVisitor) override; @@ -374,12 +375,12 @@ public: bool aCompileEventHandlers) override; virtual void UnbindFromTree(bool aDeep, bool aNullParent) override; virtual nsresult BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsAttrValueOrString* aValue, + const nsAttrValueOrString* aValue, bool aNotify) override; virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) override; - virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, - bool aNotify) override; + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; virtual void DoneAddingChildren(bool aHaveNotified) override; virtual bool IsDoneAddingChildren() override { diff --git a/dom/html/HTMLShadowElement.cpp b/dom/html/HTMLShadowElement.cpp deleted file mode 100644 index 8824c2875f..0000000000 --- a/dom/html/HTMLShadowElement.cpp +++ /dev/null @@ -1,373 +0,0 @@ -/* -*- 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 "mozilla/dom/ShadowRoot.h" - -#include "ChildIterator.h" -#include "nsContentUtils.h" -#include "nsDocument.h" -#include "mozilla/dom/HTMLShadowElement.h" -#include "mozilla/dom/HTMLUnknownElement.h" -#include "mozilla/dom/HTMLShadowElementBinding.h" - -// Expand NS_IMPL_NS_NEW_HTML_ELEMENT(Shadow) to add check for web components -// being enabled. -nsGenericHTMLElement* -NS_NewHTMLShadowElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, - mozilla::dom::FromParser aFromParser) -{ - // When this check is removed, remove the nsDocument.h and - // HTMLUnknownElement.h includes. Also remove nsINode::IsHTMLShadowElement. - // - // We have to jump through some hoops to be able to produce both NodeInfo* and - // already_AddRefed<NodeInfo>& for our callees. - RefPtr<mozilla::dom::NodeInfo> nodeInfo(aNodeInfo); - if (!nsDocument::IsWebComponentsEnabled(nodeInfo)) { - already_AddRefed<mozilla::dom::NodeInfo> nodeInfoArg(nodeInfo.forget()); - return new mozilla::dom::HTMLUnknownElement(nodeInfoArg); - } - - already_AddRefed<mozilla::dom::NodeInfo> nodeInfoArg(nodeInfo.forget()); - return new mozilla::dom::HTMLShadowElement(nodeInfoArg); -} - -using namespace mozilla::dom; - -HTMLShadowElement::HTMLShadowElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) - : nsGenericHTMLElement(aNodeInfo), mIsInsertionPoint(false) -{ -} - -HTMLShadowElement::~HTMLShadowElement() -{ - if (mProjectedShadow) { - mProjectedShadow->RemoveMutationObserver(this); - } -} - -NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLShadowElement) - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLShadowElement, - nsGenericHTMLElement) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProjectedShadow) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLShadowElement, - nsGenericHTMLElement) - if (tmp->mProjectedShadow) { - tmp->mProjectedShadow->RemoveMutationObserver(tmp); - tmp->mProjectedShadow = nullptr; - } -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_IMPL_ADDREF_INHERITED(HTMLShadowElement, Element) -NS_IMPL_RELEASE_INHERITED(HTMLShadowElement, Element) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLShadowElement) -NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement) - -NS_IMPL_ELEMENT_CLONE(HTMLShadowElement) - -JSObject* -HTMLShadowElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) -{ - return HTMLShadowElementBinding::Wrap(aCx, this, aGivenProto); -} - -void -HTMLShadowElement::SetProjectedShadow(ShadowRoot* aProjectedShadow) -{ - if (mProjectedShadow) { - mProjectedShadow->RemoveMutationObserver(this); - - // The currently projected ShadowRoot is going away, - // thus the destination insertion points need to be updated. - ExplicitChildIterator childIterator(mProjectedShadow); - for (nsIContent* content = childIterator.GetNextChild(); - content; - content = childIterator.GetNextChild()) { - ShadowRoot::RemoveDestInsertionPoint(this, content->DestInsertionPoints()); - } - } - - mProjectedShadow = aProjectedShadow; - if (mProjectedShadow) { - // A new ShadowRoot is being projected, thus its explcit - // children will be distributed to this shadow insertion point. - ExplicitChildIterator childIterator(mProjectedShadow); - for (nsIContent* content = childIterator.GetNextChild(); - content; - content = childIterator.GetNextChild()) { - content->DestInsertionPoints().AppendElement(this); - } - - // Watch for mutations on the projected shadow because - // it affects the nodes that are distributed to this shadow - // insertion point. - mProjectedShadow->AddMutationObserver(this); - } -} - -static bool -IsInFallbackContent(nsIContent* aContent) -{ - nsINode* parentNode = aContent->GetParentNode(); - while (parentNode) { - if (parentNode->IsHTMLElement(nsGkAtoms::content)) { - return true; - } - parentNode = parentNode->GetParentNode(); - } - - return false; -} - -nsresult -HTMLShadowElement::BindToTree(nsIDocument* aDocument, - nsIContent* aParent, - nsIContent* aBindingParent, - bool aCompileEventHandlers) -{ - RefPtr<ShadowRoot> oldContainingShadow = GetContainingShadow(); - - nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent, - aBindingParent, - aCompileEventHandlers); - NS_ENSURE_SUCCESS(rv, rv); - - ShadowRoot* containingShadow = GetContainingShadow(); - if (containingShadow && !oldContainingShadow) { - // Keep track of all descendant <shadow> elements in tree order so - // that when the current shadow insertion point is removed, the next - // one can be found quickly. - TreeOrderComparator comparator; - containingShadow->ShadowDescendants().InsertElementSorted(this, comparator); - - if (containingShadow->ShadowDescendants()[0] != this) { - // Only the first <shadow> (in tree order) of a ShadowRoot can be an insertion point. - return NS_OK; - } - - if (IsInFallbackContent(this)) { - // If the first shadow element in tree order is invalid (in fallback content), - // the containing ShadowRoot will not have a shadow insertion point. - containingShadow->SetShadowElement(nullptr); - } else { - mIsInsertionPoint = true; - containingShadow->SetShadowElement(this); - } - - containingShadow->SetInsertionPointChanged(); - } - - if (mIsInsertionPoint && containingShadow) { - // Propagate BindToTree calls to projected shadow root children. - ShadowRoot* projectedShadow = containingShadow->GetOlderShadowRoot(); - if (projectedShadow) { - projectedShadow->SetIsComposedDocParticipant(IsInComposedDoc()); - - for (nsIContent* child = projectedShadow->GetFirstChild(); child; - child = child->GetNextSibling()) { - rv = child->BindToTree(nullptr, projectedShadow, - projectedShadow->GetBindingParent(), - aCompileEventHandlers); - NS_ENSURE_SUCCESS(rv, rv); - } - } - } - - return NS_OK; -} - -void -HTMLShadowElement::UnbindFromTree(bool aDeep, bool aNullParent) -{ - RefPtr<ShadowRoot> oldContainingShadow = GetContainingShadow(); - - if (mIsInsertionPoint && oldContainingShadow) { - // Propagate UnbindFromTree call to previous projected shadow - // root children. - ShadowRoot* projectedShadow = oldContainingShadow->GetOlderShadowRoot(); - if (projectedShadow) { - for (nsIContent* child = projectedShadow->GetFirstChild(); child; - child = child->GetNextSibling()) { - child->UnbindFromTree(true, false); - } - - projectedShadow->SetIsComposedDocParticipant(false); - } - } - - nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); - - if (oldContainingShadow && !GetContainingShadow() && mIsInsertionPoint) { - nsTArray<HTMLShadowElement*>& shadowDescendants = - oldContainingShadow->ShadowDescendants(); - shadowDescendants.RemoveElement(this); - oldContainingShadow->SetShadowElement(nullptr); - - // Find the next shadow insertion point. - if (shadowDescendants.Length() > 0 && - !IsInFallbackContent(shadowDescendants[0])) { - oldContainingShadow->SetShadowElement(shadowDescendants[0]); - } - - oldContainingShadow->SetInsertionPointChanged(); - - mIsInsertionPoint = false; - } -} - -void -HTMLShadowElement::DistributeSingleNode(nsIContent* aContent) -{ - if (aContent->DestInsertionPoints().Contains(this)) { - // Node has already been distrbuted this this node, - // we are done. - return; - } - - aContent->DestInsertionPoints().AppendElement(this); - - // Handle the case where the shadow element is a child of - // a node with a ShadowRoot. The nodes that have been distributed to - // this shadow insertion point will need to be reprojected into the - // insertion points of the parent's ShadowRoot. - ShadowRoot* parentShadowRoot = GetParent()->GetShadowRoot(); - if (parentShadowRoot) { - parentShadowRoot->DistributeSingleNode(aContent); - return; - } - - // Handle the case where the parent of this shadow element is a ShadowRoot - // that is projected into a shadow insertion point in the younger ShadowRoot. - ShadowRoot* containingShadow = GetContainingShadow(); - ShadowRoot* youngerShadow = containingShadow->GetYoungerShadowRoot(); - if (youngerShadow && GetParent() == containingShadow) { - HTMLShadowElement* youngerShadowElement = youngerShadow->GetShadowElement(); - if (youngerShadowElement) { - youngerShadowElement->DistributeSingleNode(aContent); - } - } -} - -void -HTMLShadowElement::RemoveDistributedNode(nsIContent* aContent) -{ - ShadowRoot::RemoveDestInsertionPoint(this, aContent->DestInsertionPoints()); - - // Handle the case where the shadow element is a child of - // a node with a ShadowRoot. The nodes that have been distributed to - // this shadow insertion point will need to be removed from the - // insertion points of the parent's ShadowRoot. - ShadowRoot* parentShadowRoot = GetParent()->GetShadowRoot(); - if (parentShadowRoot) { - parentShadowRoot->RemoveDistributedNode(aContent); - return; - } - - // Handle the case where the parent of this shadow element is a ShadowRoot - // that is projected into a shadow insertion point in the younger ShadowRoot. - ShadowRoot* containingShadow = GetContainingShadow(); - ShadowRoot* youngerShadow = containingShadow->GetYoungerShadowRoot(); - if (youngerShadow && GetParent() == containingShadow) { - HTMLShadowElement* youngerShadowElement = youngerShadow->GetShadowElement(); - if (youngerShadowElement) { - youngerShadowElement->RemoveDistributedNode(aContent); - } - } -} - -void -HTMLShadowElement::DistributeAllNodes() -{ - // All the explicit children of the projected ShadowRoot are distributed - // into this shadow insertion point so update the destination insertion - // points. - ShadowRoot* containingShadow = GetContainingShadow(); - ShadowRoot* olderShadow = containingShadow->GetOlderShadowRoot(); - if (olderShadow) { - ExplicitChildIterator childIterator(olderShadow); - for (nsIContent* content = childIterator.GetNextChild(); - content; - content = childIterator.GetNextChild()) { - ShadowRoot::RemoveDestInsertionPoint(this, content->DestInsertionPoints()); - content->DestInsertionPoints().AppendElement(this); - } - } - - // Handle the case where the shadow element is a child of - // a node with a ShadowRoot. The nodes that have been distributed to - // this shadow insertion point will need to be reprojected into the - // insertion points of the parent's ShadowRoot. - ShadowRoot* parentShadowRoot = GetParent()->GetShadowRoot(); - if (parentShadowRoot) { - parentShadowRoot->DistributeAllNodes(); - return; - } - - // Handle the case where the parent of this shadow element is a ShadowRoot - // that is projected into a shadow insertion point in the younger ShadowRoot. - ShadowRoot* youngerShadow = containingShadow->GetYoungerShadowRoot(); - if (youngerShadow && GetParent() == containingShadow) { - HTMLShadowElement* youngerShadowElement = youngerShadow->GetShadowElement(); - if (youngerShadowElement) { - youngerShadowElement->DistributeAllNodes(); - } - } -} - -void -HTMLShadowElement::ContentAppended(nsIDocument* aDocument, - nsIContent* aContainer, - nsIContent* aFirstNewContent, - int32_t aNewIndexInContainer) -{ - // Watch for content appended to the projected shadow (the ShadowRoot that - // will be rendered in place of this shadow insertion point) because the - // nodes may need to be distributed into other insertion points. - nsIContent* currentChild = aFirstNewContent; - while (currentChild) { - if (ShadowRoot::IsPooledNode(currentChild, aContainer, mProjectedShadow)) { - DistributeSingleNode(currentChild); - } - currentChild = currentChild->GetNextSibling(); - } -} - -void -HTMLShadowElement::ContentInserted(nsIDocument* aDocument, - nsIContent* aContainer, - nsIContent* aChild, - int32_t aIndexInContainer) -{ - // Watch for content appended to the projected shadow (the ShadowRoot that - // will be rendered in place of this shadow insertion point) because the - // nodes may need to be distributed into other insertion points. - if (!ShadowRoot::IsPooledNode(aChild, aContainer, mProjectedShadow)) { - return; - } - - DistributeSingleNode(aChild); -} - -void -HTMLShadowElement::ContentRemoved(nsIDocument* aDocument, - nsIContent* aContainer, - nsIContent* aChild, - int32_t aIndexInContainer, - nsIContent* aPreviousSibling) -{ - // Watch for content removed from the projected shadow (the ShadowRoot that - // will be rendered in place of this shadow insertion point) because the - // nodes may need to be removed from other insertion points. - if (!ShadowRoot::IsPooledNode(aChild, aContainer, mProjectedShadow)) { - return; - } - - RemoveDistributedNode(aChild); -} - diff --git a/dom/html/HTMLShadowElement.h b/dom/html/HTMLShadowElement.h deleted file mode 100644 index 95083b802f..0000000000 --- a/dom/html/HTMLShadowElement.h +++ /dev/null @@ -1,96 +0,0 @@ -/* -*- 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_HTMLShadowElement_h__ -#define mozilla_dom_HTMLShadowElement_h__ - -#include "nsGenericHTMLElement.h" - -namespace mozilla { -namespace dom { - -class HTMLShadowElement final : public nsGenericHTMLElement, - public nsStubMutationObserver -{ -public: - explicit HTMLShadowElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); - - // nsISupports - NS_DECL_ISUPPORTS_INHERITED - - NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED - NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED - NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED - - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLShadowElement, - nsGenericHTMLElement) - - static HTMLShadowElement* FromContent(nsIContent* aContent) - { - if (aContent->IsHTMLShadowElement()) { - return static_cast<HTMLShadowElement*>(aContent); - } - - return nullptr; - } - - virtual bool IsHTMLShadowElement() const override { return true; } - - virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; - - virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, - nsIContent* aBindingParent, - bool aCompileEventHandlers) override; - - virtual void UnbindFromTree(bool aDeep = true, - bool aNullParent = true) override; - - bool IsInsertionPoint() { return mIsInsertionPoint; } - - /** - * Sets the ShadowRoot that will be rendered in place of - * this shadow insertion point. - */ - void SetProjectedShadow(ShadowRoot* aProjectedShadow); - - /** - * Distributes a single explicit child of the projected ShadowRoot - * to relevant insertion points. - */ - void DistributeSingleNode(nsIContent* aContent); - - /** - * Removes a single explicit child of the projected ShadowRoot - * from relevant insertion points. - */ - void RemoveDistributedNode(nsIContent* aContent); - - /** - * Distributes all the explicit children of the projected ShadowRoot - * to the shadow insertion point in the younger ShadowRoot and - * the content insertion point of the parent node's ShadowRoot. - */ - void DistributeAllNodes(); - - // WebIDL methods. - ShadowRoot* GetOlderShadowRoot() { return mProjectedShadow; } - -protected: - virtual ~HTMLShadowElement(); - - virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override; - - // The ShadowRoot that will be rendered in place of this shadow insertion point. - RefPtr<ShadowRoot> mProjectedShadow; - - bool mIsInsertionPoint; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_HTMLShadowElement_h__ - diff --git a/dom/html/HTMLSharedElement.cpp b/dom/html/HTMLSharedElement.cpp index bcb84d29b2..a7a10b0823 100644 --- a/dom/html/HTMLSharedElement.cpp +++ b/dom/html/HTMLSharedElement.cpp @@ -231,52 +231,32 @@ SetBaseTargetUsingFirstBaseWithTarget(nsIDocument* aDocument, } nsresult -HTMLSharedElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) +HTMLSharedElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { - nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, - aValue, aNotify); - NS_ENSURE_SUCCESS(rv, rv); - - // If the href attribute of a <base> tag is changing, we may need to update - // the document's base URI, which will cause all the links on the page to be - // re-resolved given the new base. If the target attribute is changing, we - // similarly need to change the base target. - if (mNodeInfo->Equals(nsGkAtoms::base) && - aNameSpaceID == kNameSpaceID_None && - IsInUncomposedDoc()) { - if (aName == nsGkAtoms::href) { - SetBaseURIUsingFirstBaseWithHref(GetUncomposedDoc(), this); - } else if (aName == nsGkAtoms::target) { - SetBaseTargetUsingFirstBaseWithTarget(GetUncomposedDoc(), this); - } - } - - return NS_OK; -} - -nsresult -HTMLSharedElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName, - bool aNotify) -{ - nsresult rv = nsGenericHTMLElement::UnsetAttr(aNameSpaceID, aName, aNotify); - NS_ENSURE_SUCCESS(rv, rv); - - // If we're the first <base> with an href and our href attribute is being - // unset, then we're no longer the first <base> with an href, and we need to - // find the new one. Similar for target. - if (mNodeInfo->Equals(nsGkAtoms::base) && - aNameSpaceID == kNameSpaceID_None && - IsInUncomposedDoc()) { + if (aNamespaceID == kNameSpaceID_None) { if (aName == nsGkAtoms::href) { - SetBaseURIUsingFirstBaseWithHref(GetUncomposedDoc(), nullptr); + // If the href attribute of a <base> tag is changing, we may need to + // update the document's base URI, which will cause all the links on the + // page to be re-resolved given the new base. + // If the href is being unset (aValue is null), we will need to find a new + // <base>. + if (mNodeInfo->Equals(nsGkAtoms::base) && IsInUncomposedDoc()) { + SetBaseURIUsingFirstBaseWithHref(GetUncomposedDoc(), + aValue ? this : nullptr); + } } else if (aName == nsGkAtoms::target) { - SetBaseTargetUsingFirstBaseWithTarget(GetUncomposedDoc(), nullptr); + // The target attribute is in pretty much the same situation as the href + // attribute, above. + if (mNodeInfo->Equals(nsGkAtoms::base) && IsInUncomposedDoc()) { + SetBaseTargetUsingFirstBaseWithTarget(GetUncomposedDoc(), + aValue ? this : nullptr); + } } } - - return NS_OK; + return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName, aValue, + aOldValue, aNotify); } nsresult diff --git a/dom/html/HTMLSharedElement.h b/dom/html/HTMLSharedElement.h index 205750cf6d..a12ee096df 100644 --- a/dom/html/HTMLSharedElement.h +++ b/dom/html/HTMLSharedElement.h @@ -59,17 +59,6 @@ public: nsIAtom* aAttribute, const nsAString& aValue, nsAttrValue& aResult) override; - nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAString& aValue, bool aNotify) - { - return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify); - } - virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) override; - - virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName, - bool aNotify) override; virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsIContent* aBindingParent, @@ -181,6 +170,11 @@ protected: virtual ~HTMLSharedElement(); virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + + virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; }; } // namespace dom diff --git a/dom/html/HTMLSharedObjectElement.cpp b/dom/html/HTMLSharedObjectElement.cpp index 889fd5aef3..4f35068e1c 100644 --- a/dom/html/HTMLSharedObjectElement.cpp +++ b/dom/html/HTMLSharedObjectElement.cpp @@ -161,27 +161,53 @@ HTMLSharedObjectElement::UnbindFromTree(bool aDeep, nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); } +nsresult +HTMLSharedObjectElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) +{ + if (aValue) { + nsresult rv = AfterMaybeChangeAttr(aNamespaceID, aName, aNotify); + NS_ENSURE_SUCCESS(rv, rv); + } + + return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName, aValue, + aOldValue, aNotify); +} nsresult -HTMLSharedObjectElement::SetAttr(int32_t aNameSpaceID, nsIAtom *aName, - nsIAtom *aPrefix, const nsAString &aValue, - bool aNotify) +HTMLSharedObjectElement::OnAttrSetButNotChanged(int32_t aNamespaceID, + nsIAtom* aName, + const nsAttrValueOrString& aValue, + bool aNotify) { - nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, - aValue, aNotify); + nsresult rv = AfterMaybeChangeAttr(aNamespaceID, aName, aNotify); NS_ENSURE_SUCCESS(rv, rv); - // if aNotify is false, we are coming from the parser or some such place; - // we'll get bound after all the attributes have been set, so we'll do the - // object load from BindToTree/DoneAddingChildren. - // Skip the LoadObject call in that case. - // We also don't want to start loading the object when we're not yet in - // a document, just in case that the caller wants to set additional - // attributes before inserting the node into the document. - if (aNotify && IsInComposedDoc() && mIsDoneAddingChildren && - aNameSpaceID == kNameSpaceID_None && aName == URIAttrName() - && !BlockEmbedContentLoading()) { - return LoadObject(aNotify, true); + return nsGenericHTMLElement::OnAttrSetButNotChanged(aNamespaceID, aName, + aValue, aNotify); +} + +nsresult +HTMLSharedObjectElement::AfterMaybeChangeAttr(int32_t aNamespaceID, + nsIAtom* aName, + bool aNotify) +{ + if (aNamespaceID == kNameSpaceID_None) { + if (aName == URIAttrName()) { + // If aNotify is false, we are coming from the parser or some such place; + // we'll get bound after all the attributes have been set, so we'll do the + // object load from BindToTree/DoneAddingChildren. + // Skip the LoadObject call in that case. + // We also don't want to start loading the object when we're not yet in + // a document, just in case that the caller wants to set additional + // attributes before inserting the node into the document. + if (aNotify && IsInComposedDoc() && mIsDoneAddingChildren) { + nsresult rv = LoadObject(aNotify, true); + NS_ENSURE_SUCCESS(rv, rv); + } + } } return NS_OK; diff --git a/dom/html/HTMLSharedObjectElement.h b/dom/html/HTMLSharedObjectElement.h index c70ba8ebd0..07cf86cb01 100644 --- a/dom/html/HTMLSharedObjectElement.h +++ b/dom/html/HTMLSharedObjectElement.h @@ -57,9 +57,6 @@ public: bool aCompileEventHandlers) override; virtual void UnbindFromTree(bool aDeep = true, bool aNullParent = true) override; - virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom *aName, - nsIAtom *aPrefix, const nsAString &aValue, - bool aNotify) override; virtual bool IsHTMLFocusable(bool aWithMouse, bool *aIsFocusable, int32_t *aTabIndex) override; virtual IMEState GetDesiredIMEState() override; @@ -200,6 +197,16 @@ public: * Calls LoadObject with the correct arguments to start the plugin load. */ void StartObjectLoad(bool aNotify, bool aForceLoad); + +protected: + virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; + virtual nsresult OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString& aValue, + bool aNotify) override; + private: virtual ~HTMLSharedObjectElement(); @@ -222,6 +229,17 @@ private: nsRuleData* aData); /** + * This function is called by AfterSetAttr and OnAttrSetButNotChanged. + * It will not be called if the value is being unset. + * + * @param aNamespaceID the namespace of the attr being set + * @param aName the localname of the attribute being set + * @param aNotify Whether we plan to notify document observers. + */ + nsresult AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName, + bool aNotify); + + /** * Decides whether we should load embed node content. * * If this is an embed node there are cases in which we should not try to load diff --git a/dom/html/HTMLSlotElement.cpp b/dom/html/HTMLSlotElement.cpp new file mode 100644 index 0000000000..18b8ef87bf --- /dev/null +++ b/dom/html/HTMLSlotElement.cpp @@ -0,0 +1,235 @@ +/* -*- 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 "mozilla/dom/DocGroup.h" +#include "mozilla/dom/HTMLSlotElement.h" +#include "mozilla/dom/HTMLSlotElementBinding.h" +#include "mozilla/dom/HTMLUnknownElement.h" +#include "mozilla/dom/ShadowRoot.h" +#include "nsGkAtoms.h" +#include "nsDocument.h" + +nsGenericHTMLElement* +NS_NewHTMLSlotElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, + mozilla::dom::FromParser aFromParser) +{ + RefPtr<mozilla::dom::NodeInfo> nodeInfo(aNodeInfo); + if (nsDocument::IsWebComponentsEnabled(nodeInfo->GetDocument())) { + already_AddRefed<mozilla::dom::NodeInfo> nodeInfoArg(nodeInfo.forget()); + return new mozilla::dom::HTMLSlotElement(nodeInfoArg); + } + + already_AddRefed<mozilla::dom::NodeInfo> nodeInfoArg(nodeInfo.forget()); + return new mozilla::dom::HTMLUnknownElement(nodeInfoArg); +} + +namespace mozilla { +namespace dom { + +HTMLSlotElement::HTMLSlotElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : nsGenericHTMLElement(aNodeInfo) +{ +} + +HTMLSlotElement::~HTMLSlotElement() +{ +} + +NS_IMPL_ADDREF_INHERITED(HTMLSlotElement, nsGenericHTMLElement) +NS_IMPL_RELEASE_INHERITED(HTMLSlotElement, nsGenericHTMLElement) + +NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLSlotElement, + nsGenericHTMLElement, + mAssignedNodes) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HTMLSlotElement) +NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement) + +NS_IMPL_ELEMENT_CLONE(HTMLSlotElement) + +nsresult +HTMLSlotElement::BindToTree(nsIDocument* aDocument, + nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) +{ + RefPtr<ShadowRoot> oldContainingShadow = GetContainingShadow(); + + nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent, + aBindingParent, + aCompileEventHandlers); + NS_ENSURE_SUCCESS(rv, rv); + + ShadowRoot* containingShadow = GetContainingShadow(); + if (containingShadow && !oldContainingShadow) { + containingShadow->AddSlot(this); + } + + return NS_OK; +} + +void +HTMLSlotElement::UnbindFromTree(bool aDeep, bool aNullParent) +{ + RefPtr<ShadowRoot> oldContainingShadow = GetContainingShadow(); + + nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); + + if (oldContainingShadow && !GetContainingShadow()) { + oldContainingShadow->RemoveSlot(this); + } +} + +nsresult +HTMLSlotElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, + const nsAttrValueOrString* aValue, + bool aNotify) +{ + if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::name) { + if (ShadowRoot* containingShadow = GetContainingShadow()) { + containingShadow->RemoveSlot(this); + } + } + + return nsGenericHTMLElement::BeforeSetAttr(aNameSpaceID, aName, aValue, + aNotify); +} + +nsresult +HTMLSlotElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) +{ + + if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::name) { + if (ShadowRoot* containingShadow = GetContainingShadow()) { + containingShadow->AddSlot(this); + } + } + + return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue, + aOldValue, aNotify); +} + +/** + * Flatten assigned nodes given a slot, as in: + * https://dom.spec.whatwg.org/#find-flattened-slotables + */ +static void +FlattenAssignedNodes(HTMLSlotElement* aSlot, nsTArray<RefPtr<nsINode>>& aNodes) +{ + if (!aSlot->GetContainingShadow()) { + return; + } + + const nsTArray<RefPtr<nsINode>>& assignedNodes = aSlot->AssignedNodes(); + + // If assignedNodes is empty, use children of slot as fallback content. + if (assignedNodes.IsEmpty()) { + for (nsIContent* child = aSlot->AsContent()->GetFirstChild(); + child; + child = child->GetNextSibling()) { + if (!child->IsSlotable()) { + continue; + } + + if (child->IsHTMLElement(nsGkAtoms::slot)) { + FlattenAssignedNodes(HTMLSlotElement::FromContent(child), aNodes); + } else { + aNodes.AppendElement(child); + } + } + return; + } + + for (uint32_t i = 0; i < assignedNodes.Length(); i++) { + nsINode* assignedNode = assignedNodes[i]; + if (assignedNode->IsHTMLElement(nsGkAtoms::slot)) { + FlattenAssignedNodes( + HTMLSlotElement::FromContent(assignedNode->AsContent()), aNodes); + } else { + aNodes.AppendElement(assignedNode); + } + } +} + +void +HTMLSlotElement::AssignedNodes(const AssignedNodesOptions& aOptions, + nsTArray<RefPtr<nsINode>>& aNodes) +{ + if (aOptions.mFlatten) { + return FlattenAssignedNodes(this, aNodes); + } + + aNodes = mAssignedNodes; +} + +const nsTArray<RefPtr<nsINode>>& +HTMLSlotElement::AssignedNodes() const +{ + return mAssignedNodes; +} + +void +HTMLSlotElement::InsertAssignedNode(uint32_t aIndex, nsINode* aNode) +{ + mAssignedNodes.InsertElementAt(aIndex, aNode); + aNode->AsContent()->SetAssignedSlot(this); +} + +void +HTMLSlotElement::AppendAssignedNode(nsINode* aNode) +{ + mAssignedNodes.AppendElement(aNode); + aNode->AsContent()->SetAssignedSlot(this); +} + +void +HTMLSlotElement::RemoveAssignedNode(nsINode* aNode) +{ + mAssignedNodes.RemoveElement(aNode); + aNode->AsContent()->SetAssignedSlot(nullptr); +} + +void +HTMLSlotElement::ClearAssignedNodes() +{ + for (uint32_t i = 0; i < mAssignedNodes.Length(); i++) { + mAssignedNodes[i]->AsContent()->SetAssignedSlot(nullptr); + } + + mAssignedNodes.Clear(); +} + +void +HTMLSlotElement::EnqueueSlotChangeEvent() const +{ + DocGroup* docGroup = OwnerDoc()->GetDocGroup(); + if (!docGroup) { + return; + } + + docGroup->SignalSlotChange(this); +} + +void +HTMLSlotElement::FireSlotChangeEvent() +{ + nsContentUtils::DispatchTrustedEvent(OwnerDoc(), + static_cast<nsIContent*>(this), + NS_LITERAL_STRING("slotchange"), true, + false); +} + +JSObject* +HTMLSlotElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return HTMLSlotElementBinding::Wrap(aCx, this, aGivenProto); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/html/HTMLSlotElement.h b/dom/html/HTMLSlotElement.h new file mode 100644 index 0000000000..0494a654e6 --- /dev/null +++ b/dom/html/HTMLSlotElement.h @@ -0,0 +1,79 @@ +/* -*- 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_HTMLSlotElement_h +#define mozilla_dom_HTMLSlotElement_h + +#include "nsIDOMHTMLElement.h" +#include "nsGenericHTMLElement.h" +#include "nsTArray.h" + +namespace mozilla { +namespace dom { + +struct AssignedNodesOptions; + +class HTMLSlotElement final : public nsGenericHTMLElement +{ +public: + explicit HTMLSlotElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); + NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLSlotElement, slot) + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLSlotElement, nsGenericHTMLElement) + virtual nsresult Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) const override; + + // nsIContent + virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) override; + virtual void UnbindFromTree(bool aDeep = true, + bool aNullParent = true) override; + + virtual nsresult BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, + const nsAttrValueOrString* aValue, + bool aNotify) override; + virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; + + // WebIDL + void SetName(const nsAString& aName, ErrorResult& aRv) + { + SetHTMLAttr(nsGkAtoms::name, aName, aRv); + } + + void GetName(nsAString& aName) + { + GetHTMLAttr(nsGkAtoms::name, aName); + } + + void AssignedNodes(const AssignedNodesOptions& aOptions, + nsTArray<RefPtr<nsINode>>& aNodes); + + // Helper methods + const nsTArray<RefPtr<nsINode>>& AssignedNodes() const; + void InsertAssignedNode(uint32_t aIndex, nsINode* aNode); + void AppendAssignedNode(nsINode* aNode); + void RemoveAssignedNode(nsINode* aNode); + void ClearAssignedNodes(); + + void EnqueueSlotChangeEvent() const; + void FireSlotChangeEvent(); + +protected: + virtual ~HTMLSlotElement(); + virtual JSObject* + WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + + nsTArray<RefPtr<nsINode>> mAssignedNodes; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_HTMLSlotElement_h diff --git a/dom/html/HTMLSourceElement.cpp b/dom/html/HTMLSourceElement.cpp index 73d9c9d8cb..93a17ea8c1 100644 --- a/dom/html/HTMLSourceElement.cpp +++ b/dom/html/HTMLSourceElement.cpp @@ -98,7 +98,8 @@ HTMLSourceElement::UpdateMediaList(const nsAttrValue* aValue) nsresult HTMLSourceElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { // If we are associated with a <picture> with a valid <img>, notify it of // responsive parameter changes @@ -143,7 +144,7 @@ HTMLSourceElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, } return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, - aValue, aNotify); + aValue, aOldValue, aNotify); } nsresult diff --git a/dom/html/HTMLSourceElement.h b/dom/html/HTMLSourceElement.h index 595e21015b..71b676c2c5 100644 --- a/dom/html/HTMLSourceElement.h +++ b/dom/html/HTMLSourceElement.h @@ -110,6 +110,7 @@ protected: protected: virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) override; private: diff --git a/dom/html/HTMLStyleElement.cpp b/dom/html/HTMLStyleElement.cpp index 329dda6480..743f4addb9 100644 --- a/dom/html/HTMLStyleElement.cpp +++ b/dom/html/HTMLStyleElement.cpp @@ -171,7 +171,8 @@ HTMLStyleElement::UnbindFromTree(bool aDeep, bool aNullParent) nsresult HTMLStyleElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { if (aNameSpaceID == kNameSpaceID_None) { if (aName == nsGkAtoms::title || @@ -185,7 +186,7 @@ HTMLStyleElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, } return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue, - aNotify); + aOldValue, aNotify); } NS_IMETHODIMP diff --git a/dom/html/HTMLStyleElement.h b/dom/html/HTMLStyleElement.h index 6b2a12b1fe..bd69a6aa1a 100644 --- a/dom/html/HTMLStyleElement.h +++ b/dom/html/HTMLStyleElement.h @@ -48,6 +48,7 @@ public: bool aNullParent = true) override; virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) override; virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; diff --git a/dom/html/HTMLTableCellElement.cpp b/dom/html/HTMLTableCellElement.cpp index 1cf413413c..e8b338342d 100644 --- a/dom/html/HTMLTableCellElement.cpp +++ b/dom/html/HTMLTableCellElement.cpp @@ -107,9 +107,7 @@ HTMLTableCellElement::WalkContentStyleRules(nsRuleWalker* aRuleWalker) nsresult rv = nsGenericHTMLElement::WalkContentStyleRules(aRuleWalker); NS_ENSURE_SUCCESS(rv, rv); - if (HTMLTableElement* table = GetTable()) { - nsMappedAttributes* tableInheritedAttributes = - table->GetAttributesMappedForCell(); + if (nsMappedAttributes* tableInheritedAttributes = GetMappedAttributesInheritedFromTable()) { if (tableInheritedAttributes) { aRuleWalker->Forward(tableInheritedAttributes); } @@ -117,6 +115,16 @@ HTMLTableCellElement::WalkContentStyleRules(nsRuleWalker* aRuleWalker) return NS_OK; } +nsMappedAttributes* +HTMLTableCellElement::GetMappedAttributesInheritedFromTable() const +{ + if (HTMLTableElement* table = GetTable()) { + return table->GetAttributesMappedForCell(); + } + + return nullptr; +} + NS_IMETHODIMP HTMLTableCellElement::SetAbbr(const nsAString& aAbbr) { diff --git a/dom/html/HTMLTableCellElement.h b/dom/html/HTMLTableCellElement.h index ab7a918eb9..6740b870ed 100644 --- a/dom/html/HTMLTableCellElement.h +++ b/dom/html/HTMLTableCellElement.h @@ -148,6 +148,8 @@ public: virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() const override; NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker) override; NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; + // Get mapped attributes of ancestor table, if any + nsMappedAttributes* GetMappedAttributesInheritedFromTable() const; virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; diff --git a/dom/html/HTMLTableElement.cpp b/dom/html/HTMLTableElement.cpp index c5b7696cfd..e6aa71e51e 100644 --- a/dom/html/HTMLTableElement.cpp +++ b/dom/html/HTMLTableElement.cpp @@ -322,7 +322,7 @@ TableRowsCollection::ParentDestroyed() HTMLTableElement::HTMLTableElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) : nsGenericHTMLElement(aNodeInfo), - mTableInheritedAttributes(TABLE_ATTRS_DIRTY) + mTableInheritedAttributes(nullptr) { SetHasWeirdParserInsertionMode(); } @@ -912,20 +912,15 @@ MapInheritedTableAttributesIntoRule(const nsMappedAttributes* aAttributes, nsMappedAttributes* HTMLTableElement::GetAttributesMappedForCell() { - if (mTableInheritedAttributes) { - if (mTableInheritedAttributes == TABLE_ATTRS_DIRTY) - BuildInheritedAttributes(); - if (mTableInheritedAttributes != TABLE_ATTRS_DIRTY) - return mTableInheritedAttributes; - } - return nullptr; + return mTableInheritedAttributes; } void HTMLTableElement::BuildInheritedAttributes() { - NS_ASSERTION(mTableInheritedAttributes == TABLE_ATTRS_DIRTY, + NS_ASSERTION(!mTableInheritedAttributes, "potential leak, plus waste of work"); + MOZ_ASSERT(NS_IsMainThread()); nsIDocument *document = GetComposedDoc(); nsHTMLStyleSheet* sheet = document ? document->GetAttributeStyleSheet() : nullptr; @@ -938,7 +933,9 @@ HTMLTableElement::BuildInheritedAttributes() if (modifiableMapped) { nsAttrValue val(*value); - modifiableMapped->SetAndTakeAttr(nsGkAtoms::cellpadding, val); + bool oldValueSet; + modifiableMapped->SetAndSwapAttr(nsGkAtoms::cellpadding, val, + &oldValueSet); } newAttrs = sheet->UniqueMappedAttributes(modifiableMapped); NS_ASSERTION(newAttrs, "out of memory, but handling gracefully"); @@ -960,10 +957,7 @@ HTMLTableElement::BuildInheritedAttributes() void HTMLTableElement::ReleaseInheritedAttributes() { - if (mTableInheritedAttributes && - mTableInheritedAttributes != TABLE_ATTRS_DIRTY) - NS_RELEASE(mTableInheritedAttributes); - mTableInheritedAttributes = TABLE_ATTRS_DIRTY; + NS_IF_RELEASE(mTableInheritedAttributes); } nsresult @@ -972,9 +966,12 @@ HTMLTableElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, bool aCompileEventHandlers) { ReleaseInheritedAttributes(); - return nsGenericHTMLElement::BindToTree(aDocument, aParent, - aBindingParent, - aCompileEventHandlers); + nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent, + aBindingParent, + aCompileEventHandlers); + NS_ENSURE_SUCCESS(rv, rv); + BuildInheritedAttributes(); + return NS_OK; } void @@ -986,7 +983,7 @@ HTMLTableElement::UnbindFromTree(bool aDeep, bool aNullParent) nsresult HTMLTableElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsAttrValueOrString* aValue, + const nsAttrValueOrString* aValue, bool aNotify) { if (aName == nsGkAtoms::cellpadding && aNameSpaceID == kNameSpaceID_None) { @@ -999,13 +996,13 @@ HTMLTableElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, nsresult HTMLTableElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, const nsAttrValue* aValue, - bool aNotify) + const nsAttrValue* aOldValue, bool aNotify) { if (aName == nsGkAtoms::cellpadding && aNameSpaceID == kNameSpaceID_None) { BuildInheritedAttributes(); } return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue, - aNotify); + aOldValue, aNotify); } } // namespace dom diff --git a/dom/html/HTMLTableElement.h b/dom/html/HTMLTableElement.h index 4e172964bf..ce9d224b93 100644 --- a/dom/html/HTMLTableElement.h +++ b/dom/html/HTMLTableElement.h @@ -14,8 +14,6 @@ namespace mozilla { namespace dom { -#define TABLE_ATTRS_DIRTY ((nsMappedAttributes*)0x1) - class TableRowsCollection; class HTMLTableElement final : public nsGenericHTMLElement @@ -190,13 +188,15 @@ public: * Called when an attribute is about to be changed */ virtual nsresult BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsAttrValueOrString* aValue, + const nsAttrValueOrString* aValue, bool aNotify) override; /** * Called when an attribute has just been changed */ virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) override; + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLTableElement, nsGenericHTMLElement) @@ -220,8 +220,6 @@ protected: RefPtr<nsContentList> mTBodies; RefPtr<TableRowsCollection> mRows; - // Sentinel value of TABLE_ATTRS_DIRTY indicates that this is dirty and needs - // to be recalculated. nsMappedAttributes *mTableInheritedAttributes; void BuildInheritedAttributes(); void ReleaseInheritedAttributes(); diff --git a/dom/html/HTMLTextAreaElement.cpp b/dom/html/HTMLTextAreaElement.cpp index 42bc02435e..f25241d60e 100644 --- a/dom/html/HTMLTextAreaElement.cpp +++ b/dom/html/HTMLTextAreaElement.cpp @@ -506,7 +506,7 @@ HTMLTextAreaElement::IsDisabledForEvents(EventMessage aMessage) } nsresult -HTMLTextAreaElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +HTMLTextAreaElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.mCanHandle = false; if (IsDisabledForEvents(aVisitor.mEvent->mMessage)) { @@ -534,11 +534,22 @@ HTMLTextAreaElement::PreHandleEvent(EventChainPreVisitor& aVisitor) aVisitor.mEvent->mFlags.mNoContentDispatch = false; } - // Fire onchange (if necessary), before we do the blur, bug 370521. if (aVisitor.mEvent->mMessage == eBlur) { - FireChangeEventIfNeeded(); + // Set mWantsPreHandleEvent and fire change event in PreHandleEvent to + // prevent it breaks event target chain creation. + aVisitor.mWantsPreHandleEvent = true; } + return nsGenericHTMLFormElementWithState::GetEventTargetParent(aVisitor); +} + +nsresult +HTMLTextAreaElement::PreHandleEvent(EventChainVisitor& aVisitor) +{ + if (aVisitor.mEvent->mMessage == eBlur) { + // Fire onchange (if necessary), before we do the blur, bug 370521. + FireChangeEventIfNeeded(); + } return nsGenericHTMLFormElementWithState::PreHandleEvent(aVisitor); } @@ -1275,7 +1286,7 @@ HTMLTextAreaElement::UnbindFromTree(bool aDeep, bool aNullParent) nsresult HTMLTextAreaElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsAttrValueOrString* aValue, + const nsAttrValueOrString* aValue, bool aNotify) { if (aNotify && aName == nsGkAtoms::disabled && @@ -1337,7 +1348,8 @@ HTMLTextAreaElement::ContentChanged(nsIContent* aContent) nsresult HTMLTextAreaElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { if (aNameSpaceID == kNameSpaceID_None) { if (aName == nsGkAtoms::required || aName == nsGkAtoms::disabled || @@ -1353,12 +1365,10 @@ HTMLTextAreaElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, } else if (aName == nsGkAtoms::minlength) { UpdateTooShortValidityState(); } - - UpdateState(aNotify); } return nsGenericHTMLFormElementWithState::AfterSetAttr(aNameSpaceID, aName, aValue, - aNotify); + aOldValue, aNotify); } nsresult diff --git a/dom/html/HTMLTextAreaElement.h b/dom/html/HTMLTextAreaElement.h index 0390828182..5ab58554ec 100644 --- a/dom/html/HTMLTextAreaElement.h +++ b/dom/html/HTMLTextAreaElement.h @@ -125,7 +125,9 @@ public: int32_t aModType) const override; NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; + virtual nsresult PreHandleEvent(EventChainVisitor& aVisitor) override; virtual nsresult PostHandleEvent( EventChainPostVisitor& aVisitor) override; @@ -142,7 +144,7 @@ public: * Called when an attribute is about to be changed */ virtual nsresult BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsAttrValueOrString* aValue, + const nsAttrValueOrString* aValue, bool aNotify) override; // nsIMutationObserver @@ -355,7 +357,9 @@ protected: void ContentChanged(nsIContent* aContent); virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom *aName, - const nsAttrValue* aValue, bool aNotify) override; + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; /** * Return if an element should have a specific validity UI diff --git a/dom/html/HTMLUnknownElement.h b/dom/html/HTMLUnknownElement.h index c77fba919e..6390cc5761 100644 --- a/dom/html/HTMLUnknownElement.h +++ b/dom/html/HTMLUnknownElement.h @@ -7,6 +7,7 @@ #define mozilla_dom_HTMLUnknownElement_h #include "mozilla/Attributes.h" +#include "mozilla/EventStates.h" #include "nsGenericHTMLElement.h" namespace mozilla { @@ -27,7 +28,7 @@ public: : nsGenericHTMLElement(aNodeInfo) { if (NodeInfo()->Equals(nsGkAtoms::bdi)) { - SetHasDirAuto(); + AddStatesSilently(NS_EVENT_STATE_DIR_ATTR_LIKE_AUTO); } } diff --git a/dom/html/moz.build b/dom/html/moz.build index c86c169b5c..342b027a5d 100644 --- a/dom/html/moz.build +++ b/dom/html/moz.build @@ -52,7 +52,6 @@ EXPORTS.mozilla.dom += [ 'HTMLBRElement.h', 'HTMLButtonElement.h', 'HTMLCanvasElement.h', - 'HTMLContentElement.h', 'HTMLDataElement.h', 'HTMLDataListElement.h', 'HTMLDetailsElement.h', @@ -92,10 +91,10 @@ EXPORTS.mozilla.dom += [ 'HTMLProgressElement.h', 'HTMLScriptElement.h', 'HTMLSelectElement.h', - 'HTMLShadowElement.h', 'HTMLSharedElement.h', 'HTMLSharedListElement.h', 'HTMLSharedObjectElement.h', + 'HTMLSlotElement.h', 'HTMLSourceElement.h', 'HTMLSpanElement.h', 'HTMLStyleElement.h', @@ -131,7 +130,6 @@ UNIFIED_SOURCES += [ 'HTMLBRElement.cpp', 'HTMLButtonElement.cpp', 'HTMLCanvasElement.cpp', - 'HTMLContentElement.cpp', 'HTMLDataElement.cpp', 'HTMLDataListElement.cpp', 'HTMLDetailsElement.cpp', @@ -172,10 +170,10 @@ UNIFIED_SOURCES += [ 'HTMLProgressElement.cpp', 'HTMLScriptElement.cpp', 'HTMLSelectElement.cpp', - 'HTMLShadowElement.cpp', 'HTMLSharedElement.cpp', 'HTMLSharedListElement.cpp', 'HTMLSharedObjectElement.cpp', + 'HTMLSlotElement.cpp', 'HTMLSourceElement.cpp', 'HTMLSpanElement.cpp', 'HTMLStyleElement.cpp', diff --git a/dom/html/nsDOMStringMap.cpp b/dom/html/nsDOMStringMap.cpp index 6d2bc424d3..7e2ed0ece3 100644 --- a/dom/html/nsDOMStringMap.cpp +++ b/dom/html/nsDOMStringMap.cpp @@ -62,6 +62,12 @@ nsDOMStringMap::~nsDOMStringMap() } } +DocGroup* +nsDOMStringMap::GetDocGroup() const +{ + return mElement ? mElement->GetDocGroup() : nullptr; +} + /* virtual */ JSObject* nsDOMStringMap::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) diff --git a/dom/html/nsDOMStringMap.h b/dom/html/nsDOMStringMap.h index bf28957f7a..6506f1464f 100644 --- a/dom/html/nsDOMStringMap.h +++ b/dom/html/nsDOMStringMap.h @@ -16,6 +16,9 @@ namespace mozilla { class ErrorResult; +namespace dom { +class DocGroup; +} // namespace dom } // namespace mozilla class nsDOMStringMap : public nsStubMutationObserver, @@ -32,6 +35,8 @@ public: return mElement; } + mozilla::dom::DocGroup* GetDocGroup() const; + explicit nsDOMStringMap(mozilla::dom::Element* aElement); // WebIDL API diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp index 78e4d5b958..3cf19ea8f9 100644 --- a/dom/html/nsGenericHTMLElement.cpp +++ b/dom/html/nsGenericHTMLElement.cpp @@ -607,16 +607,16 @@ nsGenericHTMLElement::CheckHandleEventForAnchorsPreconditions( } nsresult -nsGenericHTMLElement::PreHandleEventForAnchors(EventChainPreVisitor& aVisitor) +nsGenericHTMLElement::GetEventTargetParentForAnchors(EventChainPreVisitor& aVisitor) { - nsresult rv = nsGenericHTMLElementBase::PreHandleEvent(aVisitor); + nsresult rv = nsGenericHTMLElementBase::GetEventTargetParent(aVisitor); NS_ENSURE_SUCCESS(rv, rv); if (!CheckHandleEventForAnchorsPreconditions(aVisitor)) { return NS_OK; } - return PreHandleEventForLinks(aVisitor); + return GetEventTargetParentForLinks(aVisitor); } nsresult @@ -655,8 +655,46 @@ nsGenericHTMLElement::GetHrefURIForAnchors() const } nsresult +nsGenericHTMLElement::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString* aValue, + bool aNotify) +{ + if (aNamespaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::accesskey) { + // Have to unregister before clearing flag. See UnregAccessKey + UnregAccessKey(); + if (!aValue) { + UnsetFlags(NODE_HAS_ACCESSKEY); + } + } else if (aName == nsGkAtoms::name) { + // Have to do this before clearing flag. See RemoveFromNameTable + RemoveFromNameTable(); + if (!aValue || aValue->IsEmpty()) { + ClearHasName(); + } + } else if (aName == nsGkAtoms::contenteditable) { + if (aValue) { + // Set this before the attribute is set so that any subclass code that + // runs before the attribute is set won't think we're missing a + // contenteditable attr when we actually have one. + SetMayHaveContentEditableAttr(); + } + } + if (!aValue && IsEventAttributeName(aName)) { + if (EventListenerManager* manager = GetExistingListenerManager()) { + manager->RemoveEventHandler(aName, EmptyString()); + } + } + } + + return nsGenericHTMLElementBase::BeforeSetAttr(aNamespaceID, aName, aValue, + aNotify); +} + +nsresult nsGenericHTMLElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { if (aNamespaceID == kNameSpaceID_None) { if (IsEventAttributeName(aName) && aValue) { @@ -670,34 +708,83 @@ nsGenericHTMLElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, } else if (aName == nsGkAtoms::dir) { Directionality dir = eDir_LTR; + // A boolean tracking whether we need to recompute our directionality. + // This needs to happen after we update our internal "dir" attribute + // state but before we call SetDirectionalityOnDescendants. + bool recomputeDirectionality = false; + // We don't want to have to keep getting the "dir" attribute in + // IntrinsicState, so we manually recompute our dir-related event states + // here and send the relevant update notifications. + EventStates dirStates; if (aValue && aValue->Type() == nsAttrValue::eEnum) { SetHasValidDir(); + dirStates |= NS_EVENT_STATE_HAS_DIR_ATTR; Directionality dirValue = (Directionality)aValue->GetEnumValue(); if (dirValue == eDir_Auto) { - SetHasDirAuto(); - ClearHasFixedDir(); + dirStates |= NS_EVENT_STATE_DIR_ATTR_LIKE_AUTO; } else { dir = dirValue; SetDirectionality(dir, aNotify); - ClearHasDirAuto(); - SetHasFixedDir(); + if (dirValue == eDir_LTR) { + dirStates |= NS_EVENT_STATE_DIR_ATTR_LTR; + } else { + MOZ_ASSERT(dirValue == eDir_RTL); + dirStates |= NS_EVENT_STATE_DIR_ATTR_RTL; + } } } else { + if (aValue) { + // We have a value, just not a valid one. + dirStates |= NS_EVENT_STATE_HAS_DIR_ATTR; + } ClearHasValidDir(); - ClearHasFixedDir(); if (NodeInfo()->Equals(nsGkAtoms::bdi)) { - SetHasDirAuto(); + dirStates |= NS_EVENT_STATE_DIR_ATTR_LIKE_AUTO; } else { - ClearHasDirAuto(); - dir = RecomputeDirectionality(this, aNotify); + recomputeDirectionality = true; } } + // Now figure out what's changed about our dir states. + EventStates oldDirStates = State() & DIR_ATTR_STATES; + EventStates changedStates = dirStates ^ oldDirStates; + ToggleStates(changedStates, aNotify); + if (recomputeDirectionality) { + dir = RecomputeDirectionality(this, aNotify); + } SetDirectionalityOnDescendants(this, dir, aNotify); + } else if (aName == nsGkAtoms::contenteditable) { + int32_t editableCountDelta = 0; + if (aOldValue && + (aOldValue->Equals(NS_LITERAL_STRING("true"), eIgnoreCase) || + aOldValue->Equals(EmptyString(), eIgnoreCase))) { + editableCountDelta = -1; + } + if (aValue && (aValue->Equals(NS_LITERAL_STRING("true"), eIgnoreCase) || + aValue->Equals(EmptyString(), eIgnoreCase))) { + ++editableCountDelta; + } + ChangeEditableState(editableCountDelta); + } else if (aName == nsGkAtoms::accesskey) { + if (aValue && !aValue->Equals(EmptyString(), eIgnoreCase)) { + SetFlags(NODE_HAS_ACCESSKEY); + RegAccessKey(); + } + } else if (aName == nsGkAtoms::name) { + if (aValue && !aValue->Equals(EmptyString(), eIgnoreCase) && + CanHaveName(NodeInfo()->NameAtom())) { + // This may not be quite right because we can have subclass code run + // before here. But in practice subclasses don't care about this flag, + // and in particular selector matching does not care. Otherwise we'd + // want to handle it like we handle id attributes (in PreIdMaybeChange + // and PostIdMaybeChange). + SetHasName(); + AddToNameTable(aValue->GetAtomValue()); + } } } return nsGenericHTMLElementBase::AfterSetAttr(aNamespaceID, aName, - aValue, aNotify); + aValue, aOldValue, aNotify); } EventListenerManager* @@ -818,87 +905,6 @@ nsGenericHTMLElement::SetOn##name_(EventHandlerNonNull* handler) \ #undef FORWARDED_EVENT #undef EVENT -nsresult -nsGenericHTMLElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) -{ - bool contentEditable = aNameSpaceID == kNameSpaceID_None && - aName == nsGkAtoms::contenteditable; - bool accessKey = aName == nsGkAtoms::accesskey && - aNameSpaceID == kNameSpaceID_None; - - int32_t change = 0; - if (contentEditable) { - change = GetContentEditableValue() == eTrue ? -1 : 0; - SetMayHaveContentEditableAttr(); - } - - if (accessKey) { - UnregAccessKey(); - } - - nsresult rv = nsStyledElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue, - aNotify); - NS_ENSURE_SUCCESS(rv, rv); - - if (contentEditable) { - if (aValue.IsEmpty() || aValue.LowerCaseEqualsLiteral("true")) { - change += 1; - } - - ChangeEditableState(change); - } - - if (accessKey && !aValue.IsEmpty()) { - SetFlags(NODE_HAS_ACCESSKEY); - RegAccessKey(); - } - - return NS_OK; -} - -nsresult -nsGenericHTMLElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, - bool aNotify) -{ - bool contentEditable = false; - int32_t contentEditableChange = 0; - - // Check for event handlers - if (aNameSpaceID == kNameSpaceID_None) { - if (aAttribute == nsGkAtoms::name) { - // Have to do this before clearing flag. See RemoveFromNameTable - RemoveFromNameTable(); - ClearHasName(); - } - else if (aAttribute == nsGkAtoms::contenteditable) { - contentEditable = true; - contentEditableChange = GetContentEditableValue() == eTrue ? -1 : 0; - } - else if (aAttribute == nsGkAtoms::accesskey) { - // Have to unregister before clearing flag. See UnregAccessKey - UnregAccessKey(); - UnsetFlags(NODE_HAS_ACCESSKEY); - } - else if (IsEventAttributeName(aAttribute)) { - if (EventListenerManager* manager = GetExistingListenerManager()) { - manager->RemoveEventHandler(aAttribute, EmptyString()); - } - } - } - - nsresult rv = nsGenericHTMLElementBase::UnsetAttr(aNameSpaceID, aAttribute, - aNotify); - NS_ENSURE_SUCCESS(rv, rv); - - if (contentEditable) { - ChangeEditableState(contentEditableChange); - } - - return NS_OK; -} - void nsGenericHTMLElement::GetBaseTarget(nsAString& aBaseTarget) const { @@ -928,19 +934,13 @@ nsGenericHTMLElement::ParseAttribute(int32_t aNamespaceID, if (aAttribute == nsGkAtoms::name) { // Store name as an atom. name="" means that the element has no name, - // not that it has an emptystring as the name. - RemoveFromNameTable(); + // not that it has an empty string as the name. if (aValue.IsEmpty()) { ClearHasName(); return false; } aResult.ParseAtom(aValue); - - if (CanHaveName(NodeInfo()->NameAtom())) { - SetHasName(); - AddToNameTable(aResult.GetAtomValue()); - } return true; } @@ -1962,7 +1962,7 @@ nsGenericHTMLFormElement::UnbindFromTree(bool aDeep, bool aNullParent) nsresult nsGenericHTMLFormElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsAttrValueOrString* aValue, + const nsAttrValueOrString* aValue, bool aNotify) { if (aNameSpaceID == kNameSpaceID_None) { @@ -1995,13 +1995,6 @@ nsGenericHTMLFormElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, } mForm->RemoveElement(this, false); - - // Removing the element from the form can make it not be the default - // control anymore. Go ahead and notify on that change, though we might - // end up readding and becoming the default control again in - // AfterSetAttr. - // FIXME: Bug 656197 - UpdateState(aNotify); } if (aName == nsGkAtoms::form) { @@ -2022,7 +2015,8 @@ nsGenericHTMLFormElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, nsresult nsGenericHTMLFormElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { if (aNameSpaceID == kNameSpaceID_None) { // add the control to the hashtable as needed @@ -2051,12 +2045,6 @@ nsGenericHTMLFormElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, } mForm->AddElement(this, false, aNotify); - - // Adding the element to the form can make it be the default control . - // Go ahead and notify on that change. - // Note: no need to notify on CanBeDisabled(), since type attr - // changes can't affect that. - UpdateState(aNotify); } if (aName == nsGkAtoms::form) { @@ -2077,11 +2065,23 @@ nsGenericHTMLFormElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, } return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, - aValue, aNotify); + aValue, aOldValue, aNotify); +} + +nsresult +nsGenericHTMLFormElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) +{ + if (aVisitor.mEvent->IsTrusted() && (aVisitor.mEvent->mMessage == eFocus || + aVisitor.mEvent->mMessage == eBlur)) { + // We have to handle focus/blur event to change focus states in + // PreHandleEvent to prevent it breaks event target chain creation. + aVisitor.mWantsPreHandleEvent = true; + } + return nsGenericHTMLElement::GetEventTargetParent(aVisitor); } nsresult -nsGenericHTMLFormElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +nsGenericHTMLFormElement::PreHandleEvent(EventChainVisitor& aVisitor) { if (aVisitor.mEvent->IsTrusted()) { switch (aVisitor.mEvent->mMessage) { @@ -2105,7 +2105,6 @@ nsGenericHTMLFormElement::PreHandleEvent(EventChainPreVisitor& aVisitor) break; } } - return nsGenericHTMLElement::PreHandleEvent(aVisitor); } diff --git a/dom/html/nsGenericHTMLElement.h b/dom/html/nsGenericHTMLElement.h index 2b8b608b9d..1b0977fa28 100644 --- a/dom/html/nsGenericHTMLElement.h +++ b/dom/html/nsGenericHTMLElement.h @@ -465,17 +465,6 @@ public: virtual void UnbindFromTree(bool aDeep = true, bool aNullParent = true) override; - MOZ_ALWAYS_INLINE // Avoid a crashy hook from Avast 10 Beta - nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAString& aValue, bool aNotify) - { - return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify); - } - virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) override; - virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName, - bool aNotify) override; virtual bool IsFocusableInternal(int32_t *aTabIndex, bool aWithMouse) override { bool isFocusable = false; @@ -498,7 +487,8 @@ public: */ bool CheckHandleEventForAnchorsPreconditions( mozilla::EventChainVisitor& aVisitor); - nsresult PreHandleEventForAnchors(mozilla::EventChainPreVisitor& aVisitor); + nsresult GetEventTargetParentForAnchors( + mozilla::EventChainPreVisitor& aVisitor); nsresult PostHandleEventForAnchors(mozilla::EventChainPostVisitor& aVisitor); bool IsHTMLLink(nsIURI** aURI) const; @@ -913,8 +903,13 @@ private: void RegUnRegAccessKey(bool aDoReg); protected: + virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString* aValue, + bool aNotify) override; virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) override; + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; virtual mozilla::EventListenerManager* GetEventListenerManagerForAttr(nsIAtom* aAttrName, @@ -1219,8 +1214,10 @@ public: virtual IMEState GetDesiredIMEState() override; virtual mozilla::EventStates IntrinsicState() const override; - virtual nsresult PreHandleEvent( + virtual nsresult GetEventTargetParent( mozilla::EventChainPreVisitor& aVisitor) override; + virtual nsresult PreHandleEvent( + mozilla::EventChainVisitor& aVisitor) override; /** * This callback is called by a fieldest on all its elements whenever its @@ -1260,11 +1257,13 @@ protected: virtual ~nsGenericHTMLFormElement(); virtual nsresult BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsAttrValueOrString* aValue, + const nsAttrValueOrString* aValue, bool aNotify) override; virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) override; + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; /** * This method will update the form owner, using @form or looking to a parent. @@ -1305,7 +1304,7 @@ protected: static bool FormIdUpdated(Element* aOldElement, Element* aNewElement, void* aData); - // Returns true if the event should not be handled from PreHandleEvent + // Returns true if the event should not be handled from GetEventTargetParent bool IsElementDisabledForEvents(mozilla::EventMessage aMessage, nsIFrame* aFrame); @@ -1701,7 +1700,7 @@ NS_DECLARE_NS_NEW_HTML_ELEMENT(Pre) NS_DECLARE_NS_NEW_HTML_ELEMENT(Progress) NS_DECLARE_NS_NEW_HTML_ELEMENT(Script) NS_DECLARE_NS_NEW_HTML_ELEMENT(Select) -NS_DECLARE_NS_NEW_HTML_ELEMENT(Shadow) +NS_DECLARE_NS_NEW_HTML_ELEMENT(Slot) NS_DECLARE_NS_NEW_HTML_ELEMENT(Source) NS_DECLARE_NS_NEW_HTML_ELEMENT(Span) NS_DECLARE_NS_NEW_HTML_ELEMENT(Style) diff --git a/dom/html/nsGenericHTMLFrameElement.cpp b/dom/html/nsGenericHTMLFrameElement.cpp index 6e50a40921..8170179e2c 100644 --- a/dom/html/nsGenericHTMLFrameElement.cpp +++ b/dom/html/nsGenericHTMLFrameElement.cpp @@ -27,6 +27,7 @@ #include "nsServiceManagerUtils.h" #include "nsSubDocumentFrame.h" #include "nsXULElement.h" +#include "nsAttrValueOrString.h" using namespace mozilla; using namespace mozilla::dom; @@ -334,55 +335,6 @@ nsGenericHTMLFrameElement::UnbindFromTree(bool aDeep, bool aNullParent) nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); } -nsresult -nsGenericHTMLFrameElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) -{ - nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, - aValue, aNotify); - NS_ENSURE_SUCCESS(rv, rv); - - if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::src && - (!IsHTMLElement(nsGkAtoms::iframe) || - !HasAttr(kNameSpaceID_None,nsGkAtoms::srcdoc))) { - // Don't propagate error here. The attribute was successfully set, that's - // what we should reflect. - LoadSrc(); - } else if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::name) { - // Propagate "name" to the docshell to make browsing context names live, - // per HTML5. - nsIDocShell *docShell = mFrameLoader ? mFrameLoader->GetExistingDocShell() - : nullptr; - if (docShell) { - docShell->SetName(aValue); - } - } - - return NS_OK; -} - -nsresult -nsGenericHTMLFrameElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, - bool aNotify) -{ - // Invoke on the superclass. - nsresult rv = nsGenericHTMLElement::UnsetAttr(aNameSpaceID, aAttribute, aNotify); - NS_ENSURE_SUCCESS(rv, rv); - - if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::name) { - // Propagate "name" to the docshell to make browsing context names live, - // per HTML5. - nsIDocShell *docShell = mFrameLoader ? mFrameLoader->GetExistingDocShell() - : nullptr; - if (docShell) { - docShell->SetName(EmptyString()); - } - } - - return NS_OK; -} - /* static */ int32_t nsGenericHTMLFrameElement::MapScrollingAttribute(const nsAttrValue* aValue) { @@ -399,37 +351,102 @@ nsGenericHTMLFrameElement::MapScrollingAttribute(const nsAttrValue* aValue) return mappedValue; } +static bool +PrincipalAllowsBrowserFrame(nsIPrincipal* aPrincipal) +{ + nsCOMPtr<nsIPermissionManager> permMgr = mozilla::services::GetPermissionManager(); + NS_ENSURE_TRUE(permMgr, false); + uint32_t permission = nsIPermissionManager::DENY_ACTION; + nsresult rv = permMgr->TestPermissionFromPrincipal(aPrincipal, "browser", &permission); + NS_ENSURE_SUCCESS(rv, false); + return permission == nsIPermissionManager::ALLOW_ACTION; +} + /* virtual */ nsresult nsGenericHTMLFrameElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, const nsAttrValue* aValue, - bool aNotify) -{ - if (aName == nsGkAtoms::scrolling && aNameSpaceID == kNameSpaceID_None) { - if (mFrameLoader) { - nsIDocShell* docshell = mFrameLoader->GetExistingDocShell(); - nsCOMPtr<nsIScrollable> scrollable = do_QueryInterface(docshell); - if (scrollable) { - int32_t cur; - scrollable->GetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_X, &cur); - int32_t val = MapScrollingAttribute(aValue); - if (cur != val) { - scrollable->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_X, val); - scrollable->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_Y, val); - RefPtr<nsPresContext> presContext; - docshell->GetPresContext(getter_AddRefs(presContext)); - nsIPresShell* shell = presContext ? presContext->GetPresShell() : nullptr; - nsIFrame* rootScroll = shell ? shell->GetRootScrollFrame() : nullptr; - if (rootScroll) { - shell->FrameNeedsReflow(rootScroll, nsIPresShell::eStyleChange, - NS_FRAME_IS_DIRTY); + const nsAttrValue* aOldValue, bool aNotify) +{ + if (aValue) { + nsAttrValueOrString value(aValue); + AfterMaybeChangeAttr(aNameSpaceID, aName, &value, aNotify); + } else { + AfterMaybeChangeAttr(aNameSpaceID, aName, nullptr, aNotify); + } + + if (aNameSpaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::scrolling) { + if (mFrameLoader) { + nsIDocShell* docshell = mFrameLoader->GetExistingDocShell(); + nsCOMPtr<nsIScrollable> scrollable = do_QueryInterface(docshell); + if (scrollable) { + int32_t cur; + scrollable->GetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_X, &cur); + int32_t val = MapScrollingAttribute(aValue); + if (cur != val) { + scrollable->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_X, val); + scrollable->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_Y, val); + RefPtr<nsPresContext> presContext; + docshell->GetPresContext(getter_AddRefs(presContext)); + nsIPresShell* shell = presContext ? presContext->GetPresShell() : nullptr; + nsIFrame* rootScroll = shell ? shell->GetRootScrollFrame() : nullptr; + if (rootScroll) { + shell->FrameNeedsReflow(rootScroll, nsIPresShell::eStyleChange, + NS_FRAME_IS_DIRTY); + } } } } + } else if (aName == nsGkAtoms::mozbrowser) { + mReallyIsBrowser = !!aValue && BrowserFramesEnabled() && + PrincipalAllowsBrowserFrame(NodePrincipal()); } } return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue, - aNotify); + aOldValue, aNotify); +} + +nsresult +nsGenericHTMLFrameElement::OnAttrSetButNotChanged(int32_t aNamespaceID, + nsIAtom* aName, + const nsAttrValueOrString& aValue, + bool aNotify) +{ + AfterMaybeChangeAttr(aNamespaceID, aName, &aValue, aNotify); + + return nsGenericHTMLElement::OnAttrSetButNotChanged(aNamespaceID, aName, + aValue, aNotify); +} + +void +nsGenericHTMLFrameElement::AfterMaybeChangeAttr(int32_t aNamespaceID, + nsIAtom* aName, + const nsAttrValueOrString* aValue, + bool aNotify) +{ + if (aNamespaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::src) { + if (aValue && (!IsHTMLElement(nsGkAtoms::iframe) || + !HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc))) { + // Don't propagate error here. The attribute was successfully set, + // that's what we should reflect. + LoadSrc(); + } + } else if (aName == nsGkAtoms::name) { + // Propagate "name" to the docshell to make browsing context names live, + // per HTML5. + nsIDocShell* docShell = mFrameLoader ? mFrameLoader->GetExistingDocShell() + : nullptr; + if (docShell) { + if (aValue) { + docShell->SetName(aValue->String()); + } else { + docShell->SetName(EmptyString()); + } + } + } + } } void @@ -503,28 +520,7 @@ nsGenericHTMLFrameElement::BrowserFramesEnabled() /* [infallible] */ nsresult nsGenericHTMLFrameElement::GetReallyIsBrowserOrApp(bool *aOut) { - *aOut = false; - - // Fail if browser frames are globally disabled. - if (!nsGenericHTMLFrameElement::BrowserFramesEnabled()) { - return NS_OK; - } - - // Fail if this frame doesn't have the mozbrowser attribute. - if (!GetBoolAttr(nsGkAtoms::mozbrowser)) { - return NS_OK; - } - - // Fail if the node principal isn't trusted. - nsIPrincipal *principal = NodePrincipal(); - nsCOMPtr<nsIPermissionManager> permMgr = - services::GetPermissionManager(); - NS_ENSURE_TRUE(permMgr, NS_OK); - - uint32_t permission = nsIPermissionManager::DENY_ACTION; - nsresult rv = permMgr->TestPermissionFromPrincipal(principal, "browser", &permission); - NS_ENSURE_SUCCESS(rv, NS_OK); - *aOut = permission == nsIPermissionManager::ALLOW_ACTION; + *aOut = mReallyIsBrowser; return NS_OK; } diff --git a/dom/html/nsGenericHTMLFrameElement.h b/dom/html/nsGenericHTMLFrameElement.h index d9c2df9d57..adcd17715e 100644 --- a/dom/html/nsGenericHTMLFrameElement.h +++ b/dom/html/nsGenericHTMLFrameElement.h @@ -36,6 +36,7 @@ public: , mIsPrerendered(false) , mBrowserFrameListenersRegistered(false) , mFrameLoaderCreationDisallowed(false) + , mReallyIsBrowser(false) { } @@ -52,19 +53,7 @@ public: bool aCompileEventHandlers) override; virtual void UnbindFromTree(bool aDeep = true, bool aNullParent = true) override; - nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAString& aValue, bool aNotify) - { - return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify); - } - virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) override; - virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, - bool aNotify) override; - virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, - bool aNotify) override; + virtual void DestroyContent() override; nsresult CopyInnerTo(mozilla::dom::Element* aDest); @@ -109,6 +98,14 @@ protected: nsresult GetContentDocument(nsIDOMDocument** aContentDocument); already_AddRefed<nsPIDOMWindowOuter> GetContentWindow(); + virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; + virtual nsresult OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString& aValue, + bool aNotify) override; + RefPtr<nsFrameLoader> mFrameLoader; nsCOMPtr<nsPIDOMWindowOuter> mOpenerWindow; @@ -122,6 +119,7 @@ protected: bool mIsPrerendered; bool mBrowserFrameListenersRegistered; bool mFrameLoaderCreationDisallowed; + bool mReallyIsBrowser; // This flag is only used by <iframe>. See HTMLIFrameElement:: // FullscreenFlag() for details. It is placed here so that we @@ -130,6 +128,18 @@ protected: private: void GetManifestURL(nsAString& aOut); + + /** + * This function is called by AfterSetAttr and OnAttrSetButNotChanged. + * It will be called whether the value is being set or unset. + * + * @param aNamespaceID the namespace of the attr being set + * @param aName the localname of the attribute being set + * @param aValue the value being set or null if the value is being unset + * @param aNotify Whether we plan to notify document observers. + */ + void AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString* aValue, bool aNotify); }; #endif // nsGenericHTMLFrameElement_h diff --git a/dom/html/nsHTMLContentSink.cpp b/dom/html/nsHTMLContentSink.cpp index 1fe5d2a865..eb22b772f9 100644 --- a/dom/html/nsHTMLContentSink.cpp +++ b/dom/html/nsHTMLContentSink.cpp @@ -14,6 +14,7 @@ #include "nsContentSink.h" #include "nsCOMPtr.h" +#include "nsHTMLTags.h" #include "nsReadableUtils.h" #include "nsUnicharUtils.h" #include "nsIHTMLContentSink.h" @@ -30,6 +31,7 @@ #include "mozilla/Logging.h" #include "nsNodeUtils.h" #include "nsIContent.h" +#include "mozilla/dom/CustomElementRegistry.h" #include "mozilla/dom/Element.h" #include "mozilla/Preferences.h" @@ -59,8 +61,6 @@ #include "nsIScriptGlobalObject.h" #include "nsNameSpaceManager.h" -#include "nsIParserService.h" - #include "nsIStyleSheetLinkingElement.h" #include "nsITimer.h" #include "nsError.h" @@ -262,10 +262,6 @@ NS_NewHTMLElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo; - nsIParserService* parserService = nsContentUtils::GetParserService(); - if (!parserService) - return NS_ERROR_OUT_OF_MEMORY; - nsIAtom *name = nodeInfo->NameAtom(); RefPtr<nsIAtom> tagAtom = nodeInfo->NameAtom(); RefPtr<nsIAtom> typeAtom = aIs ? NS_Atomize(*aIs) : tagAtom; @@ -273,7 +269,7 @@ NS_NewHTMLElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& NS_ASSERTION(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML), "Trying to HTML elements that don't have the XHTML namespace"); - int32_t tag = parserService->HTMLCaseSensitiveAtomTagToId(name); + int32_t tag = nsHTMLTags::CaseSensitiveAtomTagToId(name); bool isCustomElementName = (tag == eHTMLTag_userdefined && nsContentUtils::IsCustomElementName(name)); diff --git a/dom/html/nsTextEditorState.cpp b/dom/html/nsTextEditorState.cpp index 25be6016c4..812a8d0b6a 100644 --- a/dom/html/nsTextEditorState.cpp +++ b/dom/html/nsTextEditorState.cpp @@ -2214,6 +2214,23 @@ nsTextEditorState::SetValue(const nsAString& aValue, uint32_t aFlags) return true; } +bool +nsTextEditorState::HasNonEmptyValue() +{ + if (mEditor && mBoundFrame && mEditorInitialized && + !mIsCommittingComposition) { + bool empty; + nsresult rv = mEditor->GetDocumentIsEmpty(&empty); + if (NS_SUCCEEDED(rv)) { + return !empty; + } + } + + nsAutoString value; + GetValue(value, true); + return !value.IsEmpty(); +} + void nsTextEditorState::InitializeKeyboardEventListeners() { diff --git a/dom/html/nsTextEditorState.h b/dom/html/nsTextEditorState.h index caf5e8eedd..77d12e81a4 100644 --- a/dom/html/nsTextEditorState.h +++ b/dom/html/nsTextEditorState.h @@ -157,6 +157,11 @@ public: }; MOZ_MUST_USE bool SetValue(const nsAString& aValue, uint32_t aFlags); void GetValue(nsAString& aValue, bool aIgnoreWrap) const; + bool HasNonEmptyValue(); + // The following methods are for textarea element to use whether default + // value or not. + // XXX We might have to add assertion when it is into editable, + // or reconsider fixing bug 597525 to remove these. void EmptyValue() { if (mValue) mValue->Truncate(); } bool IsEmpty() const { return mValue ? mValue->IsEmpty() : true; } diff --git a/dom/html/reftests/body-frame-margin-remove-other-pres-hint-ref.html b/dom/html/reftests/body-frame-margin-remove-other-pres-hint-ref.html new file mode 100644 index 0000000000..8446d7f260 --- /dev/null +++ b/dom/html/reftests/body-frame-margin-remove-other-pres-hint-ref.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<head> + <title></title> +</head> +<body> +<script type="text/javascript"> + function loadFrame() { + document.documentElement.className = ""; + } +</script> +<iframe id=frame onload="loadFrame()" src="data:text/html,<body><span lang='en'>text</span></body>" marginwidth="100px" marginheight="100px" width=300px height=300px></iframe> +</body> +</html>
\ No newline at end of file diff --git a/dom/html/reftests/body-frame-margin-remove-other-pres-hint.html b/dom/html/reftests/body-frame-margin-remove-other-pres-hint.html new file mode 100644 index 0000000000..6f49536376 --- /dev/null +++ b/dom/html/reftests/body-frame-margin-remove-other-pres-hint.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<head> + <title></title> +</head> +<body> +<script type="text/javascript"> + function loadFrame() { + let frame = document.getElementById('frame'); + frame.contentDocument.body.removeAttribute('lang'); + document.documentElement.className = ""; + } +</script> +<iframe id=frame onload="loadFrame()" src="data:text/html,<body lang='en'>text</body>" marginwidth="100px" marginheight="100px" width=300px height=300px></iframe> +</body> +</html>
\ No newline at end of file diff --git a/dom/html/reftests/body-topmargin-dynamic.html b/dom/html/reftests/body-topmargin-dynamic.html new file mode 100644 index 0000000000..e6c8c505e7 --- /dev/null +++ b/dom/html/reftests/body-topmargin-dynamic.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<html> +<body> +this text should have a margin of 100px on the top and left +<p style="direction: rtl">this text should have a margin of 100px on the right</p> +<script type="text/javascript"> + document.body.setAttribute("topmargin", "100px"); + document.body.setAttribute("leftmargin", "100px"); + document.body.setAttribute("rightmargin", "100px"); +</script> +</body> +</html> diff --git a/dom/html/reftests/body-topmargin-ref.html b/dom/html/reftests/body-topmargin-ref.html new file mode 100644 index 0000000000..6530a0ae4b --- /dev/null +++ b/dom/html/reftests/body-topmargin-ref.html @@ -0,0 +1,7 @@ +<!DOCTYPE html> +<html> +<body topmargin="100px" leftmargin="100px" rightmargin="100px"> +this text should have a margin of 100px on the top and left +<p style="direction: rtl">this text should have a margin of 100px on the right</p> +</body> +</html> diff --git a/dom/html/reftests/reftest-stylo.list b/dom/html/reftests/reftest-stylo.list index dd6339edf8..570c073587 100644 --- a/dom/html/reftests/reftest-stylo.list +++ b/dom/html/reftests/reftest-stylo.list @@ -64,3 +64,10 @@ pref(permissions.default.image,2) HTTP == bug1196784-with-srcset.html bug1196784 # Test video with rotation information can be rotated. == bug1228601-video-rotation-90.html bug1228601-video-rotation-90.html + +# Test that dynamically setting body margin attributes updates style appropriately +== body-topmargin-dynamic.html body-topmargin-dynamic.html + +# Test that dynamically removing a nonmargin mapped attribute does not +# destroy margins inherited from the frame. +== body-frame-margin-remove-other-pres-hint.html body-frame-margin-remove-other-pres-hint.html diff --git a/dom/html/reftests/reftest.list b/dom/html/reftests/reftest.list index 27a13e7c92..82e773abb5 100644 --- a/dom/html/reftests/reftest.list +++ b/dom/html/reftests/reftest.list @@ -62,3 +62,10 @@ pref(permissions.default.image,2) HTTP == bug1196784-with-srcset.html bug1196784 # Test video with rotation information can be rotated. == bug1228601-video-rotation-90.html bug1228601-video-rotated-ref.html + +# Test that dynamically setting body margin attributes updates style appropriately +== body-topmargin-dynamic.html body-topmargin-ref.html + +# Test that dynamically removing a nonmargin mapped attribute does not +# destroy margins inherited from the frame. +== body-frame-margin-remove-other-pres-hint.html body-frame-margin-remove-other-pres-hint-ref.html diff --git a/dom/html/test/mochitest.ini b/dom/html/test/mochitest.ini index 4a50a9c3f5..5c9c66e614 100644 --- a/dom/html/test/mochitest.ini +++ b/dom/html/test/mochitest.ini @@ -569,7 +569,6 @@ support-files = file_cookiemanager.js support-files = file_bug871161-1.html file_bug871161-2.html [test_bug1013316.html] [test_hash_encoded.html] -[test_bug1081037.html] [test_window_open_close.html] tags = openwindow [test_viewport_resize.html] diff --git a/dom/html/test/test_bug1081037.html b/dom/html/test/test_bug1081037.html deleted file mode 100644 index 9d87825802..0000000000 --- a/dom/html/test/test_bug1081037.html +++ /dev/null @@ -1,133 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=1081037 ---> -<head> - <meta charset="utf-8"> - <title>Test for Bug 1081037</title> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - <script type="application/javascript"> - - /** Test for Bug 1081037 **/ - -function shouldThrow(fun, msg, ex, todo) { - try { - fun(); - ok(todo, msg); - } catch (e) { - ok(new RegExp(ex).test(e), msg + " (thrown:" + e + ")") - } -} - -var Foo = document.registerElement('x-foo', { - prototype: {bar: 5} -}); - -Foo.prototype.bar = 6; -var foo = new Foo(); -is(foo.bar, 6, "prototype of the ctor returned from registerElement works"); - -var protoDesc = Object.getOwnPropertyDescriptor(Foo, "prototype"); -is(protoDesc.configurable, false, "proto should be non-configurable"); -is(protoDesc.enumerable, false, "proto should be non-enumerable"); -is(protoDesc.writable, false, "proto should be non-writable"); - -// TODO: FIXME! -shouldThrow(function() { - document.registerElement('x-foo2', { - prototype: Foo.prototype - }); - }, - "if proto is an interface prototype object, registerElement should throw", - "not supported", - /* todo = */ true); - -var nonConfigReadonlyProto = Object.create(HTMLElement.prototype, - { constructor: { configurable: false, writable: false, value: 42 } }); - -shouldThrow(function() { - document.registerElement('x-nonconfig-readonly', { - prototype: nonConfigReadonlyProto - }); - }, - "non-configurable and not-writable constructor property", - "not supported"); - - -// this is not defined in current spec: -var readonlyProto = Object.create(HTMLElement.prototype, - { constructor: { configurable: true, writable: false, value: 42 } }); - -var Readonly = document.registerElement('x-nonconfig-readonly', { - prototype: readonlyProto -}); - -is(Readonly.prototype, readonlyProto, "configurable readonly constructor property"); - -var handler = { - getOwnPropertyDescriptor: function(target, name) { - return name == "constructor" ? undefined : Object.getOwnPropertyDescriptor(target,name); - }, - defineProperty: function(target, name, propertyDescriptor) { - if (name == "constructor") { - throw "spec this"; - } - - return Object.defineProperty(target, name, propertyDescriptor); - }, - has: function(target, name) { - if (name == "constructor") { - return false; - } - return name in target; - } -}; -var proxy = new Proxy({}, handler); - -shouldThrow(function() { - document.registerElement('x-proxymagic', { - prototype: proxy - }); - }, - "proxy magic", - "spec this"); - -var getOwn = 0; -var defineProp = 0; -var handler2 = { - getOwnPropertyDescriptor: function(target, name) { - if (name == "constructor") { - getOwn++; - } - return Object.getOwnPropertyDescriptor(target,name); - }, - defineProperty: function(target, name, propertyDescriptor) { - if (name == "constructor") { - defineProp++; - } - return Object.defineProperty(target, name, propertyDescriptor); - } -}; -var proxy2 = new Proxy({}, handler2); - -document.registerElement('x-proxymagic2', { - prototype: proxy2 -}); - -is(getOwn, 1, "number of getOwnPropertyDescriptor calls from registerElement: " + getOwn); -is(defineProp, 1, "number of defineProperty calls from registerElement: " + defineProp); - - </script> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1081037">Mozilla Bug 1081037</a> -<p id="display"></p> -<div id="content" style="display: none"> - -</div> -<pre id="test"> -</pre> -</body> -</html> diff --git a/dom/indexedDB/IDBFileHandle.cpp b/dom/indexedDB/IDBFileHandle.cpp index 8b88e1722a..93e163c116 100644 --- a/dom/indexedDB/IDBFileHandle.cpp +++ b/dom/indexedDB/IDBFileHandle.cpp @@ -127,12 +127,12 @@ IDBFileHandle::Run() } nsresult -IDBFileHandle::PreHandleEvent(EventChainPreVisitor& aVisitor) +IDBFileHandle::GetEventTargetParent(EventChainPreVisitor& aVisitor) { AssertIsOnOwningThread(); aVisitor.mCanHandle = true; - aVisitor.mParentTarget = mMutableFile; + aVisitor.SetParentTarget(mMutableFile, false); return NS_OK; } diff --git a/dom/indexedDB/IDBFileHandle.h b/dom/indexedDB/IDBFileHandle.h index 574524090d..17206e09c4 100644 --- a/dom/indexedDB/IDBFileHandle.h +++ b/dom/indexedDB/IDBFileHandle.h @@ -117,7 +117,7 @@ public: // nsIDOMEventTarget virtual nsresult - PreHandleEvent(EventChainPreVisitor& aVisitor) override; + GetEventTargetParent(EventChainPreVisitor& aVisitor) override; // WrapperCache virtual JSObject* diff --git a/dom/indexedDB/IDBFileRequest.cpp b/dom/indexedDB/IDBFileRequest.cpp index 066b2b24a1..1401f7c76c 100644 --- a/dom/indexedDB/IDBFileRequest.cpp +++ b/dom/indexedDB/IDBFileRequest.cpp @@ -64,12 +64,12 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(IDBFileRequest, DOMRequest, mFileHandle) nsresult -IDBFileRequest::PreHandleEvent(EventChainPreVisitor& aVisitor) +IDBFileRequest::GetEventTargetParent(EventChainPreVisitor& aVisitor) { AssertIsOnOwningThread(); aVisitor.mCanHandle = true; - aVisitor.mParentTarget = mFileHandle; + aVisitor.SetParentTarget(mFileHandle, false); return NS_OK; } diff --git a/dom/indexedDB/IDBFileRequest.h b/dom/indexedDB/IDBFileRequest.h index 4f4252dc9a..3d8d282fde 100644 --- a/dom/indexedDB/IDBFileRequest.h +++ b/dom/indexedDB/IDBFileRequest.h @@ -58,7 +58,7 @@ public: // nsIDOMEventTarget virtual nsresult - PreHandleEvent(EventChainPreVisitor& aVisitor) override; + GetEventTargetParent(EventChainPreVisitor& aVisitor) override; // nsWrapperCache virtual JSObject* diff --git a/dom/indexedDB/IDBRequest.cpp b/dom/indexedDB/IDBRequest.cpp index e0e318059e..5ce077e33f 100644 --- a/dom/indexedDB/IDBRequest.cpp +++ b/dom/indexedDB/IDBRequest.cpp @@ -449,12 +449,12 @@ NS_IMPL_ADDREF_INHERITED(IDBRequest, IDBWrapperCache) NS_IMPL_RELEASE_INHERITED(IDBRequest, IDBWrapperCache) nsresult -IDBRequest::PreHandleEvent(EventChainPreVisitor& aVisitor) +IDBRequest::GetEventTargetParent(EventChainPreVisitor& aVisitor) { AssertIsOnOwningThread(); aVisitor.mCanHandle = true; - aVisitor.mParentTarget = mTransaction; + aVisitor.SetParentTarget(mTransaction, false); return NS_OK; } diff --git a/dom/indexedDB/IDBRequest.h b/dom/indexedDB/IDBRequest.h index 1c7fca7565..e6cbaab9e8 100644 --- a/dom/indexedDB/IDBRequest.h +++ b/dom/indexedDB/IDBRequest.h @@ -90,7 +90,7 @@ public: // nsIDOMEventTarget virtual nsresult - PreHandleEvent(EventChainPreVisitor& aVisitor) override; + GetEventTargetParent(EventChainPreVisitor& aVisitor) override; void GetSource(Nullable<OwningIDBObjectStoreOrIDBIndexOrIDBCursor>& aSource) const; diff --git a/dom/indexedDB/IDBTransaction.cpp b/dom/indexedDB/IDBTransaction.cpp index a50489898a..0a10e2ca0e 100644 --- a/dom/indexedDB/IDBTransaction.cpp +++ b/dom/indexedDB/IDBTransaction.cpp @@ -996,12 +996,12 @@ IDBTransaction::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) } nsresult -IDBTransaction::PreHandleEvent(EventChainPreVisitor& aVisitor) +IDBTransaction::GetEventTargetParent(EventChainPreVisitor& aVisitor) { AssertIsOnOwningThread(); aVisitor.mCanHandle = true; - aVisitor.mParentTarget = mDatabase; + aVisitor.SetParentTarget(mDatabase, false); return NS_OK; } diff --git a/dom/indexedDB/IDBTransaction.h b/dom/indexedDB/IDBTransaction.h index 1c3e8be991..5a893ca20d 100644 --- a/dom/indexedDB/IDBTransaction.h +++ b/dom/indexedDB/IDBTransaction.h @@ -314,7 +314,7 @@ public: // nsIDOMEventTarget virtual nsresult - PreHandleEvent(EventChainPreVisitor& aVisitor) override; + GetEventTargetParent(EventChainPreVisitor& aVisitor) override; private: IDBTransaction(IDBDatabase* aDatabase, diff --git a/dom/interfaces/events/nsIDOMEventTarget.idl b/dom/interfaces/events/nsIDOMEventTarget.idl index a7e3aae5a4..9e00c598c3 100644 --- a/dom/interfaces/events/nsIDOMEventTarget.idl +++ b/dom/interfaces/events/nsIDOMEventTarget.idl @@ -13,6 +13,7 @@ using mozilla::dom::Nullable; namespace mozilla { +class EventChainVisitor; class EventChainPostVisitor; class EventChainPreVisitor; class EventListenerManager; @@ -224,7 +225,19 @@ interface nsIDOMEventTarget : nsISupports * @note Only EventDispatcher should call this method. */ [noscript, nostdcall] - void PreHandleEvent(in EventChainPreVisitorRef aVisitor); + void GetEventTargetParent(in EventChainPreVisitorRef aVisitor); + + /** + * Called before the capture phase of the event flow and after event target + * chain creation. This is used to handle those stuffs must be executed before + * dispatch event to DOM + */ +%{C++ + virtual nsresult PreHandleEvent(mozilla::EventChainVisitor& aVisitor) + { + return NS_OK; + } +%} /** * If EventChainPreVisitor.mWantsWillHandleEvent is set PR_TRUE, diff --git a/dom/interfaces/range/nsIDOMRange.idl b/dom/interfaces/range/nsIDOMRange.idl index 2dc40ac981..7c74ddaf2f 100644 --- a/dom/interfaces/range/nsIDOMRange.idl +++ b/dom/interfaces/range/nsIDOMRange.idl @@ -16,14 +16,14 @@ interface nsIDOMRange : nsISupports { readonly attribute nsIDOMNode startContainer; - readonly attribute long startOffset; + readonly attribute unsigned long startOffset; readonly attribute nsIDOMNode endContainer; - readonly attribute long endOffset; + readonly attribute unsigned long endOffset; readonly attribute boolean collapsed; readonly attribute nsIDOMNode commonAncestorContainer; - void setStart(in nsIDOMNode refNode, in long offset); - void setEnd(in nsIDOMNode refNode, in long offset); + void setStart(in nsIDOMNode refNode, in unsigned long offset); + void setEnd(in nsIDOMNode refNode, in unsigned long offset); void setStartBefore(in nsIDOMNode refNode); void setStartAfter(in nsIDOMNode refNode); void setEndBefore(in nsIDOMNode refNode); @@ -56,14 +56,14 @@ interface nsIDOMRange : nsISupports // This returns true if parent+offset equals either // of the boundary points or is between them. boolean isPointInRange(in nsIDOMNode parent, - in long offset); + in unsigned long offset); // comparePoint returns // -1 if point is before the start boundary point, // 0 if point is either of the boundary points or between them, // 1 if point is after the end boundary point. // Sort of a strcmp for ranges. - short comparePoint(in nsIDOMNode parent, in long offset); + short comparePoint(in nsIDOMNode parent, in unsigned long offset); /** * Returns whether the range intersects node. diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h index d9988a5969..0040673558 100644 --- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -135,7 +135,7 @@ public: } nsresult - PreHandleEvent(EventChainPreVisitor& aVisitor) override + GetEventTargetParent(EventChainPreVisitor& aVisitor) override { aVisitor.mForceContentDispatch = true; return NS_OK; diff --git a/dom/mathml/nsMathMLElement.cpp b/dom/mathml/nsMathMLElement.cpp index 2be9316824..a74d8168c6 100644 --- a/dom/mathml/nsMathMLElement.cpp +++ b/dom/mathml/nsMathMLElement.cpp @@ -919,12 +919,12 @@ nsMathMLElement::MapMathMLAttributesInto(const nsMappedAttributes* aAttributes, } nsresult -nsMathMLElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +nsMathMLElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { - nsresult rv = Element::PreHandleEvent(aVisitor); + nsresult rv = Element::GetEventTargetParent(aVisitor); NS_ENSURE_SUCCESS(rv, rv); - return PreHandleEventForLinks(aVisitor); + return GetEventTargetParentForLinks(aVisitor); } nsresult @@ -1085,50 +1085,27 @@ nsMathMLElement::GetHrefURI() const } nsresult -nsMathMLElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) +nsMathMLElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { - nsresult rv = nsMathMLElementBase::SetAttr(aNameSpaceID, aName, aPrefix, - aValue, aNotify); - - // The ordering of the parent class's SetAttr call and Link::ResetLinkState - // is important here! The attribute is not set until SetAttr returns, and - // we will need the updated attribute value because notifying the document + // It is important that this be done after the attribute is set/unset. + // We will need the updated attribute value because notifying the document // that content states have changed will call IntrinsicState, which will try // to get updated information about the visitedness from Link. if (aName == nsGkAtoms::href && (aNameSpaceID == kNameSpaceID_None || aNameSpaceID == kNameSpaceID_XLink)) { - if (aNameSpaceID == kNameSpaceID_XLink) { + if (aValue && aNameSpaceID == kNameSpaceID_XLink) { WarnDeprecated(u"xlink:href", u"href", OwnerDoc()); } - Link::ResetLinkState(!!aNotify, true); - } - - return rv; -} - -nsresult -nsMathMLElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttr, - bool aNotify) -{ - nsresult rv = nsMathMLElementBase::UnsetAttr(aNameSpaceID, aAttr, aNotify); - - // The ordering of the parent class's UnsetAttr call and Link::ResetLinkState - // is important here! The attribute is not unset until UnsetAttr returns, and - // we will need the updated attribute value because notifying the document - // that content states have changed will call IntrinsicState, which will try - // to get updated information about the visitedness from Link. - if (aAttr == nsGkAtoms::href && - (aNameSpaceID == kNameSpaceID_None || - aNameSpaceID == kNameSpaceID_XLink)) { - // Note: just because we removed a single href attr doesn't mean there's no href, - // since there are 2 possible namespaces. - Link::ResetLinkState(!!aNotify, Link::ElementHasHref()); + // Note: When unsetting href, there may still be another href since there + // are 2 possible namespaces. + Link::ResetLinkState(aNotify, aValue || Link::ElementHasHref()); } - return rv; + return nsMathMLElementBase::AfterSetAttr(aNameSpaceID, aName, aValue, + aOldValue, aNotify); } JSObject* diff --git a/dom/mathml/nsMathMLElement.h b/dom/mathml/nsMathMLElement.h index 47ed8b1656..0fdaf021f5 100644 --- a/dom/mathml/nsMathMLElement.h +++ b/dom/mathml/nsMathMLElement.h @@ -74,7 +74,7 @@ public: static void MapMathMLAttributesInto(const nsMappedAttributes* aAttributes, nsRuleData* aRuleData); - virtual nsresult PreHandleEvent( + virtual nsresult GetEventTargetParent( mozilla::EventChainPreVisitor& aVisitor) override; virtual nsresult PostHandleEvent( mozilla::EventChainPostVisitor& aVisitor) override; @@ -93,16 +93,6 @@ public: virtual bool IsLink(nsIURI** aURI) const override; virtual void GetLinkTarget(nsAString& aTarget) override; virtual already_AddRefed<nsIURI> GetHrefURI() const override; - nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAString& aValue, bool aNotify) - { - return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify); - } - virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) override; - virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, - bool aNotify) override; virtual nsIDOMNode* AsDOMNode() override { return this; } @@ -111,6 +101,11 @@ protected: virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override; + virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; + private: bool mIncrementScriptLevel; }; diff --git a/dom/svg/SVGAElement.cpp b/dom/svg/SVGAElement.cpp index a69c60ee4e..95c3d69794 100644 --- a/dom/svg/SVGAElement.cpp +++ b/dom/svg/SVGAElement.cpp @@ -84,12 +84,12 @@ SVGAElement::Href() // nsINode methods nsresult -SVGAElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +SVGAElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { - nsresult rv = Element::PreHandleEvent(aVisitor); + nsresult rv = Element::GetEventTargetParent(aVisitor); NS_ENSURE_SUCCESS(rv, rv); - return PreHandleEventForLinks(aVisitor); + return GetEventTargetParentForLinks(aVisitor); } nsresult diff --git a/dom/svg/SVGAElement.h b/dom/svg/SVGAElement.h index 4f7df9e507..776c96f53a 100644 --- a/dom/svg/SVGAElement.h +++ b/dom/svg/SVGAElement.h @@ -37,7 +37,8 @@ public: NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SVGAElement, SVGAElementBase) // nsINode interface methods - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; virtual nsresult PostHandleEvent( EventChainPostVisitor& aVisitor) override; virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; diff --git a/dom/svg/SVGAnimationElement.cpp b/dom/svg/SVGAnimationElement.cpp index d6550c96ee..90c26657fd 100644 --- a/dom/svg/SVGAnimationElement.cpp +++ b/dom/svg/SVGAnimationElement.cpp @@ -299,11 +299,12 @@ SVGAnimationElement::ParseAttribute(int32_t aNamespaceID, nsresult SVGAnimationElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { nsresult rv = SVGAnimationElementBase::AfterSetAttr(aNamespaceID, aName, aValue, - aNotify); + aOldValue, aNotify); if (SVGTests::IsConditionalProcessingAttribute(aName)) { bool isDisabled = !SVGTests::PassesConditionalProcessingTests(); diff --git a/dom/svg/SVGAnimationElement.h b/dom/svg/SVGAnimationElement.h index 9bcbdf0c22..eb602c4a88 100644 --- a/dom/svg/SVGAnimationElement.h +++ b/dom/svg/SVGAnimationElement.h @@ -58,7 +58,9 @@ public: const nsAString& aValue, nsAttrValue& aResult) override; virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) override; + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; const nsAttrValue* GetAnimAttr(nsIAtom* aName) const; bool GetAnimAttr(nsIAtom* aAttName, nsAString& aResult) const; diff --git a/dom/svg/SVGFEImageElement.cpp b/dom/svg/SVGFEImageElement.cpp index f235d5ccbf..5492f291d7 100644 --- a/dom/svg/SVGFEImageElement.cpp +++ b/dom/svg/SVGFEImageElement.cpp @@ -119,7 +119,8 @@ SVGFEImageElement::IsAttributeMapped(const nsIAtom* name) const nsresult SVGFEImageElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { if (aName == nsGkAtoms::href && (aNamespaceID == kNameSpaceID_XLink || @@ -139,7 +140,7 @@ SVGFEImageElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, } return SVGFEImageElementBase::AfterSetAttr(aNamespaceID, aName, - aValue, aNotify); + aValue, aOldValue, aNotify); } void diff --git a/dom/svg/SVGFEImageElement.h b/dom/svg/SVGFEImageElement.h index edf6f065db..a5b76066a9 100644 --- a/dom/svg/SVGFEImageElement.h +++ b/dom/svg/SVGFEImageElement.h @@ -58,7 +58,9 @@ public: virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) override; + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsIContent* aBindingParent, bool aCompileEventHandlers) override; diff --git a/dom/svg/SVGImageElement.cpp b/dom/svg/SVGImageElement.cpp index 524485dee8..e240ee4160 100644 --- a/dom/svg/SVGImageElement.cpp +++ b/dom/svg/SVGImageElement.cpp @@ -150,7 +150,8 @@ SVGImageElement::AsyncEventRunning(AsyncEventDispatcher* aEvent) nsresult SVGImageElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { if (aName == nsGkAtoms::href && (aNamespaceID == kNameSpaceID_None || @@ -169,7 +170,7 @@ SVGImageElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, } } return SVGImageElementBase::AfterSetAttr(aNamespaceID, aName, - aValue, aNotify); + aValue, aOldValue, aNotify); } void diff --git a/dom/svg/SVGImageElement.h b/dom/svg/SVGImageElement.h index 85730212b4..e8ea954993 100644 --- a/dom/svg/SVGImageElement.h +++ b/dom/svg/SVGImageElement.h @@ -46,7 +46,9 @@ public: // nsIContent interface virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) override; + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsIContent* aBindingParent, bool aCompileEventHandlers) override; diff --git a/dom/svg/SVGSVGElement.cpp b/dom/svg/SVGSVGElement.cpp index 90c3c3fff3..bb3aaccd2a 100644 --- a/dom/svg/SVGSVGElement.cpp +++ b/dom/svg/SVGSVGElement.cpp @@ -589,7 +589,7 @@ SVGSVGElement::IsAttributeMapped(const nsIAtom* name) const // nsIContent methods: nsresult -SVGSVGElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +SVGSVGElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { if (aVisitor.mEvent->mMessage == eSVGLoad) { if (mTimedDocumentRoot) { @@ -600,7 +600,7 @@ SVGSVGElement::PreHandleEvent(EventChainPreVisitor& aVisitor) AnimationNeedsResample(); } } - return SVGSVGElementBase::PreHandleEvent(aVisitor); + return SVGSVGElementBase::GetEventTargetParent(aVisitor); } bool diff --git a/dom/svg/SVGSVGElement.h b/dom/svg/SVGSVGElement.h index da08ad770c..0145ae8fac 100644 --- a/dom/svg/SVGSVGElement.h +++ b/dom/svg/SVGSVGElement.h @@ -152,7 +152,8 @@ public: // nsIContent interface NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; virtual bool IsEventAttributeName(nsIAtom* aName) override; diff --git a/dom/svg/SVGScriptElement.cpp b/dom/svg/SVGScriptElement.cpp index ffc049c212..0cc827b9fd 100644 --- a/dom/svg/SVGScriptElement.cpp +++ b/dom/svg/SVGScriptElement.cpp @@ -211,7 +211,8 @@ SVGScriptElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsresult SVGScriptElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { if ((aNamespaceID == kNameSpaceID_XLink || aNamespaceID == kNameSpaceID_None) && @@ -219,7 +220,7 @@ SVGScriptElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, MaybeProcessScript(); } return SVGScriptElementBase::AfterSetAttr(aNamespaceID, aName, - aValue, aNotify); + aValue, aOldValue, aNotify); } bool diff --git a/dom/svg/SVGScriptElement.h b/dom/svg/SVGScriptElement.h index 620a1bcde0..d3a233b728 100644 --- a/dom/svg/SVGScriptElement.h +++ b/dom/svg/SVGScriptElement.h @@ -55,7 +55,9 @@ public: nsIContent* aBindingParent, bool aCompileEventHandlers) override; virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) override; + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; virtual bool ParseAttribute(int32_t aNamespaceID, nsIAtom* aAttribute, const nsAString& aValue, diff --git a/dom/svg/nsSVGClass.cpp b/dom/svg/nsSVGClass.cpp index 6b64c3be7f..c5a825be43 100644 --- a/dom/svg/nsSVGClass.cpp +++ b/dom/svg/nsSVGClass.cpp @@ -70,7 +70,7 @@ nsSVGClass::SetBaseValue(const nsAString& aValue, { NS_ASSERTION(aSVGElement, "Null element passed to SetBaseValue"); - aSVGElement->SetFlags(NODE_MAY_HAVE_CLASS); + aSVGElement->SetMayHaveClass(); if (aDoSetAttr) { aSVGElement->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, aValue, true); } @@ -106,7 +106,7 @@ nsSVGClass::SetAnimValue(const nsAString& aValue, nsSVGElement *aSVGElement) mAnimVal = new nsString(); } *mAnimVal = aValue; - aSVGElement->SetFlags(NODE_MAY_HAVE_CLASS); + aSVGElement->SetMayHaveClass(); aSVGElement->DidAnimateClass(); } diff --git a/dom/svg/nsSVGElement.cpp b/dom/svg/nsSVGElement.cpp index ce849acf0f..25d6d944d9 100644 --- a/dom/svg/nsSVGElement.cpp +++ b/dom/svg/nsSVGElement.cpp @@ -18,6 +18,7 @@ #include "nsIDOMMutationEvent.h" #include "nsSVGPathGeometryElement.h" #include "mozilla/InternalMutationEvent.h" +#include "mozAutoDocUpdate.h" #include "nsError.h" #include "nsIPresShell.h" #include "nsGkAtoms.h" @@ -272,7 +273,9 @@ nsSVGElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, ParseStyleAttribute(stringValue, attrValue, true); // Don't bother going through SetInlineStyleDeclaration; we don't // want to fire off mutation events or document notifications anyway - rv = mAttrsAndChildren.SetAndSwapAttr(nsGkAtoms::style, attrValue); + bool oldValueSet; + rv = mAttrsAndChildren.SetAndSwapAttr(nsGkAtoms::style, attrValue, + &oldValueSet); NS_ENSURE_SUCCESS(rv, rv); } @@ -281,7 +284,8 @@ nsSVGElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsresult nsSVGElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { // We don't currently use nsMappedAttributes within SVG. If this changes, we // need to be very careful because some nsAttrValues used by SVG point to @@ -309,7 +313,8 @@ nsSVGElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, NS_ENSURE_SUCCESS(rv, rv); } - return nsSVGElementBase::AfterSetAttr(aNamespaceID, aName, aValue, aNotify); + return nsSVGElementBase::AfterSetAttr(aNamespaceID, aName, aValue, aOldValue, + aNotify); } bool @@ -1441,27 +1446,16 @@ nsSVGElement::WillChangeValue(nsIAtom* aName) // We need an empty attr value: // a) to pass to BeforeSetAttr when GetParsedAttr returns nullptr // b) to store the old value in the case we have mutation listeners - // We can use the same value for both purposes since (a) happens before (b). + // + // We can use the same value for both purposes, because if GetParsedAttr + // returns non-null its return value is what will get passed to BeforeSetAttr, + // not matter what our mutation listener situation is. + // // Also, we should be careful to always return this value to benefit from // return value optimization. nsAttrValue emptyOrOldAttrValue; const nsAttrValue* attrValue = GetParsedAttr(aName); - // This is not strictly correct--the attribute value parameter for - // BeforeSetAttr should reflect the value that *will* be set but that implies - // allocating, e.g. an extra nsSVGLength2, and isn't necessary at the moment - // since no SVG elements overload BeforeSetAttr. For now we just pass the - // current value. - nsAttrValueOrString attrStringOrValue(attrValue ? *attrValue - : emptyOrOldAttrValue); - DebugOnly<nsresult> rv = - BeforeSetAttr(kNameSpaceID_None, aName, &attrStringOrValue, - kNotifyDocumentObservers); - // SVG elements aren't expected to overload BeforeSetAttr in such a way that - // it may fail. So long as this is the case we don't need to check and pass on - // the return value which simplifies the calling code significantly. - MOZ_ASSERT(NS_SUCCEEDED(rv), "Unexpected failure from BeforeSetAttr"); - // We only need to set the old value if we have listeners since otherwise it // isn't used. if (attrValue && @@ -1477,6 +1471,21 @@ nsSVGElement::WillChangeValue(nsIAtom* aName) nsNodeUtils::AttributeWillChange(this, kNameSpaceID_None, aName, modType, nullptr); + // This is not strictly correct--the attribute value parameter for + // BeforeSetAttr should reflect the value that *will* be set but that implies + // allocating, e.g. an extra nsSVGLength2, and isn't necessary at the moment + // since no SVG elements overload BeforeSetAttr. For now we just pass the + // current value. + nsAttrValueOrString attrStringOrValue(attrValue ? *attrValue + : emptyOrOldAttrValue); + DebugOnly<nsresult> rv = + BeforeSetAttr(kNameSpaceID_None, aName, &attrStringOrValue, + kNotifyDocumentObservers); + // SVG elements aren't expected to overload BeforeSetAttr in such a way that + // it may fail. So long as this is the case we don't need to check and pass on + // the return value which simplifies the calling code significantly. + MOZ_ASSERT(NS_SUCCEEDED(rv), "Unexpected failure from BeforeSetAttr"); + return emptyOrOldAttrValue; } @@ -1505,9 +1514,17 @@ nsSVGElement::DidChangeValue(nsIAtom* aName, uint8_t modType = HasAttr(kNameSpaceID_None, aName) ? static_cast<uint8_t>(nsIDOMMutationEvent::MODIFICATION) : static_cast<uint8_t>(nsIDOMMutationEvent::ADDITION); - SetAttrAndNotify(kNameSpaceID_None, aName, nullptr, aEmptyOrOldValue, + + nsIDocument* document = GetComposedDoc(); + mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, + kNotifyDocumentObservers); + // XXX Really, the fourth argument to SetAttrAndNotify should be null if + // aEmptyOrOldValue does not represent the actual previous value of the + // attribute, but currently SVG elements do not even use the old attribute + // value in |AfterSetAttr|, so this should be ok. + SetAttrAndNotify(kNameSpaceID_None, aName, nullptr, &aEmptyOrOldValue, aNewValue, modType, hasListeners, kNotifyDocumentObservers, - kCallAfterSetAttr); + kCallAfterSetAttr, document, updateBatch); } void @@ -1527,7 +1544,8 @@ nsSVGElement::MaybeSerializeAttrBeforeRemoval(nsIAtom* aName, bool aNotify) nsAutoString serializedValue; attrValue->ToString(serializedValue); nsAttrValue oldAttrValue(serializedValue); - mAttrsAndChildren.SetAndSwapAttr(aName, oldAttrValue); + bool oldValueSet; + mAttrsAndChildren.SetAndSwapAttr(aName, oldAttrValue, &oldValueSet); } /* static */ diff --git a/dom/svg/nsSVGElement.h b/dom/svg/nsSVGElement.h index 257ed7a2e6..4e8c7a9384 100644 --- a/dom/svg/nsSVGElement.h +++ b/dom/svg/nsSVGElement.h @@ -329,14 +329,16 @@ protected: // BeforeSetAttr since it would involve allocating extra SVG value types. // See the comment in nsSVGElement::WillChangeValue. virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, - nsAttrValueOrString* aValue, + const nsAttrValueOrString* aValue, bool aNotify) override final { return nsSVGElementBase::BeforeSetAttr(aNamespaceID, aName, aValue, aNotify); } #endif // DEBUG virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) override; + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; virtual bool ParseAttribute(int32_t aNamespaceID, nsIAtom* aAttribute, const nsAString& aValue, nsAttrValue& aResult) override; static nsresult ReportAttributeParseFailure(nsIDocument* aDocument, diff --git a/dom/svg/nsSVGPathGeometryElement.cpp b/dom/svg/nsSVGPathGeometryElement.cpp index ea2e1c7c47..9b9e96c8f5 100644 --- a/dom/svg/nsSVGPathGeometryElement.cpp +++ b/dom/svg/nsSVGPathGeometryElement.cpp @@ -26,7 +26,8 @@ nsSVGPathGeometryElement::nsSVGPathGeometryElement(already_AddRefed<mozilla::dom nsresult nsSVGPathGeometryElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { if (mCachedPath && aNamespaceID == kNameSpaceID_None && @@ -34,7 +35,7 @@ nsSVGPathGeometryElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, mCachedPath = nullptr; } return nsSVGPathGeometryElementBase::AfterSetAttr(aNamespaceID, aName, - aValue, aNotify); + aValue, aOldValue, aNotify); } bool diff --git a/dom/svg/nsSVGPathGeometryElement.h b/dom/svg/nsSVGPathGeometryElement.h index 1091fa0dcd..29e90c4e05 100644 --- a/dom/svg/nsSVGPathGeometryElement.h +++ b/dom/svg/nsSVGPathGeometryElement.h @@ -45,7 +45,9 @@ public: explicit nsSVGPathGeometryElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo); virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) override; + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; /** * Causes this element to discard any Path object that GetOrBuildPath may diff --git a/dom/tests/mochitest/webcomponents/mochitest.ini b/dom/tests/mochitest/webcomponents/mochitest.ini index f5d0f84ea9..84322d21d0 100644 --- a/dom/tests/mochitest/webcomponents/mochitest.ini +++ b/dom/tests/mochitest/webcomponents/mochitest.ini @@ -17,18 +17,15 @@ support-files = htmlconstructor_builtin_tests.js [test_custom_element_in_shadow.html] skip-if = true || stylo # disabled - See bug 1390396 and 1293844 -[test_custom_element_register_invalid_callbacks.html] [test_custom_element_throw_on_dynamic_markup_insertion.html] [test_custom_element_get.html] [test_custom_element_when_defined.html] [test_custom_element_uncatchable_exception.html] skip-if = !debug # TestFunctions only applied in debug builds -[test_nested_content_element.html] -[test_dest_insertion_points.html] -[test_dest_insertion_points_shadow.html] -[test_fallback_dest_insertion_points.html] +[test_custom_element_define.html] +[test_custom_element_define_parser.html] +[test_custom_element_template.html] [test_detached_style.html] -[test_dynamic_content_element_matching.html] [test_document_adoptnode.html] [test_document_importnode.html] [test_document_register.html] @@ -38,19 +35,13 @@ skip-if = true # disabled - See bug 1390396 [test_document_register_stack.html] skip-if = true # disabled - See bug 1390396 [test_document_shared_registry.html] -[test_event_dispatch.html] [test_event_retarget.html] [test_event_stopping.html] [test_template.html] [test_template_xhtml.html] -[test_template_custom_elements.html] [test_shadowroot.html] [test_shadowroot_inert_element.html] -[test_shadowroot_host.html] [test_shadowroot_style.html] -[test_shadowroot_style_multiple_shadow.html] [test_shadowroot_style_order.html] -[test_shadowroot_youngershadowroot.html] [test_style_fallback_content.html] -[test_unresolved_pseudo_class.html] [test_link_prefetch.html] diff --git a/dom/tests/mochitest/webcomponents/test_bug1276240.html b/dom/tests/mochitest/webcomponents/test_bug1276240.html index ded8d82763..33e532a6a7 100644 --- a/dom/tests/mochitest/webcomponents/test_bug1276240.html +++ b/dom/tests/mochitest/webcomponents/test_bug1276240.html @@ -47,7 +47,7 @@ test(); // test with webcomponents disabled SimpleTest.waitForExplicitFinish(); SpecialPowers.pushPrefEnv( - { 'set': [["dom.webcomponents.customelements.enabled", false]]}, runTest); + { 'set': [["dom.webcomponents.enabled", false]]}, runTest); </script> </pre> diff --git a/dom/tests/mochitest/webcomponents/test_custom_element_callback_innerhtml.html b/dom/tests/mochitest/webcomponents/test_custom_element_callback_innerhtml.html index bb5008538a..22d9571178 100644 --- a/dom/tests/mochitest/webcomponents/test_custom_element_callback_innerhtml.html +++ b/dom/tests/mochitest/webcomponents/test_custom_element_callback_innerhtml.html @@ -18,23 +18,19 @@ SimpleTest.waitForExplicitFinish(); var connectedCallbackCount = 0; -var p = Object.create(HTMLElement.prototype); - -p.createdCallback = function() { - ok(true, "createdCallback called."); -}; - -p.connectedCallback = function() { - ok(true, "connectedCallback should be called when the parser creates an element in the document."); - connectedCallbackCount++; - // connectedCallback should be called twice, once for the element created for innerHTML and - // once for the element created in this document. - if (connectedCallbackCount == 2) { - SimpleTest.finish(); +class Foo extends HTMLElement { + connectedCallback() { + ok(true, "connectedCallback should be called when the parser creates an element in the document."); + connectedCallbackCount++; + // connectedCallback should be called twice, once for the element created for innerHTML and + // once for the element created in this document. + if (connectedCallbackCount == 2) { + SimpleTest.finish(); + } } -} +}; -document.registerElement("x-foo", { prototype: p }); +customElements.define("x-foo", Foo); var container = document.getElementById("container"); container.innerHTML = '<x-foo></x-foo>'; diff --git a/dom/tests/mochitest/webcomponents/test_custom_element_register_invalid_callbacks.html b/dom/tests/mochitest/webcomponents/test_custom_element_register_invalid_callbacks.html deleted file mode 100644 index 572579ba8d..0000000000 --- a/dom/tests/mochitest/webcomponents/test_custom_element_register_invalid_callbacks.html +++ /dev/null @@ -1,69 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=1275835 ---> -<head> - <title>Test registering invalid lifecycle callbacks for custom elements.</title> - <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1275835">Bug 1275835</a> -<iframe id="iframe"></iframe> -<script> - -// Use window from iframe to isolate the test. -const testWindow = iframe.contentDocument.defaultView; - -// This is for backward compatibility. -// We should do the same checks for the callbacks from v0 spec. -[ - 'attributeChangedCallback', -].forEach(callback => { - var c = class {}; - var p = c.prototype; - - // Test getting callback throws exception. - Object.defineProperty(p, callback, { - get() { - const e = new Error('this is rethrown'); - e.name = 'rethrown'; - throw e; - } - }); - - SimpleTest.doesThrow(() => { - testWindow.document.registerElement(`test-register-${callback}-rethrown`, - { prototype: p }); - }, `document.registerElement should throw exception if prototype.${callback} throws`); - - SimpleTest.doesThrow(() => { - testWindow.customElements.define(`test-define-${callback}-rethrown`, c); - }, `customElements.define should throw exception if constructor.${callback} throws`); - - // Test callback is not callable. - [ - { name: 'null', value: null }, - { name: 'object', value: {} }, - { name: 'integer', value: 1 }, - ].forEach(data => { - var c = class {}; - var p = c.prototype; - - p[callback] = data.value; - - SimpleTest.doesThrow(() => { - testWindow.document.registerElement(`test-register-${callback}-${data.name}`, - { prototype: p }); - }, `document.registerElement should throw exception if ${callback} is ${data.name}`); - - SimpleTest.doesThrow(() => { - testWindow.customElements.define(`test-define-${callback}-${data.name}`, c); - }, `customElements.define should throw exception if ${callback} is ${data.name}`); - }); -}); - -</script> -</body> -</html> diff --git a/dom/tests/mochitest/webcomponents/test_dest_insertion_points.html b/dom/tests/mochitest/webcomponents/test_dest_insertion_points.html deleted file mode 100644 index 2d4a92ed28..0000000000 --- a/dom/tests/mochitest/webcomponents/test_dest_insertion_points.html +++ /dev/null @@ -1,73 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=999999 ---> -<head> - <meta charset="utf-8"> - <title>Test for Bug 999999</title> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=999999">Mozilla Bug 999999</a> -<p id="display"></p> -<div id="content"> -<div id="shadowhost"> -</div> -</div> -<pre id="test"> -</pre> -<script type="application/javascript"> - -/** Test for Bug 999999 **/ -var host = document.getElementById("shadowhost"); - -// Test destination insertion points of node distributed to content element. -var shadowRoot = host.createShadowRoot(); -shadowRoot.innerHTML = '<div id="innerhost"><content id="innercontent" select=".red"></content></div>'; -var innerContent = shadowRoot.getElementById("innercontent"); - -var span = document.createElement("span"); -span.setAttribute("class", "red blue"); -is(host.getDestinationInsertionPoints().length, 0, "Destination insertion points should be empty when not being distributed."); - -host.appendChild(span); - -is(span.getDestinationInsertionPoints().length, 1, "Destination insertion points should only contain a single content insertion point."); -is(span.getDestinationInsertionPoints()[0], innerContent, "Content element should contain destination insertion point."); - -// Test destination insertion points of redistributed node. -var innerHost = shadowRoot.getElementById("innerhost"); -var innerShadowRoot = innerHost.createShadowRoot(); -innerShadowRoot.innerHTML = '<content id="innerinnercontent" select=".blue"></content>'; - -var innerInnerContent = innerShadowRoot.getElementById("innerinnercontent"); - -is(span.getDestinationInsertionPoints().length, 2, "Redistributed node should have 2 destination insertion points."); -is(span.getDestinationInsertionPoints()[1], innerInnerContent, "Nested content insertion point should be in list of destination insertion points."); - -// Test destination insertion points after removing reprojection onto second content element. -span.setAttribute("class", "red"); -is(span.getDestinationInsertionPoints().length, 1, "Destination insertion points should only contain 1 insertion point after removing reprojection."); -is(span.getDestinationInsertionPoints()[0], innerContent, "First content element should be only insertion point after removing reprojection."); - -// Test destination insertion points after removing the projected content from the host. -host.removeChild(span); -is(span.getDestinationInsertionPoints().length, 0, "Destination insertion points should be empty after being removed from the shadow host."); - -// Test destination insertion points of distributed content after removing insertion point. -var div = document.createElement("div"); -div.setAttribute("class", "red blue"); -host.appendChild(div); - -is(div.getDestinationInsertionPoints().length, 2, "Div should be distributed into 2 insertion points."); - -innerShadowRoot.removeChild(innerInnerContent); - -is(div.getDestinationInsertionPoints().length, 1, "Div should be distributed into insertion point in one ShadowRoot."); -is(div.getDestinationInsertionPoints()[0], innerContent, "Destination insertion points should only contain content insertion point in first ShadowRoot."); - -</script> -</body> -</html> diff --git a/dom/tests/mochitest/webcomponents/test_dest_insertion_points_shadow.html b/dom/tests/mochitest/webcomponents/test_dest_insertion_points_shadow.html deleted file mode 100644 index 75286463e6..0000000000 --- a/dom/tests/mochitest/webcomponents/test_dest_insertion_points_shadow.html +++ /dev/null @@ -1,68 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=999999 ---> -<head> - <meta charset="utf-8"> - <title>Test for Bug 999999</title> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=999999">Mozilla Bug 999999</a> -<p id="display"></p> -<div id="content"> -<div id="shadowhost"></div> -</div> -<pre id="test"> -</pre> -<script type="application/javascript"> - -/** Test for Bug 999999 **/ -var host = document.getElementById("shadowhost"); - -// Test destination insertion points of node distributed to shadow element. -var olderShadowRoot = host.createShadowRoot(); -var youngerShadowRoot = host.createShadowRoot(); - -var shadowElem = document.createElement("shadow"); -youngerShadowRoot.appendChild(shadowElem); - -var span = document.createElement("span"); -olderShadowRoot.appendChild(span); - -is(span.getDestinationInsertionPoints().length, 1, "Child of ShadowRoot should be distributed to shadow insertion point."); -is(span.getDestinationInsertionPoints()[0], shadowElem, "Shadow element should be in destination insertion point list."); - -// Test destination insertion points of node removed from tree. -olderShadowRoot.removeChild(span); -is(span.getDestinationInsertionPoints().length, 0, "Node removed from tree should no longer be distributed."); - -// Test destination insertion points of fallback content being reprojected into a shadow element. -var content = document.createElement("content"); -var fallback = document.createElement("span"); - -content.appendChild(fallback); -olderShadowRoot.appendChild(content); - -is(fallback.getDestinationInsertionPoints().length, 2, "The fallback content should have 2 destination insertion points, the parent content and the shadow element to which it is reprojected."); -is(fallback.getDestinationInsertionPoints()[0], content, "First destination of the fallback content should be the parent content element."); -is(fallback.getDestinationInsertionPoints()[1], shadowElem, "Second destination of the fallback content should be the shadow element to which the element is reprojected."); - -// Test destination insertion points of fallback content being removed from tree. -content.removeChild(fallback); -is(fallback.getDestinationInsertionPoints().length, 0, "The content should no longer be distributed to any nodes because it is no longer fallback content."); - -// Test destination insertion points of distributed content after removing shadow insertion point. -var div = document.createElement("div"); -olderShadowRoot.appendChild(div); -is(div.getDestinationInsertionPoints().length, 1, "Children in older shadow root should be distributed to shadow insertion point."); -is(div.getDestinationInsertionPoints()[0], shadowElem, "Destination insertion point should include shadow element."); - -youngerShadowRoot.removeChild(shadowElem); -is(div.getDestinationInsertionPoints().length, 0, "Destination insertion points should be empty after removing shadow element."); - -</script> -</body> -</html> diff --git a/dom/tests/mochitest/webcomponents/test_document_register_parser.html b/dom/tests/mochitest/webcomponents/test_document_register_parser.html deleted file mode 100644 index a4fb63139f..0000000000 --- a/dom/tests/mochitest/webcomponents/test_document_register_parser.html +++ /dev/null @@ -1,47 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=783129 ---> -<head> - <title>Test for document.registerElement for elements created by the parser</title> - <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> -<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> -<script> - -var extendedButtonProto = Object.create(HTMLButtonElement.prototype); -var buttonCallbackCalled = false; -extendedButtonProto.connectedCallback = function() { - is(buttonCallbackCalled, false, "created callback for x-button should only be called once."); - is(this.tagName, "BUTTON", "Only the <button> element should be upgraded."); - buttonCallbackCalled = true; -}; - -document.registerElement("x-button", { prototype: extendedButtonProto, extends: "button" }); - -var divProto = Object.create(HTMLDivElement.prototype); -var divCallbackCalled = false; -divProto.connectedCallback = function() { - is(divCallbackCalled, false, "created callback for x-div should only be called once."); - is(buttonCallbackCalled, true, "crated callback should be called for x-button before x-div."); - is(this.tagName, "X-DIV", "Only the <x-div> element should be upgraded."); - divCallbackCalled = true; - SimpleTest.finish(); -}; - -document.registerElement("x-div", { prototype: divProto }); - -SimpleTest.waitForExplicitFinish(); -</script> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=783129">Bug 783129</a> -<button is="x-button"></button><!-- should be upgraded --> -<x-button></x-button><!-- should not be upgraded --> -<span is="x-button"></span><!-- should not be upgraded --> -<div is="x-div"></div><!-- should not be upgraded --> -<x-div></x-div><!-- should be upgraded --> -<script> -</script> -</body> -</html> diff --git a/dom/tests/mochitest/webcomponents/test_document_shared_registry.html b/dom/tests/mochitest/webcomponents/test_document_shared_registry.html deleted file mode 100644 index db72e1e6ca..0000000000 --- a/dom/tests/mochitest/webcomponents/test_document_shared_registry.html +++ /dev/null @@ -1,46 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=783129 ---> -<head> - <title>Test shared registry for associated HTML documents.</title> - <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> -</head> -<body> -<div id="container"></div> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=783129">Bug 783129</a> -<script> -var container = document.getElementById("container"); - -function registerNoRegistryDoc() { - var assocDoc = document.implementation.createDocument(null, "html"); - try { - assocDoc.registerElement("x-dummy", { prototype: Object.create(HTMLElement.prototype) }); - ok(false, "Registring element in document without registry should throw."); - } catch (ex) { - ok(true, "Registring element in document without registry should throw."); - } - - runNextTest(); -} - -function runNextTest() { - if (testFunctions.length > 0) { - var nextTestFunction = testFunctions.shift(); - nextTestFunction(); - } -} - -var testFunctions = [ - registerNoRegistryDoc, - SimpleTest.finish -]; - -SimpleTest.waitForExplicitFinish(); - -runNextTest(); -</script> -</body> -</html> diff --git a/dom/tests/mochitest/webcomponents/test_dynamic_content_element_matching.html b/dom/tests/mochitest/webcomponents/test_dynamic_content_element_matching.html deleted file mode 100644 index c9af76610c..0000000000 --- a/dom/tests/mochitest/webcomponents/test_dynamic_content_element_matching.html +++ /dev/null @@ -1,50 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=806506 ---> -<head> - <title>Test for dynamic changes to content matching content elements</title> - <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> -</head> -<body> -<div class="tall" id="bodydiv"></div> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a> -<script> -// Create ShadowRoot. -var elem = document.createElement("div"); -var root = elem.createShadowRoot(); - -var redInsertionPoint = document.createElement("content"); -redInsertionPoint.select = "*[data-color=red]"; - -var blueInsertionPoint = document.createElement("content"); -blueInsertionPoint.select = "*[data-color=blue]"; - -root.appendChild(redInsertionPoint); -root.appendChild(blueInsertionPoint); - -is(blueInsertionPoint.getDistributedNodes().length, 0, "Blue insertion point should have no distributed nodes."); -is(redInsertionPoint.getDistributedNodes().length, 0, "Red insertion point should have no distrubted nodes."); - -var matchElement = document.createElement("div"); -matchElement.setAttribute("data-color", "red"); -elem.appendChild(matchElement); - -is(blueInsertionPoint.getDistributedNodes().length, 0, "Blue insertion point should have no distributed nodes."); -is(redInsertionPoint.getDistributedNodes().length, 1, "Red insertion point should match recently inserted div."); - -matchElement.setAttribute("data-color", "blue"); -is(blueInsertionPoint.getDistributedNodes().length, 1, "Blue insertion point should match element after changing attribute value."); -is(redInsertionPoint.getDistributedNodes().length, 0, "Red insertion point should not match element after changing attribute value."); - -matchElement.removeAttribute("data-color"); - -is(blueInsertionPoint.getDistributedNodes().length, 0, "Blue insertion point should have no distributed nodes after removing the matching attribute."); -is(redInsertionPoint.getDistributedNodes().length, 0, "Red insertion point should have no distrubted nodes after removing the matching attribute."); - -</script> -</body> -</html> - diff --git a/dom/tests/mochitest/webcomponents/test_event_dispatch.html b/dom/tests/mochitest/webcomponents/test_event_dispatch.html deleted file mode 100644 index c73bfb214e..0000000000 --- a/dom/tests/mochitest/webcomponents/test_event_dispatch.html +++ /dev/null @@ -1,458 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=887541 ---> -<head> - <title>Test for event model in web components</title> - <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=887541">Bug 887541</a> -<script> - -var els = SpecialPowers.Cc["@mozilla.org/eventlistenerservice;1"] - .getService(SpecialPowers.Ci.nsIEventListenerService); - -function eventListener(e) { - eventChain.push(this); -} - -function isEventChain(actual, expected, msg) { - is(actual.length, expected.length, msg); - for (var i = 0; i < expected.length; i++) { - is(actual[i], expected[i], msg + " at " + i); - } - - // Check to make sure the event chain matches what we get back from nsIEventListenerService.getEventTargetChainFor - if (0 < actual.length) { - var chain = els.getEventTargetChainFor(actual[0], true); // Events should be dispatched on actual[0]. - for (var i = 0; i < expected.length; i++) { - ok(SpecialPowers.compare(chain[i], expected[i]), msg + " at " + i + " for nsIEventListenerService"); - } - } -} - -/* - * Test 1: Test of event dispatch through a basic ShadowRoot with content a insertion point. - * - * <div elemOne> ------ <shadow-root shadowOne> - * | | - * <div elemTwo> <span elemThree> - * | - * <content elemFour> - */ - -var elemOne = document.createElement("div"); -elemOne.addEventListener("custom", eventListener); - -var elemTwo = document.createElement("div"); -elemTwo.addEventListener("custom", eventListener); - -var elemThree = document.createElement("span"); -elemThree.addEventListener("custom", eventListener); - -var elemFour = document.createElement("content"); -elemFour.addEventListener("custom", eventListener); - -var shadowOne = elemOne.createShadowRoot(); -shadowOne.addEventListener("custom", eventListener); - -elemThree.appendChild(elemFour); -shadowOne.appendChild(elemThree); -elemOne.appendChild(elemTwo); - -var eventChain = []; -var customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true }); -elemTwo.dispatchEvent(customEvent); -isEventChain(eventChain, [elemTwo, elemFour, elemThree, shadowOne, elemOne], "Event path for test 1 for event dispatched on elemTwo."); - -/* - * Test 2: Test of event dispatch through a nested ShadowRoots with content insertion points. - * - * <div elemFive> --- <shadow-root shadowTwo> - * | | - * <div elemOne> <div elemFour> ----- <shadow-root shadowOne> - * | | - * <content elemTwo> <p elemSix> - * | - * <content elemThree> - */ - -elemOne = document.createElement("div"); -elemOne.addEventListener("custom", eventListener); - -elemTwo = document.createElement("content"); -elemTwo.addEventListener("custom", eventListener); - -elemThree = document.createElement("content"); -elemThree.addEventListener("custom", eventListener); - -var elemFour = document.createElement("div"); -elemFour.addEventListener("custom", eventListener); - -var elemFive = document.createElement("div"); -elemFive.addEventListener("custom", eventListener); - -var elemSix = document.createElement("p"); -elemSix.addEventListener("custom", eventListener); - -var shadowOne = elemFour.createShadowRoot(); -shadowOne.addEventListener("custom", eventListener); - -var shadowTwo = elemFive.createShadowRoot(); -shadowTwo.addEventListener("custom", eventListener); - -elemFive.appendChild(elemOne); -shadowTwo.appendChild(elemFour); -elemFour.appendChild(elemTwo); -shadowOne.appendChild(elemSix); -elemSix.appendChild(elemThree); - -eventChain = []; -customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true }); -elemOne.dispatchEvent(customEvent); -is(elemOne.getDestinationInsertionPoints().length, 2, "yes"); -isEventChain(eventChain, [elemOne, elemThree, elemSix, shadowOne, elemTwo, elemFour, shadowTwo, elemFive], "Event path for test 2 for event dispatched on elemOne."); - -/* - * Test 3: Test of event dispatch through nested ShadowRoot with content insertion points. - * - * <div elemOne> ------- <shadow-root shadowOne> - * | | - * <span elemTwo> <span elemThree> ------------ <shadow-root shadowTwo> - * | | - * <span elemFour> <content elemSix> - * | - * <content elemFive> - */ - -elemOne = document.createElement("div"); -elemOne.addEventListener("custom", eventListener); - -elemTwo = document.createElement("span"); -elemTwo.addEventListener("custom", eventListener); - -elemThree = document.createElement("span"); -elemThree.addEventListener("custom", eventListener); - -elemFour = document.createElement("span"); -elemFour.addEventListener("custom", eventListener); - -elemFive = document.createElement("content"); -elemFive.addEventListener("custom", eventListener); - -elemSix = document.createElement("content"); -elemSix.addEventListener("custom", eventListener); - -shadowOne = elemOne.createShadowRoot(); -shadowOne.addEventListener("custom", eventListener); - -shadowTwo = elemThree.createShadowRoot(); -shadowTwo.addEventListener("custom", eventListener); - -elemOne.appendChild(elemTwo); -shadowOne.appendChild(elemThree); -elemThree.appendChild(elemFour); -elemFour.appendChild(elemFive); -shadowTwo.appendChild(elemSix); - -eventChain = []; -customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true }); -elemTwo.dispatchEvent(customEvent); -isEventChain(eventChain, [elemTwo, elemFive, elemFour, elemSix, shadowTwo, elemThree, shadowOne, elemOne], "Event path for test 3 for event dispatched on elemTwo."); - -/* - * Test 4: Test of event dispatch through host with multiple ShadowRoots with shadow insertion point. - * - * <div elemSeven> --- <shadow-root shadowTwo> (younger ShadowRoot) - * | | | - * <div elemOne> | <div elemSix> -------- <shadow-root shadowOne> - * | | | - * | <shadow elemFour> <content elemFive> - * | | - * | <content elemTwo> - * | - * --- <shadow-root shadowThree> (older ShadowRoot) - * | | - * | <content elemThree> - * | - * <div elemEight> - */ - -elemOne = document.createElement("div"); -elemOne.addEventListener("custom", eventListener); - -elemTwo = document.createElement("content"); -elemTwo.addEventListener("custom", eventListener); - -elemThree = document.createElement("content"); -elemThree.addEventListener("custom", eventListener); - -elemFour = document.createElement("shadow"); -elemFour.addEventListener("custom", eventListener); - -elemFive = document.createElement("content"); -elemFive.addEventListener("custom", eventListener); - -elemSix = document.createElement("div"); -elemSix.addEventListener("custom", eventListener); - -var elemSeven = document.createElement("div"); -elemSeven.addEventListener("custom", eventListener); - -var elemEight = document.createElement("div"); -elemEight.addEventListener("custom", eventListener); - -var shadowThree = elemSeven.createShadowRoot(); -shadowThree.addEventListener("custom", eventListener); - -shadowTwo = elemSeven.createShadowRoot(); -shadowTwo.addEventListener("custom", eventListener); - -shadowOne = elemSix.createShadowRoot(); -shadowOne.addEventListener("custom", eventListener); - -elemSeven.appendChild(elemOne); -shadowTwo.appendChild(elemSix); -elemSix.appendChild(elemFour); -elemFour.appendChild(elemTwo); -shadowThree.appendChild(elemEight); -shadowThree.appendChild(elemThree); -shadowOne.appendChild(elemFive); - -eventChain = []; -customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true }); -elemOne.dispatchEvent(customEvent); -isEventChain(eventChain, [elemOne, elemFive, shadowOne, elemThree, shadowThree, elemTwo, elemFour, elemSix, shadowTwo, elemSeven], "Event path for test 4 for event dispatched on elemOne."); - -eventChain = []; -customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true }); -elemEight.dispatchEvent(customEvent); -isEventChain(eventChain, [elemEight, elemFive, shadowOne, elemSix, shadowTwo, elemSeven], "Event path for test 4 for event dispatched on elemEight."); - -/* - * Test 5: Test of event dispatch through nested shadowroot with insertion points that match specific tags. - * - * <div elemOne> --------- <shadow-root shadowOne> - * | | | - * | <p elemThree> <span elemFour> ------------------------ <shadow-root shadowTwo> - * | | | | - * <span elemTwo> | <content select="p" elemFive> <content elemSeven> - * | - * <content select="span" elemSix> - */ - -elemOne = document.createElement("div"); -elemOne.addEventListener("custom", eventListener); - -elemTwo = document.createElement("span"); -elemTwo.addEventListener("custom", eventListener); - -elemThree = document.createElement("p"); -elemThree.addEventListener("custom", eventListener); - -elemFour = document.createElement("span"); -elemFour.addEventListener("custom", eventListener); - -elemFive = document.createElement("content"); -elemFive.select = "p"; -elemFive.addEventListener("custom", eventListener); - -elemSix = document.createElement("content"); -elemSix.select = "span"; -elemSix.addEventListener("custom", eventListener); - -elemSeven = document.createElement("content"); -elemSeven.addEventListener("custom", eventListener); - -shadowTwo = elemFour.createShadowRoot(); -shadowTwo.addEventListener("custom", eventListener); - -shadowOne = elemOne.createShadowRoot(); -shadowOne.addEventListener("custom", eventListener); - -elemOne.appendChild(elemTwo); -elemOne.appendChild(elemThree); -shadowOne.appendChild(elemFour); -elemFour.appendChild(elemSix); -elemFour.appendChild(elemFive); -shadowTwo.appendChild(elemSeven); - -eventChain = []; -customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true }); -elemTwo.dispatchEvent(customEvent); -isEventChain(eventChain, [elemTwo, elemSeven, shadowTwo, elemSix, elemFour, shadowOne, elemOne], "Event path for test 5 for event dispatched on elemTwo."); - -eventChain = []; -customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true }); -elemThree.dispatchEvent(customEvent); -isEventChain(eventChain, [elemThree, elemSeven, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 5 for event dispatched on elemThree."); - -/* - * Test 6: Test of event dispatch through nested shadowroot with insertion points that match specific tags. - * - * <div elemOne> --------- <shadow-root shadowOne>; - * | | | - * | <p elemThree> <span elemFour> ------ <shadow-root shadowTwo> - * | | | | - * <span elemTwo> <content elemFive> | <content select="p" elemSeven> - * | - * <content select="span" elemSix> - */ - -elemOne = document.createElement("div"); -elemOne.addEventListener("custom", eventListener); - -elemTwo = document.createElement("span"); -elemTwo.addEventListener("custom", eventListener); - -elemThree = document.createElement("p"); -elemThree.addEventListener("custom", eventListener); - -elemFour = document.createElement("span"); -elemFour.addEventListener("custom", eventListener); - -elemFive = document.createElement("content"); -elemFive.addEventListener("custom", eventListener); - -elemSix = document.createElement("content"); -elemSix.select = "span"; -elemSix.addEventListener("custom", eventListener); - -elemSeven = document.createElement("content"); -elemSeven.select = "p"; -elemSeven.addEventListener("custom", eventListener); - -shadowTwo = elemFour.createShadowRoot(); -shadowTwo.addEventListener("custom", eventListener); - -shadowOne = elemOne.createShadowRoot(); -shadowOne.addEventListener("custom", eventListener); - -elemOne.appendChild(elemTwo); -elemOne.appendChild(elemThree); -shadowOne.appendChild(elemFour); -elemFour.appendChild(elemFive); -shadowTwo.appendChild(elemSix); -shadowTwo.appendChild(elemSeven); - -eventChain = []; -customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true }); -elemTwo.dispatchEvent(customEvent); -isEventChain(eventChain, [elemTwo, elemSix, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 6 for event dispatched on elemTwo."); - -eventChain = []; -customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true }); -elemThree.dispatchEvent(customEvent); -isEventChain(eventChain, [elemThree, elemSeven, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 6 for event dispatched on elemThree."); - -/* - * Test 7: Test of event dispatch through nested shadowroot with insertion points that match specific tags. - * - * <div elemOne> --------- <shadow-root shadowOne> - * | | | - * | <p elemThree> <span elemFour> ------ <shadow-root shadowTwo> - * | | | - * <span elemTwo> <content elemFive> <span elemEight> - * | | - * | <content select="p" elemSeven> - * | - * <content select="span" elemSix> - */ - -elemOne = document.createElement("div"); -elemOne.addEventListener("custom", eventListener); - -elemTwo = document.createElement("span"); -elemTwo.addEventListener("custom", eventListener); - -elemThree = document.createElement("p"); -elemThree.addEventListener("custom", eventListener); - -elemFour = document.createElement("span"); -elemFour.addEventListener("custom", eventListener); - -elemFive = document.createElement("content"); -elemFive.addEventListener("custom", eventListener); - -elemSix = document.createElement("content"); -elemSix.select = "span"; -elemSix.addEventListener("custom", eventListener); - -elemSeven = document.createElement("content"); -elemSeven.select = "p"; -elemSeven.addEventListener("custom", eventListener); - -elemEight = document.createElement("span"); -elemEight.addEventListener("custom", eventListener); - -shadowTwo = elemFour.createShadowRoot(); -shadowTwo.addEventListener("custom", eventListener); - -shadowOne = elemOne.createShadowRoot(); -shadowOne.addEventListener("custom", eventListener); - -elemOne.appendChild(elemTwo); -elemOne.appendChild(elemThree); -shadowOne.appendChild(elemFour); -elemFour.appendChild(elemFive); -shadowTwo.appendChild(elemEight); -elemEight.appendChild(elemSix); -elemEight.appendChild(elemSeven); - -eventChain = []; -customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true }); -elemTwo.dispatchEvent(customEvent); -isEventChain(eventChain, [elemTwo, elemSix, elemEight, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 7 for event dispatched on elemTwo."); - -eventChain = []; -customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true }); -elemThree.dispatchEvent(customEvent); -isEventChain(eventChain, [elemThree, elemSeven, elemEight, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 7 for event dispatched on elemThree."); - -/* - * Test 8: Test of event dispatch through host with multiple ShadowRoots with shadow insertion point. - * - * <div elemOne> --- <shadow-root shadowOne> (younger ShadowRoot) - * | | - * | <div elemFour> - * | | - * | <shadow elemTwo> - * | - * --- <shadow-root shadowTwo> (older ShadowRoot) - * | - * <div elemThree> - */ - -elemOne = document.createElement("div"); -elemOne.addEventListener("custom", eventListener); - -elemTwo = document.createElement("shadow"); -elemTwo.addEventListener("custom", eventListener); - -elemThree = document.createElement("div"); -elemThree.addEventListener("custom", eventListener); - -elemFour = document.createElement("div"); -elemFour.addEventListener("custom", eventListener); - -shadowTwo = elemOne.createShadowRoot(); -shadowTwo.addEventListener("custom", eventListener); - -shadowOne = elemOne.createShadowRoot(); -shadowOne.addEventListener("custom", eventListener); - -shadowOne.appendChild(elemFour); -elemFour.appendChild(elemTwo); -shadowTwo.appendChild(elemThree); - -eventChain = []; -customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true }); -elemThree.dispatchEvent(customEvent); -isEventChain(eventChain, [elemThree, shadowTwo, elemTwo, elemFour, shadowOne, elemOne], "Event path for test 8 for event dispatched on elemThree."); - -</script> -</body> -</html> diff --git a/dom/tests/mochitest/webcomponents/test_fallback_dest_insertion_points.html b/dom/tests/mochitest/webcomponents/test_fallback_dest_insertion_points.html deleted file mode 100644 index 4eefa165f2..0000000000 --- a/dom/tests/mochitest/webcomponents/test_fallback_dest_insertion_points.html +++ /dev/null @@ -1,71 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=999999 ---> -<head> - <meta charset="utf-8"> - <title>Test for Bug 999999</title> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=999999">Mozilla Bug 999999</a> -<p id="display"></p> -<div id="content"> -<div id="shadowhost"></div> -</div> -<pre id="test"> -</pre> -<script type="application/javascript"> - -/** Test for Bug 999999 **/ -var host = document.getElementById("shadowhost"); - -// Test destination insertion points of node distributed to content element. -var shadowRoot = host.createShadowRoot(); -shadowRoot.innerHTML = '<div id="innerhost"><content id="innercontent"></content></div>'; - -var fallback = document.createElement("span"); -var innerContent = shadowRoot.getElementById("innercontent"); - -innerContent.appendChild(fallback); - -is(fallback.getDestinationInsertionPoints().length, 1, "Active fallback content should be distributed to insertion point."); -is(fallback.getDestinationInsertionPoints()[0], innerContent, "Insertion point should be in list of destination insertion points."); - -// Test destination insertion points of reprojected fallback content. -var innerHost = shadowRoot.getElementById("innerhost"); -var innerShadowRoot = innerHost.createShadowRoot(); -innerShadowRoot.innerHTML = '<content id="innerinnercontent"></content>'; - -var innerInnerContent = innerShadowRoot.getElementById("innerinnercontent"); - -is(fallback.getDestinationInsertionPoints().length, 2, "Fallback content should have been distributed into parent and reprojected into another insertion point."); -is(fallback.getDestinationInsertionPoints()[1], innerInnerContent, "Destination insertion points should contain content element to which the node was reprojected."); - -// Test destination insertion points of fallback content that was dropped due to content element matching a node in the host. -var span = document.createElement("span"); -host.appendChild(span); - -is(fallback.getDestinationInsertionPoints().length, 0, "After dropping insertion points, fallback content should not have any nodes in destination insertion points list."); - -// Test destination insertion points of fallback content after reactivating by dropping matched content on host. -host.removeChild(span); -is(fallback.getDestinationInsertionPoints().length, 2, "Fallback content should have 2 destination insertion points after being reactivated."); -is(fallback.getDestinationInsertionPoints()[0], innerContent, "First destination insertion point should be the parent content"); -is(fallback.getDestinationInsertionPoints()[1], innerInnerContent, "Second destination insertion point should be the content to which the node is reprojected."); - -// Test destination insertion points of fallback content after removed from the tree. -innerContent.removeChild(fallback); -is(fallback.getDestinationInsertionPoints().length, 0, "Fallback content is no longer fallback content, destination insertion points should be empty."); - -// Test destination insertion points of child of non-insertion point content element. -var notInsertionPointContent = document.createElement("content"); -var notFallback = document.createElement("span"); -notInsertionPointContent.appendChild(notFallback); -is(notFallback.getDestinationInsertionPoints().length, 0, "Child of non-insertion point content should not be distributed to any nodes."); - -</script> -</body> -</html> diff --git a/dom/tests/mochitest/webcomponents/test_nested_content_element.html b/dom/tests/mochitest/webcomponents/test_nested_content_element.html deleted file mode 100644 index 1d98d2996b..0000000000 --- a/dom/tests/mochitest/webcomponents/test_nested_content_element.html +++ /dev/null @@ -1,127 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=806506 ---> -<head> - <title>Test for HTMLContent element</title> - <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> -</head> -<body> -<div id="grabme"></div> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a> -<script> - -/** - * Constructs a node with a nested ShadowRoot with the following structure: - * <span> - - - - - - - - - - <ShadowRoot> - * <span> <span> - - - - - - - - - - <ShadowRoot> - * id=one id=four <span> - * data-color=red data-color=orange id=eleven - * <span> <span> <content> - * id=two id=five id=twelve - * data-color=blue data-color=purple select=secondSelect - * <span> <content> <span> - * id=three id=six id=thirteen - * data-color=green select=firstSelect - * <span> - * id=seven - * <content> - * id=eight - * <span> - * id=nine - * <span> - * id=ten - * data-color=grey - */ -function constructTree(firstSelect, secondSelect) { - var rootSpan = document.createElement("span"); - rootSpan.innerHTML = '<span id="one" data-color="red"></span><span id="two" data-color="blue"></span><span id="three" data-color="green"></span>'; - var firstShadow = rootSpan.createShadowRoot(); - firstShadow.innerHTML = '<span id="four" data-color="orange"><span id="five" data-color="purple"></span><content id="six" select="' + firstSelect + '"><span id="seven"></span><content id="eight"></content><span id="nine"></span></content><span id="ten"></span></span>'; - var secondShadow = firstShadow.firstChild.createShadowRoot(); - secondShadow.innerHTML = '<span id="eleven"></span><content id="twelve" select="' + secondSelect + '"></content><span id="thirteen"></span>'; - return rootSpan; -} - -// Create a tree with content that matches on everything and check node distribution. -var allSpan = constructTree("*", "*"); -var firstContent = allSpan.shadowRoot.getElementById("six"); -var firstDistNodes = firstContent.getDistributedNodes(); -is(firstDistNodes.length, 3, "Universal selector should match all nodes."); -// Check the order of the distributed nodes. -is(firstDistNodes.item(0).id, "one", "First distributed node should have id of 'one'"); -is(firstDistNodes.item(1).id, "two", "Second distributed node should have id of 'two'"); -is(firstDistNodes.item(2).id, "three", "Third distributed node should have id of 'three'"); -var secondContent = allSpan.shadowRoot.firstChild.shadowRoot.getElementById("twelve"); -var secondDistNodes = secondContent.getDistributedNodes(); -is(secondDistNodes.length, 5, "Universial selector should match all nodes including those distributed into content."); -// Check the order of the distribute nodes. -is(secondDistNodes.item(0).id, "five", "First distributed node should have id of 'five'"); -is(secondDistNodes.item(1).id, "one", "Second distributed (reprojected) node should have id of 'one'"); -is(secondDistNodes.item(2).id, "two", "Third distributed (reprojected) node should have id of 'two'"); -is(secondDistNodes.item(3).id, "three", "Fourth distributed (reprojected) node should have id of 'three'"); -is(secondDistNodes.item(4).id, "ten", "Fifth distributed node should have id of 'ten'"); - -// Append an element after id=two and make sure that it is inserted into the corrent -// position in the insertion points. -var additionalSpan = document.createElement("span"); -additionalSpan.id = "additional"; - -// Insert the additional span in the third position, before the span with id=three. -allSpan.insertBefore(additionalSpan, allSpan.childNodes.item(2)); -firstDistNodes = firstContent.getDistributedNodes(); -secondDistNodes = secondContent.getDistributedNodes(); -is(firstDistNodes.length, 4, "First insertion point should match one more node."); -is(firstDistNodes.item(2).id, "additional", "Additional span should have been inserted into the third position of the first insertion point."); - -is(secondDistNodes.length, 6, "Second insertion point should match one more node."); -is(secondDistNodes.item(3).id, "additional", "Additional span should have been inserted into the fourth position of the second insertion point."); - -function nodeListDoesNotContain(nodeList, element) { - for (var i = 0; i < nodeList.length; i++) { - if (nodeList[i] == element) { - return false; - } - } - return true; -} - -// Remove the span with id=one and check that it is removed from all insertion points. -allSpan = constructTree("*", "*"); -var spanOne = allSpan.firstChild; -allSpan.removeChild(spanOne); -firstContent = allSpan.shadowRoot.getElementById("six"); -ok(nodeListDoesNotContain(firstContent.getDistributedNodes(), spanOne), "Child removed from host should not appear in insertion point node list."); -secondContent = allSpan.shadowRoot.firstChild.shadowRoot.getElementById("twelve"); -ok(nodeListDoesNotContain(secondContent.getDistributedNodes(), spanOne), "Child removed from host should not appear in nested insertion point node list."); - -// Make sure <content> in fallback content is inactive. -// First insertion point will not match anything and will use fallback content. -allSpan = constructTree("#nomatch", "*"); -var fallbackInsertionPoint = allSpan.shadowRoot.getElementById("eight"); -is(fallbackInsertionPoint.getDistributedNodes().length, 0, "Insertion points in default content should be inactive."); - -// Insertion points with non-universal selectors. -allSpan = constructTree("span[data-color=blue]", "*"); -firstContent = allSpan.shadowRoot.getElementById("six"); -is(firstContent.getDistributedNodes().length, 1, "Insertion point selector should only match one node."); -is(firstContent.getDistributedNodes()[0].dataset.color, "blue", "Projected node should match selector."); -secondContent = allSpan.shadowRoot.firstChild.shadowRoot.getElementById("twelve"); -is(secondContent.getDistributedNodes().length, 3, "Second insertion point should match two children and one reprojected node."); -is(secondContent.getDistributedNodes()[1].dataset.color, "blue", "Projected node should match selector."); - -allSpan = constructTree("span[data-color=blue]", "span[data-color=blue]"); -firstContent = allSpan.shadowRoot.getElementById("six"); -is(firstContent.getDistributedNodes().length, 1, "Insertion point selector should only match one node."); -is(firstContent.getDistributedNodes()[0].dataset.color, "blue", "Projected node should match selector."); -secondContent = allSpan.shadowRoot.firstChild.shadowRoot.getElementById("twelve"); -is(secondContent.getDistributedNodes().length, 1, "Insertion point should only match reprojected node."); -is(secondContent.getDistributedNodes()[0].dataset.color, "blue", "Projected node should match selector."); - -// Make sure that dynamically appended default content will get distributed. -</script> -</body> -</html> - diff --git a/dom/tests/mochitest/webcomponents/test_shadowroot_host.html b/dom/tests/mochitest/webcomponents/test_shadowroot_host.html deleted file mode 100644 index f48d63e87d..0000000000 --- a/dom/tests/mochitest/webcomponents/test_shadowroot_host.html +++ /dev/null @@ -1,41 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=1083587 ---> -<head> - <meta charset="utf-8"> - <title>Test for Bug 1083587</title> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1083587">Mozilla Bug 1083587</a> -<p id="display"></p> -<div id="content" style="display: none"> -<div id="host"></div> -</div> -<pre id="test"> -</pre> -<script type="application/javascript"> - -/** Test for Bug 1083587 **/ -var hostInDoc = document.getElementById("host"); -var shadowOne = hostInDoc.createShadowRoot(); -is(shadowOne.host, hostInDoc); - -var shadowTwo = hostInDoc.createShadowRoot(); -is(shadowOne.host, hostInDoc); -is(shadowTwo.host, hostInDoc); - -var hostNotInDoc = document.createElement("div"); -var shadowThree = hostNotInDoc.createShadowRoot(); -is(shadowThree.host, hostNotInDoc); - -var shadowFour = hostNotInDoc.createShadowRoot(); -is(shadowThree.host, hostNotInDoc); -is(shadowFour.host, hostNotInDoc); - -</script> -</body> -</html> diff --git a/dom/tests/mochitest/webcomponents/test_shadowroot_style_multiple_shadow.html b/dom/tests/mochitest/webcomponents/test_shadowroot_style_multiple_shadow.html deleted file mode 100644 index 7a606bcd7f..0000000000 --- a/dom/tests/mochitest/webcomponents/test_shadowroot_style_multiple_shadow.html +++ /dev/null @@ -1,57 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=806506 ---> -<head> - <title>Test for ShadowRoot styles with multiple ShadowRoot on host.</title> - <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> -</head> -<body> -<div class="tall" id="bodydiv"></div> -<div id="container"></div> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a> -<script> -// Create ShadowRoot. -var container = document.getElementById("container"); -var elem = document.createElement("div"); -container.appendChild(elem); // Put ShadowRoot host in document. -var firstRoot = elem.createShadowRoot(); -var secondRoot = elem.createShadowRoot(); -var thirdRoot = elem.createShadowRoot(); - -// A style element that will be appended into the ShadowRoot. -var firstStyle = document.createElement("style"); -firstRoot.appendChild(firstStyle); -is(firstRoot.styleSheets.length, 1, "firstStyle should be the only style in firstRoot."); -is(firstRoot.styleSheets[0].ownerNode, firstStyle, "firstStyle should in the ShadowRoot styleSheets."); - -var secondStyle = document.createElement("style"); -secondRoot.appendChild(secondStyle); -is(secondRoot.styleSheets.length, 1, "secondStyle should be the only style in secondRoot."); -is(secondRoot.styleSheets[0].ownerNode, secondStyle, "secondStyle should in the ShadowRoot styleSheets."); - -var thirdStyle = document.createElement("style"); -thirdRoot.appendChild(thirdStyle); -is(thirdRoot.styleSheets.length, 1, "thirdStyle should be the only style in thirdRoot."); -is(thirdRoot.styleSheets[0].ownerNode, thirdStyle, "thirdStyle should in the ShadowRoot styleSheets."); - -// Check the stylesheet counts again to make sure that none of the style sheets leaked into the older ShadowRoots. -is(firstRoot.styleSheets.length, 1, "Adding a stylesheet to a younger ShadowRoot should not affect stylesheets in the older ShadowRoot."); -is(secondRoot.styleSheets.length, 1, "Adding a stylesheet to a younger ShadowRoot should not affect stylesheets in the older ShadowRoot."); - -// Remove styles and make sure they are removed from the correct ShadowRoot. -firstRoot.removeChild(firstStyle); -is(firstRoot.styleSheets.length, 0, "firstRoot should no longer have any styles."); - -thirdRoot.removeChild(thirdStyle); -is(thirdRoot.styleSheets.length, 0, "thirdRoot should no longer have any styles."); - -secondRoot.removeChild(secondStyle); -is(secondRoot.styleSheets.length, 0, "secondRoot should no longer have any styles."); - -</script> -</body> -</html> - diff --git a/dom/tests/mochitest/webcomponents/test_shadowroot_youngershadowroot.html b/dom/tests/mochitest/webcomponents/test_shadowroot_youngershadowroot.html deleted file mode 100644 index 17743321bc..0000000000 --- a/dom/tests/mochitest/webcomponents/test_shadowroot_youngershadowroot.html +++ /dev/null @@ -1,41 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=1083587 ---> -<head> - <meta charset="utf-8"> - <title>Test for Bug 1083587</title> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1083587">Mozilla Bug 1083587</a> -<p id="display"></p> -<div id="content" style="display: none"> -<div id="host"></div> -</div> -<pre id="test"> -</pre> -<script type="application/javascript"> - -/** Test for Bug 1083587 **/ -var hostInDoc = document.getElementById("host"); -var shadowOne = hostInDoc.createShadowRoot(); -is(shadowOne.olderShadowRoot, null); - -var shadowTwo = hostInDoc.createShadowRoot(); -is(shadowOne.olderShadowRoot, null); -is(shadowTwo.olderShadowRoot, shadowOne); - -var hostNotInDoc = document.createElement("div"); -var shadowThree = hostNotInDoc.createShadowRoot(); -is(shadowThree.olderShadowRoot, null); - -var shadowFour = hostNotInDoc.createShadowRoot(); -is(shadowThree.olderShadowRoot, null); -is(shadowFour.olderShadowRoot, shadowThree); - -</script> -</body> -</html> diff --git a/dom/tests/mochitest/webcomponents/test_template_custom_elements.html b/dom/tests/mochitest/webcomponents/test_template_custom_elements.html deleted file mode 100644 index f7f4340cf4..0000000000 --- a/dom/tests/mochitest/webcomponents/test_template_custom_elements.html +++ /dev/null @@ -1,32 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=1091425 ---> -<head> - <title>Test for custom elements in template</title> - <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> -</head> -<body> -<template> - <x-foo></x-foo> -</template> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1091425">Bug 1091425</a> -<script> - -var p = {}; -p.createdCallback = function() { - ok(false, "Created callback should not be called for custom elements in templates."); -}; - -document.registerElement("x-foo", { prototype: p }); - -ok(true, "Created callback should not be called for custom elements in templates."); - -</script> -<template> - <x-foo></x-foo> -</template> -</body> -</html> diff --git a/dom/tests/mochitest/webcomponents/test_unresolved_pseudo_class.html b/dom/tests/mochitest/webcomponents/test_unresolved_pseudo_class.html deleted file mode 100644 index 3e1fae8ee8..0000000000 --- a/dom/tests/mochitest/webcomponents/test_unresolved_pseudo_class.html +++ /dev/null @@ -1,99 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=1111633 ---> -<head> - <title>Test template element in stale document.</title> - <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> - <style> - :unresolved { - color: rgb(0, 0, 255); - background-color: rgb(0, 0, 255); - } - - x-foo { color: rgb(255, 0, 0); } - - [is="x-del"]:not(:unresolved) { color: rgb(255, 0, 0); } - - [is="x-bar"]:not(:unresolved) { color: rgb(255, 0, 0); } - - [is="x-bar"]:unresolved { background-color: rgb(255, 0, 0); } - - x-baz:not(:unresolved) { - color: rgb(255, 0, 0); - background-color: rgb(255, 0, 0); - } - - span { color: rgb(0,255,0); } - - x-foo:unresolved + span { color: rgb(255,0,0); } - - </style> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1111633">Bug 1111633</a> -<div id="container"></div> -<x-foo id="foo"></x-foo> -<span id="span1">This text should be green</span> -<span id="bar" is="x-bar"></span> -<x-baz id="baz"></x-baz> -<span id="del" is="x-del"></span> -<script> - -// Before registerElement -var foo = document.querySelector('#foo'); -is(getComputedStyle(foo).color, "rgb(0, 0, 255)", "foo - color"); -is(getComputedStyle(foo).backgroundColor, "rgb(0, 0, 255)", "foo - backgroundColor"); - -var bar = document.querySelector('#bar'); -is(getComputedStyle(bar).color, "rgb(0, 0, 255)", "bar - color"); -is(getComputedStyle(bar).backgroundColor, "rgb(255, 0, 0)", "bar - backgroundColor"); - -var baz = document.querySelector('#baz'); -is(getComputedStyle(baz).color, "rgb(0, 0, 255)", "baz - color"); -is(getComputedStyle(baz).backgroundColor, "rgb(0, 0, 255)", "baz - backgroundColor"); - -var span1 = document.querySelector('#span1'); -is(getComputedStyle(span1).color, "rgb(255, 0, 0)", "span1 - color"); - -var Foo = document.registerElement('x-foo', { prototype: Object.create(HTMLElement.prototype) }); - -var Bar = document.registerElement('x-bar', { extends: 'span', prototype: Object.create(HTMLSpanElement.prototype) }); - -var Baz = document.registerElement('x-baz', { prototype: Object.create(HTMLElement.prototype) }); - -// After registerElement -is(getComputedStyle(foo).color, "rgb(255, 0, 0)", - "foo - color (after registerElement)"); - -is(getComputedStyle(bar).color, - "rgb(255, 0, 0)", "bar - color (after registerElement)"); - -is(getComputedStyle(baz).color, - "rgb(255, 0, 0)", "baz - color (after registerElement)"); -is(getComputedStyle(baz).backgroundColor, - "rgb(255, 0, 0)", "baz - backgroundColor (after registerElement)"); - -is(getComputedStyle(span1).color, "rgb(0, 255, 0)", "span1 - color (after registerElement)"); - -// After tree removal -var del = document.querySelector('#del'); -is(getComputedStyle(del).color, "rgb(0, 0, 255)", "del - color"); -var par = del.parentNode; -par.removeChild(del); -// Changing is attribute after creation should not change the type -// of a custom element, even if the element was removed and re-append to the tree. -del.setAttribute("is", "foobar"); -par.appendChild(del); -is(getComputedStyle(del).color, "rgb(0, 0, 255)", "del - color (after reappend)"); -var Del = document.registerElement('x-del', { extends: 'span', prototype: Object.create(HTMLSpanElement.prototype) }); -// [is="x-del"] will not match any longer so the rule of span will apply -is(getComputedStyle(del).color, "rgb(0, 255, 0)", "del - color (after registerElement)"); -// but the element should have been upgraded: -ok(del instanceof Del, "element was upgraded correctly after changing |is|"); - -</script> -</body> -</html> diff --git a/dom/webidl/AnonymousContent.webidl b/dom/webidl/AnonymousContent.webidl index 6755fe598a..8be69cd26f 100644 --- a/dom/webidl/AnonymousContent.webidl +++ b/dom/webidl/AnonymousContent.webidl @@ -77,4 +77,12 @@ interface AnonymousContent { [Throws] void setCutoutRectsForElement(DOMString elementId, sequence<DOMRect> rects); + + /** + * Get the computed value of a property on an element inside this custom + * anonymous content. + */ + [Throws] + DOMString? getComputedStylePropertyValue(DOMString elementId, + DOMString propertyName); }; diff --git a/dom/webidl/Document.webidl b/dom/webidl/Document.webidl index b28903ae23..ba2ec17ea7 100644 --- a/dom/webidl/Document.webidl +++ b/dom/webidl/Document.webidl @@ -17,6 +17,9 @@ enum VisibilityState { "hidden", "visible", "prerender" }; /* https://dom.spec.whatwg.org/#dictdef-elementcreationoptions */ dictionary ElementCreationOptions { DOMString is; + + [ChromeOnly] + DOMString pseudo; }; /* http://dom.spec.whatwg.org/#interface-document */ @@ -268,13 +271,6 @@ partial interface Document { attribute EventHandler onpointerlockerror; }; -//http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html#dfn-document-register -partial interface Document { - // this is deprecated from CustomElements v0 - [Throws, Func="CustomElementRegistry::IsCustomElementEnabled"] - object registerElement(DOMString name, optional ElementRegistrationOptions options); -}; - // https://w3c.github.io/page-visibility/#extensions-to-the-document-interface partial interface Document { [Pref="dom.visibilityAPI.enabled"] diff --git a/dom/webidl/Element.webidl b/dom/webidl/Element.webidl index a6b2bfdd5c..d5cac7c647 100644 --- a/dom/webidl/Element.webidl +++ b/dom/webidl/Element.webidl @@ -228,14 +228,26 @@ partial interface Element { NodeList querySelectorAll(DOMString selectors); }; -// http://w3c.github.io/webcomponents/spec/shadow/#extensions-to-element-interface +// https://dom.spec.whatwg.org/#dictdef-shadowrootinit +dictionary ShadowRootInit { + required ShadowRootMode mode; +}; + +// https://dom.spec.whatwg.org/#element partial interface Element { - [Throws,Func="nsDocument::IsWebComponentsEnabled"] - ShadowRoot createShadowRoot(); - [Func="nsDocument::IsWebComponentsEnabled"] - NodeList getDestinationInsertionPoints(); - [Func="nsDocument::IsWebComponentsEnabled"] + // Shadow DOM v1 + [Throws, Func="nsDocument::IsWebComponentsEnabled"] + ShadowRoot attachShadow(ShadowRootInit shadowRootInitDict); + [BinaryName="shadowRootByMode", Func="nsDocument::IsWebComponentsEnabled"] readonly attribute ShadowRoot? shadowRoot; + [BinaryName="assignedSlotByMode", Func="nsDocument::IsWebComponentsEnabled"] + readonly attribute HTMLSlotElement? assignedSlot; + [CEReactions, Unscopable, SetterThrows, Func="nsDocument::IsWebComponentsEnabled"] + attribute DOMString slot; + + // [deprecated] Shadow DOM v0 + [Throws, Func="nsDocument::IsWebComponentsEnabled"] + ShadowRoot createShadowRoot(); }; Element implements ChildNode; diff --git a/dom/webidl/Event.webidl b/dom/webidl/Event.webidl index a5d7da7d43..8a8e71c43b 100644 --- a/dom/webidl/Event.webidl +++ b/dom/webidl/Event.webidl @@ -22,6 +22,8 @@ interface Event { [Pure] readonly attribute EventTarget? currentTarget; + sequence<EventTarget> composedPath(); + const unsigned short NONE = 0; const unsigned short CAPTURING_PHASE = 1; const unsigned short AT_TARGET = 2; diff --git a/dom/webidl/HTMLContentElement.webidl b/dom/webidl/HTMLContentElement.webidl deleted file mode 100644 index ea809f120c..0000000000 --- a/dom/webidl/HTMLContentElement.webidl +++ /dev/null @@ -1,20 +0,0 @@ -/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* 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/. - * - * The origin of this IDL file is - * https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html - * - * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and - * Opera Software ASA. You are granted a license to use, reproduce - * and create derivative works of this document. - */ - -[Func="nsDocument::IsWebComponentsEnabled"] -interface HTMLContentElement : HTMLElement -{ - attribute DOMString select; - NodeList getDistributedNodes(); -}; - diff --git a/dom/webidl/HTMLShadowElement.webidl b/dom/webidl/HTMLSlotElement.webidl index f72cd06e18..24dfcf3508 100644 --- a/dom/webidl/HTMLShadowElement.webidl +++ b/dom/webidl/HTMLSlotElement.webidl @@ -4,16 +4,19 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. * * The origin of this IDL file is - * https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html + * https://html.spec.whatwg.org/multipage/forms.html#the-dialog-element * * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and * Opera Software ASA. You are granted a license to use, reproduce * and create derivative works of this document. */ -[Func="nsDocument::IsWebComponentsEnabled"] -interface HTMLShadowElement : HTMLElement -{ - readonly attribute ShadowRoot? olderShadowRoot; +[Func="nsDocument::IsWebComponentsEnabled", Exposed=Window, HTMLConstructor] +interface HTMLSlotElement : HTMLElement { + [CEReactions, SetterThrows] attribute DOMString name; + sequence<Node> assignedNodes(optional AssignedNodesOptions options); }; +dictionary AssignedNodesOptions { + boolean flatten = false; +};
\ No newline at end of file diff --git a/dom/webidl/ShadowRoot.webidl b/dom/webidl/ShadowRoot.webidl index 8dd069244c..47e6cf5ec5 100644 --- a/dom/webidl/ShadowRoot.webidl +++ b/dom/webidl/ShadowRoot.webidl @@ -10,17 +10,27 @@ * liability, trademark and document use rules apply. */ +// https://dom.spec.whatwg.org/#enumdef-shadowrootmode +enum ShadowRootMode { + "open", + "closed" +}; + +// https://dom.spec.whatwg.org/#shadowroot [Func="nsDocument::IsWebComponentsEnabled"] interface ShadowRoot : DocumentFragment { + // Shadow DOM v1 + readonly attribute ShadowRootMode mode; + readonly attribute Element host; + + // [deprecated] Shadow DOM v0 Element? getElementById(DOMString elementId); HTMLCollection getElementsByTagName(DOMString localName); HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName); HTMLCollection getElementsByClassName(DOMString classNames); [CEReactions, SetterThrows, TreatNullAs=EmptyString] attribute DOMString innerHTML; - readonly attribute Element host; - readonly attribute ShadowRoot? olderShadowRoot; attribute boolean applyAuthorStyles; readonly attribute StyleSheetList styleSheets; }; diff --git a/dom/webidl/Text.webidl b/dom/webidl/Text.webidl index dcd25cec3a..fb7b5d6857 100644 --- a/dom/webidl/Text.webidl +++ b/dom/webidl/Text.webidl @@ -18,4 +18,9 @@ interface Text : CharacterData { readonly attribute DOMString wholeText; }; +partial interface Text { + [BinaryName="assignedSlotByMode", Func="nsTextNode::IsWebComponentsEnabled"] + readonly attribute HTMLSlotElement? assignedSlot; +}; + Text implements GeometryUtils; diff --git a/dom/webidl/WebComponents.webidl b/dom/webidl/WebComponents.webidl index f9bb8214af..9c5e53131b 100644 --- a/dom/webidl/WebComponents.webidl +++ b/dom/webidl/WebComponents.webidl @@ -25,8 +25,3 @@ dictionary LifecycleCallbacks { LifecycleAdoptedCallback? adoptedCallback; LifecycleAttributeChangedCallback? attributeChangedCallback; }; - -dictionary ElementRegistrationOptions { - object? prototype = null; - DOMString? extends = null; -}; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 156da302dd..d76a58e1f7 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -177,7 +177,6 @@ WEBIDL_FILES = [ 'HTMLButtonElement.webidl', 'HTMLCanvasElement.webidl', 'HTMLCollection.webidl', - 'HTMLContentElement.webidl', 'HTMLDataElement.webidl', 'HTMLDataListElement.webidl', 'HTMLDetailsElement.webidl', @@ -226,7 +225,7 @@ WEBIDL_FILES = [ 'HTMLQuoteElement.webidl', 'HTMLScriptElement.webidl', 'HTMLSelectElement.webidl', - 'HTMLShadowElement.webidl', + 'HTMLSlotElement.webidl', 'HTMLSourceElement.webidl', 'HTMLSpanElement.webidl', 'HTMLStyleElement.webidl', diff --git a/dom/workers/SharedWorker.cpp b/dom/workers/SharedWorker.cpp index 99bb503397..71c644405c 100644 --- a/dom/workers/SharedWorker.cpp +++ b/dom/workers/SharedWorker.cpp @@ -181,7 +181,7 @@ SharedWorker::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) } nsresult -SharedWorker::PreHandleEvent(EventChainPreVisitor& aVisitor) +SharedWorker::GetEventTargetParent(EventChainPreVisitor& aVisitor) { AssertIsOnMainThread(); @@ -196,9 +196,9 @@ SharedWorker::PreHandleEvent(EventChainPreVisitor& aVisitor) QueueEvent(event); aVisitor.mCanHandle = false; - aVisitor.mParentTarget = nullptr; + aVisitor.SetParentTarget(nullptr, false); return NS_OK; } - return DOMEventTargetHelper::PreHandleEvent(aVisitor); + return DOMEventTargetHelper::GetEventTargetParent(aVisitor); } diff --git a/dom/workers/SharedWorker.h b/dom/workers/SharedWorker.h index 220436e4dc..4b50d39e85 100644 --- a/dom/workers/SharedWorker.h +++ b/dom/workers/SharedWorker.h @@ -76,7 +76,7 @@ public: WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; virtual nsresult - PreHandleEvent(EventChainPreVisitor& aVisitor) override; + GetEventTargetParent(EventChainPreVisitor& aVisitor) override; WorkerPrivate* GetWorkerPrivate() const diff --git a/dom/xbl/XBLChildrenElement.cpp b/dom/xbl/XBLChildrenElement.cpp index e4058a7890..f785a3058a 100644 --- a/dom/xbl/XBLChildrenElement.cpp +++ b/dom/xbl/XBLChildrenElement.cpp @@ -7,6 +7,7 @@ #include "mozilla/dom/XBLChildrenElement.h" #include "nsCharSeparatedTokenizer.h" #include "mozilla/dom/NodeListBinding.h" +#include "nsAttrValueOrString.h" namespace mozilla { namespace dom { @@ -27,34 +28,24 @@ NS_INTERFACE_MAP_END_INHERITING(Element) NS_IMPL_ELEMENT_CLONE(XBLChildrenElement) nsresult -XBLChildrenElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, - bool aNotify) +XBLChildrenElement::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString* aValue, + bool aNotify) { - if (aAttribute == nsGkAtoms::includes && - aNameSpaceID == kNameSpaceID_None) { - mIncludes.Clear(); - } - - return Element::UnsetAttr(aNameSpaceID, aAttribute, aNotify); -} - -bool -XBLChildrenElement::ParseAttribute(int32_t aNamespaceID, - nsIAtom* aAttribute, - const nsAString& aValue, - nsAttrValue& aResult) -{ - if (aAttribute == nsGkAtoms::includes && - aNamespaceID == kNameSpaceID_None) { - mIncludes.Clear(); - nsCharSeparatedTokenizer tok(aValue, '|', - nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL); - while (tok.hasMoreTokens()) { - mIncludes.AppendElement(NS_Atomize(tok.nextToken())); + if (aNamespaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::includes) { + mIncludes.Clear(); + if (aValue) { + nsCharSeparatedTokenizer tok(aValue->String(), '|', + nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL); + while (tok.hasMoreTokens()) { + mIncludes.AppendElement(NS_Atomize(tok.nextToken())); + } + } } } - return false; + return nsXMLElement::BeforeSetAttr(aNamespaceID, aName, aValue, aNotify); } } // namespace dom diff --git a/dom/xbl/XBLChildrenElement.h b/dom/xbl/XBLChildrenElement.h index 4714da4a84..c010854742 100644 --- a/dom/xbl/XBLChildrenElement.h +++ b/dom/xbl/XBLChildrenElement.h @@ -37,14 +37,6 @@ public: virtual nsIDOMNode* AsDOMNode() override { return this; } - // nsIContent interface methods - virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, - bool aNotify) override; - virtual bool ParseAttribute(int32_t aNamespaceID, - nsIAtom* aAttribute, - const nsAString& aValue, - nsAttrValue& aResult) override; - void AppendInsertedChild(nsIContent* aChild) { mInsertedChildren.AppendElement(aChild); @@ -147,6 +139,10 @@ public: protected: ~XBLChildrenElement(); + virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString* aValue, + bool aNotify) override; + private: nsTArray<nsIContent*> mInsertedChildren; // WEAK nsTArray<nsCOMPtr<nsIAtom> > mIncludes; diff --git a/dom/xbl/nsXBLService.cpp b/dom/xbl/nsXBLService.cpp index 1475b1368d..b50b2c6fe3 100644 --- a/dom/xbl/nsXBLService.cpp +++ b/dom/xbl/nsXBLService.cpp @@ -115,45 +115,19 @@ public: if (!doc) return; - // Destroy the frames for mBoundElement. - nsIContent* destroyedFramesFor = nullptr; - nsIPresShell* shell = doc->GetShell(); - if (shell) { - shell->DestroyFramesFor(mBoundElement, &destroyedFramesFor); - } - MOZ_ASSERT(!mBoundElement->GetPrimaryFrame()); - // Get the binding. bool ready = false; nsXBLService::GetInstance()->BindingReady(mBoundElement, mBindingURI, &ready); if (!ready) return; - // If |mBoundElement| is (in addition to having binding |mBinding|) - // also a descendant of another element with binding |mBinding|, - // then we might have just constructed it due to the - // notification of its parent. (We can know about both if the - // binding loads were triggered from the DOM rather than frame - // construction.) So we have to check both whether the element - // has a primary frame and whether it's in the frame manager maps - // before sending a ContentInserted notification, or bad things - // will happen. - MOZ_ASSERT(shell == doc->GetShell()); - if (shell) { - nsIFrame* childFrame = mBoundElement->GetPrimaryFrame(); - if (!childFrame) { - // Check to see if it's in the undisplayed content map... - nsFrameManager* fm = shell->FrameManager(); - nsStyleContext* sc = fm->GetUndisplayedContent(mBoundElement); - if (!sc) { - // or in the display:contents map. - sc = fm->GetDisplayContentsStyleFor(mBoundElement); - } - if (!sc) { - shell->CreateFramesFor(destroyedFramesFor); - } - } + // Destroy the frames for mBoundElement. Do this after getting the binding, + // since if the binding fetch fails then we don't want to destroy the + // frames. + if (nsIPresShell* shell = doc->GetShell()) { + shell->DestroyFramesForAndRestyle(mBoundElement->AsElement()); } + MOZ_ASSERT(!mBoundElement->GetPrimaryFrame()); } nsXBLBindingRequest(nsIURI* aURI, nsIContent* aBoundElement) diff --git a/dom/xml/nsXMLElement.cpp b/dom/xml/nsXMLElement.cpp index 45163a1c42..ad0314f4ca 100644 --- a/dom/xml/nsXMLElement.cpp +++ b/dom/xml/nsXMLElement.cpp @@ -9,6 +9,7 @@ #include "mozilla/dom/ElementInlines.h" #include "nsContentUtils.h" // nsAutoScriptBlocker +using namespace mozilla; using namespace mozilla::dom; nsresult @@ -29,4 +30,26 @@ nsXMLElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) return ElementBinding::Wrap(aCx, this, aGivenProto); } +void +nsXMLElement::UnbindFromTree(bool aDeep, bool aNullParent) +{ + CSSPseudoElementType pseudoType = GetPseudoElementType(); + bool isBefore = pseudoType == CSSPseudoElementType::before; + nsIAtom* property = isBefore + ? nsGkAtoms::beforePseudoProperty : nsGkAtoms::afterPseudoProperty; + + switch (pseudoType) { + case CSSPseudoElementType::before: + case CSSPseudoElementType::after: { + MOZ_ASSERT(GetParent()); + MOZ_ASSERT(GetParent()->IsElement()); + GetParent()->DeleteProperty(property); + break; + } + default: + break; + } + Element::UnbindFromTree(aDeep, aNullParent); +} + NS_IMPL_ELEMENT_CLONE(nsXMLElement) diff --git a/dom/xml/nsXMLElement.h b/dom/xml/nsXMLElement.h index d16b82f399..85f79970cd 100644 --- a/dom/xml/nsXMLElement.h +++ b/dom/xml/nsXMLElement.h @@ -35,6 +35,9 @@ public: virtual nsIDOMNode* AsDOMNode() override { return this; } + virtual void UnbindFromTree(bool aDeep = true, + bool aNullParent = true) override; + protected: virtual ~nsXMLElement() {} diff --git a/dom/xslt/xslt/txMozillaXSLTProcessor.cpp b/dom/xslt/xslt/txMozillaXSLTProcessor.cpp index facb435b4c..0a2dd9b084 100644 --- a/dom/xslt/xslt/txMozillaXSLTProcessor.cpp +++ b/dom/xslt/xslt/txMozillaXSLTProcessor.cpp @@ -1284,6 +1284,11 @@ txMozillaXSLTProcessor::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenP return XSLTProcessorBinding::Wrap(aCx, this, aGivenProto); } +DocGroup* +txMozillaXSLTProcessor::GetDocGroup() const +{ + return mStylesheetDocument ? mStylesheetDocument->GetDocGroup() : nullptr; +} /* static */ already_AddRefed<txMozillaXSLTProcessor> txMozillaXSLTProcessor::Constructor(const GlobalObject& aGlobal, diff --git a/dom/xslt/xslt/txMozillaXSLTProcessor.h b/dom/xslt/xslt/txMozillaXSLTProcessor.h index 93d3c2c301..fc76585f2d 100644 --- a/dom/xslt/xslt/txMozillaXSLTProcessor.h +++ b/dom/xslt/xslt/txMozillaXSLTProcessor.h @@ -29,6 +29,7 @@ class txIGlobalParameter; namespace mozilla { namespace dom { +class DocGroup; class Document; class DocumentFragment; class GlobalObject; @@ -103,6 +104,8 @@ public: return mOwner; } + mozilla::dom::DocGroup* GetDocGroup() const; + static already_AddRefed<txMozillaXSLTProcessor> Constructor(const mozilla::dom::GlobalObject& aGlobal, mozilla::ErrorResult& aRv); diff --git a/dom/xul/XULDocument.cpp b/dom/xul/XULDocument.cpp index 1dcb55aee4..929efc1afc 100644 --- a/dom/xul/XULDocument.cpp +++ b/dom/xul/XULDocument.cpp @@ -66,7 +66,6 @@ #include "nsContentUtils.h" #include "nsIParser.h" #include "nsCharsetSource.h" -#include "nsIParserService.h" #include "mozilla/StyleSheetInlines.h" #include "mozilla/css/Loader.h" #include "nsIScriptError.h" diff --git a/dom/xul/nsXULElement.cpp b/dom/xul/nsXULElement.cpp index a854f53ecd..2ae03e0b17 100644 --- a/dom/xul/nsXULElement.cpp +++ b/dom/xul/nsXULElement.cpp @@ -126,6 +126,8 @@ uint32_t nsXULPrototypeAttribute::gNumCacheSets; uint32_t nsXULPrototypeAttribute::gNumCacheFills; #endif +#define NS_DISPATCH_XUL_COMMAND (1 << 0) + class nsXULElementTearoff final : public nsIFrameLoaderOwner { ~nsXULElementTearoff() {} @@ -202,7 +204,7 @@ nsXULElement::Create(nsXULPrototypeElement* aPrototype, mozilla::dom::NodeInfo * element->SetHasID(); } if (aPrototype->mHasClassAttribute) { - element->SetFlags(NODE_MAY_HAVE_CLASS); + element->SetMayHaveClass(); } if (aPrototype->mHasStyleAttribute) { element->SetMayHaveStyle(); @@ -358,12 +360,15 @@ nsXULElement::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const attrValue.SetTo(*originalValue); } + bool oldValueSet; if (originalName->IsAtom()) { rv = element->mAttrsAndChildren.SetAndSwapAttr(originalName->Atom(), - attrValue); + attrValue, + &oldValueSet); } else { rv = element->mAttrsAndChildren.SetAndSwapAttr(originalName->NodeInfo(), - attrValue); + attrValue, + &oldValueSet); } NS_ENSURE_SUCCESS(rv, rv); element->AddListenerFor(*originalName, true); @@ -372,7 +377,7 @@ nsXULElement::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const element->SetHasID(); } if (originalName->Equals(nsGkAtoms::_class)) { - element->SetFlags(NODE_MAY_HAVE_CLASS); + element->SetMayHaveClass(); } if (originalName->Equals(nsGkAtoms::style)) { element->SetMayHaveStyle(); @@ -1005,7 +1010,7 @@ nsXULElement::UnregisterAccessKey(const nsAString& aOldValue) nsresult nsXULElement::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, - nsAttrValueOrString* aValue, bool aNotify) + const nsAttrValueOrString* aValue, bool aNotify) { if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::accesskey && IsInUncomposedDoc()) { @@ -1051,7 +1056,8 @@ nsXULElement::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, nsresult nsXULElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { if (aNamespaceID == kNameSpaceID_None) { if (aValue) { @@ -1172,7 +1178,7 @@ nsXULElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, } return nsStyledElement::AfterSetAttr(aNamespaceID, aName, - aValue, aNotify); + aValue, aOldValue, aNotify); } bool @@ -1236,20 +1242,68 @@ nsXULElement::List(FILE* out, int32_t aIndent) const } #endif +bool +nsXULElement::IsEventStoppedFromAnonymousScrollbar(EventMessage aMessage) +{ + return (IsRootOfNativeAnonymousSubtree() && + IsAnyOfXULElements(nsGkAtoms::scrollbar, nsGkAtoms::scrollcorner) && + (aMessage == eMouseClick || aMessage == eMouseDoubleClick || + aMessage == eXULCommand || aMessage == eContextMenu || + aMessage == eDragStart)); +} + nsresult -nsXULElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +nsXULElement::DispatchXULCommand(const EventChainVisitor& aVisitor, + nsAutoString& aCommand) +{ + // XXX sXBL/XBL2 issue! Owner or current document? + nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(GetUncomposedDoc())); + NS_ENSURE_STATE(domDoc); + nsCOMPtr<nsIDOMElement> commandElt; + domDoc->GetElementById(aCommand, getter_AddRefs(commandElt)); + nsCOMPtr<nsIContent> commandContent(do_QueryInterface(commandElt)); + if (commandContent) { + // Create a new command event to dispatch to the element + // pointed to by the command attribute. The new event's + // sourceEvent will be the original command event that we're + // handling. + nsCOMPtr<nsIDOMEvent> domEvent = aVisitor.mDOMEvent; + while (domEvent) { + Event* event = domEvent->InternalDOMEvent(); + NS_ENSURE_STATE(!SameCOMIdentity(event->GetOriginalTarget(), + commandContent)); + nsCOMPtr<nsIDOMXULCommandEvent> commandEvent = + do_QueryInterface(domEvent); + if (commandEvent) { + commandEvent->GetSourceEvent(getter_AddRefs(domEvent)); + } else { + domEvent = nullptr; + } + } + WidgetInputEvent* orig = aVisitor.mEvent->AsInputEvent(); + nsContentUtils::DispatchXULCommand( + commandContent, + orig->IsTrusted(), + aVisitor.mDOMEvent, + nullptr, + orig->IsControl(), + orig->IsAlt(), + orig->IsShift(), + orig->IsMeta()); + } else { + NS_WARNING("A XUL element is attached to a command that doesn't exist!\n"); + } + return NS_OK; +} + +nsresult +nsXULElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.mForceContentDispatch = true; //FIXME! Bug 329119 - if (IsRootOfNativeAnonymousSubtree() && - (IsAnyOfXULElements(nsGkAtoms::scrollbar, nsGkAtoms::scrollcorner)) && - (aVisitor.mEvent->mMessage == eMouseClick || - aVisitor.mEvent->mMessage == eMouseDoubleClick || - aVisitor.mEvent->mMessage == eXULCommand || - aVisitor.mEvent->mMessage == eContextMenu || - aVisitor.mEvent->mMessage == eDragStart)) { + if (IsEventStoppedFromAnonymousScrollbar(aVisitor.mEvent->mMessage)) { // Don't propagate these events from native anonymous scrollbar. aVisitor.mCanHandle = true; - aVisitor.mParentTarget = nullptr; + aVisitor.SetParentTarget(nullptr, false); return NS_OK; } if (aVisitor.mEvent->mMessage == eXULCommand && @@ -1263,55 +1317,33 @@ nsXULElement::PreHandleEvent(EventChainPreVisitor& aVisitor) // See if we have a command elt. If so, we execute on the command // instead of on our content element. nsAutoString command; - if (xulEvent && GetAttr(kNameSpaceID_None, nsGkAtoms::command, command) && + if (xulEvent && + GetAttr(kNameSpaceID_None, nsGkAtoms::command, command) && !command.IsEmpty()) { // Stop building the event target chain for the original event. // We don't want it to propagate to any DOM nodes. aVisitor.mCanHandle = false; aVisitor.mAutomaticChromeDispatch = false; - - // XXX sXBL/XBL2 issue! Owner or current document? - nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(GetUncomposedDoc())); - NS_ENSURE_STATE(domDoc); - nsCOMPtr<nsIDOMElement> commandElt; - domDoc->GetElementById(command, getter_AddRefs(commandElt)); - nsCOMPtr<nsIContent> commandContent(do_QueryInterface(commandElt)); - if (commandContent) { - // Create a new command event to dispatch to the element - // pointed to by the command attribute. The new event's - // sourceEvent will be the original command event that we're - // handling. - nsCOMPtr<nsIDOMEvent> domEvent = aVisitor.mDOMEvent; - while (domEvent) { - Event* event = domEvent->InternalDOMEvent(); - NS_ENSURE_STATE(!SameCOMIdentity(event->GetOriginalTarget(), - commandContent)); - nsCOMPtr<nsIDOMXULCommandEvent> commandEvent = - do_QueryInterface(domEvent); - if (commandEvent) { - commandEvent->GetSourceEvent(getter_AddRefs(domEvent)); - } else { - domEvent = nullptr; - } - } - - WidgetInputEvent* orig = aVisitor.mEvent->AsInputEvent(); - nsContentUtils::DispatchXULCommand( - commandContent, - aVisitor.mEvent->IsTrusted(), - aVisitor.mDOMEvent, - nullptr, - orig->IsControl(), - orig->IsAlt(), - orig->IsShift(), - orig->IsMeta()); - } else { - NS_WARNING("A XUL element is attached to a command that doesn't exist!\n"); - } + // Dispatch XUL command in PreHandleEvent to prevent it breaks event + // target chain creation + aVisitor.mWantsPreHandleEvent = true; + aVisitor.mItemFlags |= NS_DISPATCH_XUL_COMMAND; return NS_OK; } } + return nsStyledElement::GetEventTargetParent(aVisitor); +} + +nsresult +nsXULElement::PreHandleEvent(EventChainVisitor& aVisitor) +{ + if (aVisitor.mItemFlags & NS_DISPATCH_XUL_COMMAND) { + nsAutoString command; + GetAttr(kNameSpaceID_None, nsGkAtoms::command, command); + MOZ_ASSERT(!command.IsEmpty()); + return DispatchXULCommand(aVisitor, command); + } return nsStyledElement::PreHandleEvent(aVisitor); } @@ -1880,12 +1912,14 @@ nsXULElement::MakeHeavyweight(nsXULPrototypeElement* aPrototype) attrValue.SetTo(protoattr->mValue); } + bool oldValueSet; // XXX we might wanna have a SetAndTakeAttr that takes an nsAttrName if (protoattr->mName.IsAtom()) { - rv = mAttrsAndChildren.SetAndSwapAttr(protoattr->mName.Atom(), attrValue); + rv = mAttrsAndChildren.SetAndSwapAttr(protoattr->mName.Atom(), + attrValue, &oldValueSet); } else { rv = mAttrsAndChildren.SetAndSwapAttr(protoattr->mName.NodeInfo(), - attrValue); + attrValue, &oldValueSet); } NS_ENSURE_SUCCESS(rv, rv); } diff --git a/dom/xul/nsXULElement.h b/dom/xul/nsXULElement.h index 80424412f8..a307141452 100644 --- a/dom/xul/nsXULElement.h +++ b/dom/xul/nsXULElement.h @@ -355,9 +355,10 @@ public: NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsXULElement, nsStyledElement) // nsINode - virtual nsresult PreHandleEvent( + virtual nsresult GetEventTargetParent( mozilla::EventChainPreVisitor& aVisitor) override; - + virtual nsresult PreHandleEvent( + mozilla::EventChainVisitor& aVisitor) override; // nsIContent virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsIContent* aBindingParent, @@ -623,10 +624,12 @@ protected: nsresult MakeHeavyweight(nsXULPrototypeElement* aPrototype); virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, - nsAttrValueOrString* aValue, + const nsAttrValueOrString* aValue, bool aNotify) override; virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, - const nsAttrValue* aValue, bool aNotify) override; + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; virtual void UpdateEditableState(bool aNotify) override; @@ -689,6 +692,11 @@ protected: virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override; void MaybeUpdatePrivateLifetime(); + + bool IsEventStoppedFromAnonymousScrollbar(mozilla::EventMessage aMessage); + + nsresult DispatchXULCommand(const mozilla::EventChainVisitor& aVisitor, + nsAutoString& aCommand); }; #endif // nsXULElement_h__ diff --git a/editor/libeditor/EditorBase.cpp b/editor/libeditor/EditorBase.cpp index f7988cd1ad..3f419a74ed 100644 --- a/editor/libeditor/EditorBase.cpp +++ b/editor/libeditor/EditorBase.cpp @@ -37,6 +37,7 @@ #include "mozilla/TextComposition.h" // for TextComposition #include "mozilla/TextEvents.h" #include "mozilla/dom/Element.h" // for Element, nsINode::AsElement +#include "mozilla/dom/HTMLBodyElement.h" #include "mozilla/dom/Text.h" #include "mozilla/dom/Event.h" #include "mozilla/mozalloc.h" // for operator new, etc. @@ -71,7 +72,6 @@ #include "nsIDOMNode.h" // for nsIDOMNode, etc. #include "nsIDOMNodeList.h" // for nsIDOMNodeList #include "nsIDOMText.h" // for nsIDOMText -#include "nsIDocument.h" // for nsIDocument #include "nsIDocumentStateListener.h" // for nsIDocumentStateListener #include "nsIEditActionListener.h" // for nsIEditActionListener #include "nsIEditorObserver.h" // for nsIEditorObserver @@ -107,16 +107,12 @@ #include "prtime.h" // for PR_Now class nsIOutputStream; -class nsIParserService; class nsITransferable; #ifdef DEBUG #include "nsIDOMHTMLDocument.h" // for nsIDOMHTMLDocument #endif -// Defined in nsEditorRegistration.cpp -extern nsIParserService *sParserService; - namespace mozilla { using namespace dom; @@ -127,13 +123,13 @@ using namespace widget; *****************************************************************************/ EditorBase::EditorBase() - : mPlaceHolderName(nullptr) + : mPlaceholderName(nullptr) , mSelState(nullptr) , mPhonetic(nullptr) , mModCount(0) , mFlags(0) , mUpdateCount(0) - , mPlaceHolderBatch(0) + , mPlaceholderBatch(0) , mAction(EditAction::none) , mIMETextOffset(0) , mIMETextLength(0) @@ -151,7 +147,8 @@ EditorBase::EditorBase() EditorBase::~EditorBase() { - NS_ASSERTION(!mDocWeak || mDidPreDestroy, "Why PreDestroy hasn't been called?"); + MOZ_ASSERT(!IsInitialized() || mDidPreDestroy, + "Why PreDestroy hasn't been called?"); if (mComposition) { mComposition->OnEditorDestroyed(); @@ -220,20 +217,21 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(EditorBase) NS_IMETHODIMP -EditorBase::Init(nsIDOMDocument* aDoc, +EditorBase::Init(nsIDOMDocument* aDOMDocument, nsIContent* aRoot, - nsISelectionController* aSelCon, + nsISelectionController* aSelectionController, uint32_t aFlags, const nsAString& aValue) { MOZ_ASSERT(mAction == EditAction::none, "Initializing during an edit action is an error"); - MOZ_ASSERT(aDoc); - if (!aDoc) + MOZ_ASSERT(aDOMDocument); + if (!aDOMDocument) { return NS_ERROR_NULL_POINTER; + } // First only set flags, but other stuff shouldn't be initialized now. - // Don't move this call after initializing mDocWeak. + // Don't move this call after initializing mDocumentWeak. // SetFlags() can check whether it's called during initialization or not by // them. Note that SetFlags() will be called by PostCreate(). #ifdef DEBUG @@ -242,19 +240,21 @@ EditorBase::Init(nsIDOMDocument* aDoc, SetFlags(aFlags); NS_ASSERTION(NS_SUCCEEDED(rv), "SetFlags() failed"); - mDocWeak = do_GetWeakReference(aDoc); // weak reference to doc + nsCOMPtr<nsIDocument> document = do_QueryInterface(aDOMDocument); + mDocumentWeak = document.get(); // HTML editors currently don't have their own selection controller, // so they'll pass null as aSelCon, and we'll get the selection controller // off of the presshell. - nsCOMPtr<nsISelectionController> selCon; - if (aSelCon) { - mSelConWeak = do_GetWeakReference(aSelCon); // weak reference to selectioncontroller - selCon = aSelCon; + nsCOMPtr<nsISelectionController> selectionController; + if (aSelectionController) { + mSelectionControllerWeak = aSelectionController; + selectionController = aSelectionController; } else { nsCOMPtr<nsIPresShell> presShell = GetPresShell(); - selCon = do_QueryInterface(presShell); + selectionController = do_QueryInterface(presShell); } - NS_ASSERTION(selCon, "Selection controller should be available at this point"); + MOZ_ASSERT(selectionController, + "Selection controller should be available at this point"); //set up root element if we are passed one. if (aRoot) @@ -271,13 +271,14 @@ EditorBase::Init(nsIDOMDocument* aDoc, mIMETextNode = nullptr; } - /* Show the caret */ - selCon->SetCaretReadOnly(false); - selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON); + // Show the caret. + selectionController->SetCaretReadOnly(false); + selectionController->SetDisplaySelection( + nsISelectionController::SELECTION_ON); + // Show all the selection reflected to user. + selectionController->SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL); - selCon->SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL);//we want to see all the selection reflected to user - - NS_POSTCONDITION(mDocWeak, "bad state"); + MOZ_ASSERT(IsInitialized()); // Make sure that the editor will be destroyed properly mDidPreDestroy = false; @@ -356,8 +357,9 @@ EditorBase::CreateEventListeners() nsresult EditorBase::InstallEventListeners() { - NS_ENSURE_TRUE(mDocWeak && mEventListener, - NS_ERROR_NOT_INITIALIZED); + if (NS_WARN_IF(!IsInitialized()) || NS_WARN_IF(!mEventListener)) { + return NS_ERROR_NOT_INITIALIZED; + } // Initialize the event target. nsCOMPtr<nsIContent> rootContent = GetRoot(); @@ -378,7 +380,7 @@ EditorBase::InstallEventListeners() void EditorBase::RemoveEventListeners() { - if (!mDocWeak || !mEventListener) { + if (!IsInitialized() || !mEventListener) { return; } reinterpret_cast<EditorEventListener*>(mEventListener.get())->Disconnect(); @@ -501,7 +503,7 @@ EditorBase::SetFlags(uint32_t aFlags) bool spellcheckerWasEnabled = CanEnableSpellCheck(); mFlags = aFlags; - if (!mDocWeak) { + if (!IsInitialized()) { // If we're initializing, we shouldn't do anything now. // SetFlags() will be called by PostCreate(), // we should synchronize some stuff for the flags at that time. @@ -567,17 +569,15 @@ EditorBase::GetIsDocumentEditable(bool* aIsDocumentEditable) already_AddRefed<nsIDocument> EditorBase::GetDocument() { - NS_PRECONDITION(mDocWeak, "bad state, mDocWeak weak pointer not initialized"); - nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak); - return doc.forget(); + nsCOMPtr<nsIDocument> document = mDocumentWeak.get(); + return document.forget(); } already_AddRefed<nsIDOMDocument> EditorBase::GetDOMDocument() { - NS_PRECONDITION(mDocWeak, "bad state, mDocWeak weak pointer not initialized"); - nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(mDocWeak); - return doc.forget(); + nsCOMPtr<nsIDOMDocument> domDocument = do_QueryInterface(mDocumentWeak); + return domDocument.forget(); } NS_IMETHODIMP @@ -590,11 +590,12 @@ EditorBase::GetDocument(nsIDOMDocument** aDoc) already_AddRefed<nsIPresShell> EditorBase::GetPresShell() { - NS_PRECONDITION(mDocWeak, "bad state, null mDocWeak"); - nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak); - NS_ENSURE_TRUE(doc, nullptr); - nsCOMPtr<nsIPresShell> ps = doc->GetShell(); - return ps.forget(); + nsCOMPtr<nsIDocument> document = GetDocument(); + if (NS_WARN_IF(!document)) { + return nullptr; + } + nsCOMPtr<nsIPresShell> presShell = document->GetShell(); + return presShell.forget(); } already_AddRefed<nsIWidget> @@ -629,20 +630,27 @@ EditorBase::GetSelectionController(nsISelectionController** aSel) { NS_ENSURE_TRUE(aSel, NS_ERROR_NULL_POINTER); *aSel = nullptr; // init out param - nsCOMPtr<nsISelectionController> selCon; - if (mSelConWeak) { - selCon = do_QueryReferent(mSelConWeak); - } else { - nsCOMPtr<nsIPresShell> presShell = GetPresShell(); - selCon = do_QueryInterface(presShell); - } - if (!selCon) { + nsCOMPtr<nsISelectionController> selCon = GetSelectionController(); + if (NS_WARN_IF(!selCon)) { return NS_ERROR_NOT_INITIALIZED; } - NS_ADDREF(*aSel = selCon); + selCon.forget(aSel); return NS_OK; } +already_AddRefed<nsISelectionController> +EditorBase::GetSelectionController() +{ + nsCOMPtr<nsISelectionController> selectionController; + if (mSelectionControllerWeak) { + selectionController = mSelectionControllerWeak.get(); + } else { + nsCOMPtr<nsIPresShell> presShell = GetPresShell(); + selectionController = do_QueryInterface(presShell); + } + return selectionController.forget(); +} + NS_IMETHODIMP EditorBase::DeleteSelection(EDirection aAction, EStripWrappers aStripWrappers) @@ -663,8 +671,7 @@ EditorBase::GetSelection(SelectionType aSelectionType, { NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER); *aSelection = nullptr; - nsCOMPtr<nsISelectionController> selcon; - GetSelectionController(getter_AddRefs(selcon)); + nsCOMPtr<nsISelectionController> selcon = GetSelectionController(); if (!selcon) { return NS_ERROR_NOT_INITIALIZED; } @@ -686,32 +693,29 @@ EditorBase::GetSelection(SelectionType aSelectionType) NS_IMETHODIMP EditorBase::DoTransaction(nsITransaction* aTxn) { - if (mPlaceHolderBatch && !mPlaceHolderTxn) { - nsCOMPtr<nsIAbsorbingTransaction> placeholderTransaction = - new PlaceholderTransaction(); + if (mPlaceholderBatch && !mPlaceholderTransactionWeak) { + RefPtr<PlaceholderTransaction> placeholderTransaction = + new PlaceholderTransaction(*this, mPlaceholderName, Move(mSelState)); // Save off weak reference to placeholder transaction - mPlaceHolderTxn = do_GetWeakReference(placeholderTransaction); - placeholderTransaction->Init(mPlaceHolderName, mSelState, this); - // placeholder txn took ownership of this pointer - mSelState = nullptr; - - // QI to an nsITransaction since that's what DoTransaction() expects - nsCOMPtr<nsITransaction> transaction = - do_QueryInterface(placeholderTransaction); + mPlaceholderTransactionWeak = placeholderTransaction; + // We will recurse, but will not hit this case in the nested call - DoTransaction(transaction); + DoTransaction(placeholderTransaction); if (mTxnMgr) { - nsCOMPtr<nsITransaction> topTxn = mTxnMgr->PeekUndoStack(); - if (topTxn) { - placeholderTransaction = do_QueryInterface(topTxn); - if (placeholderTransaction) { + nsCOMPtr<nsITransaction> topTransaction = mTxnMgr->PeekUndoStack(); + nsCOMPtr<nsIAbsorbingTransaction> topAbsorbingTransaction = + do_QueryInterface(topTransaction); + if (topAbsorbingTransaction) { + RefPtr<PlaceholderTransaction> topPlaceholderTransaction = + topAbsorbingTransaction->AsPlaceholderTransaction(); + if (topPlaceholderTransaction) { // there is a placeholder transaction on top of the undo stack. It // is either the one we just created, or an earlier one that we are // now merging into. From here on out remember this placeholder // instead of the one we just created. - mPlaceHolderTxn = do_GetWeakReference(placeholderTransaction); + mPlaceholderTransactionWeak = topPlaceholderTransaction; } } } @@ -935,28 +939,28 @@ EditorBase::EndTransaction() NS_IMETHODIMP EditorBase::BeginPlaceHolderTransaction(nsIAtom* aName) { - NS_PRECONDITION(mPlaceHolderBatch >= 0, "negative placeholder batch count!"); - if (!mPlaceHolderBatch) { + MOZ_ASSERT(mPlaceholderBatch >= 0, "negative placeholder batch count!"); + if (!mPlaceholderBatch) { NotifyEditorObservers(eNotifyEditorObserversOfBefore); // time to turn on the batch BeginUpdateViewBatch(); - mPlaceHolderTxn = nullptr; - mPlaceHolderName = aName; + mPlaceholderTransactionWeak = nullptr; + mPlaceholderName = aName; RefPtr<Selection> selection = GetSelection(); if (selection) { - mSelState = new SelectionState(); + mSelState = MakeUnique<SelectionState>(); mSelState->SaveSelection(selection); // Composition transaction can modify multiple nodes and it merges text // node for ime into single text node. // So if current selection is into IME text node, it might be failed // to restore selection by UndoTransaction. // So we need update selection by range updater. - if (mPlaceHolderName == nsGkAtoms::IMETxnName) { + if (mPlaceholderName == nsGkAtoms::IMETxnName) { mRangeUpdater.RegisterSelectionState(*mSelState); } } } - mPlaceHolderBatch++; + mPlaceholderBatch++; return NS_OK; } @@ -964,8 +968,9 @@ EditorBase::BeginPlaceHolderTransaction(nsIAtom* aName) NS_IMETHODIMP EditorBase::EndPlaceHolderTransaction() { - NS_PRECONDITION(mPlaceHolderBatch > 0, "zero or negative placeholder batch count when ending batch!"); - if (mPlaceHolderBatch == 1) { + MOZ_ASSERT(mPlaceholderBatch > 0, + "zero or negative placeholder batch count when ending batch!"); + if (mPlaceholderBatch == 1) { RefPtr<Selection> selection = GetSelection(); // By making the assumption that no reflow happens during the calls @@ -1005,22 +1010,16 @@ EditorBase::EndPlaceHolderTransaction() if (mSelState) { // we saved the selection state, but never got to hand it to placeholder // (else we ould have nulled out this pointer), so destroy it to prevent leaks. - if (mPlaceHolderName == nsGkAtoms::IMETxnName) { + if (mPlaceholderName == nsGkAtoms::IMETxnName) { mRangeUpdater.DropSelectionState(*mSelState); } - delete mSelState; mSelState = nullptr; } // We might have never made a placeholder if no action took place. - if (mPlaceHolderTxn) { - nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryReferent(mPlaceHolderTxn); - if (plcTxn) { - plcTxn->EndPlaceHolderBatch(); - } else { - // in the future we will check to make sure undo is off here, - // since that is the only known case where the placeholdertxn would disappear on us. - // For now just removing the assert. - } + if (mPlaceholderTransactionWeak) { + RefPtr<PlaceholderTransaction> placeholderTransaction = + mPlaceholderTransactionWeak.get(); + placeholderTransaction->EndPlaceHolderBatch(); // notify editor observers of action but if composing, it's done by // compositionchange event handler. if (!mComposition) { @@ -1030,7 +1029,7 @@ EditorBase::EndPlaceHolderTransaction() NotifyEditorObservers(eNotifyEditorObserversOfCancel); } } - mPlaceHolderBatch--; + mPlaceholderBatch--; return NS_OK; } @@ -1067,7 +1066,8 @@ EditorBase::GetDocumentIsEmpty(bool* aDocumentIsEmpty) NS_IMETHODIMP EditorBase::SelectAll() { - if (!mDocWeak) { + // XXX Why doesn't this check if the document is alive? + if (!IsInitialized()) { return NS_ERROR_NOT_INITIALIZED; } ForceCompositionEnd(); @@ -1080,7 +1080,8 @@ EditorBase::SelectAll() NS_IMETHODIMP EditorBase::BeginningOfDocument() { - if (!mDocWeak) { + // XXX Why doesn't this check if the document is alive? + if (!IsInitialized()) { return NS_ERROR_NOT_INITIALIZED; } @@ -1117,7 +1118,10 @@ EditorBase::BeginningOfDocument() NS_IMETHODIMP EditorBase::EndOfDocument() { - NS_ENSURE_TRUE(mDocWeak, NS_ERROR_NOT_INITIALIZED); + // XXX Why doesn't this check if the document is alive? + if (NS_WARN_IF(!IsInitialized())) { + return NS_ERROR_NOT_INITIALIZED; + } // get selection RefPtr<Selection> selection = GetSelection(); @@ -1152,20 +1156,22 @@ EditorBase::GetDocumentModified(bool* outDocModified) NS_IMETHODIMP EditorBase::GetDocumentCharacterSet(nsACString& characterSet) { - nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak); - NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED); - - characterSet = doc->GetDocumentCharacterSet(); + nsCOMPtr<nsIDocument> document = GetDocument(); + if (NS_WARN_IF(!document)) { + return NS_ERROR_UNEXPECTED; + } + characterSet = document->GetDocumentCharacterSet(); return NS_OK; } NS_IMETHODIMP EditorBase::SetDocumentCharacterSet(const nsACString& characterSet) { - nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak); - NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED); - - doc->SetDocumentCharacterSet(characterSet); + nsCOMPtr<nsIDocument> document = GetDocument(); + if (NS_WARN_IF(!document)) { + return NS_ERROR_UNEXPECTED; + } + document->SetDocumentCharacterSet(characterSet); return NS_OK; } @@ -1385,9 +1391,12 @@ EditorBase::CreateNode(nsIAtom* aTag, AutoRules beginRulesSniffing(this, EditAction::createNode, nsIEditor::eNext); - for (auto& listener : mActionListeners) { - listener->WillCreateNode(nsDependentAtomString(aTag), - GetAsDOMNode(aParent), aPosition); + { + AutoActionListenerArray listeners(mActionListeners); + for (auto& listener : listeners) { + listener->WillCreateNode(nsDependentAtomString(aTag), + GetAsDOMNode(aParent), aPosition); + } } nsCOMPtr<Element> ret; @@ -1402,9 +1411,12 @@ EditorBase::CreateNode(nsIAtom* aTag, mRangeUpdater.SelAdjCreateNode(aParent, aPosition); - for (auto& listener : mActionListeners) { - listener->DidCreateNode(nsDependentAtomString(aTag), GetAsDOMNode(ret), - GetAsDOMNode(aParent), aPosition, rv); + { + AutoActionListenerArray listeners(mActionListeners); + for (auto& listener : listeners) { + listener->DidCreateNode(nsDependentAtomString(aTag), GetAsDOMNode(ret), + GetAsDOMNode(aParent), aPosition, rv); + } } return ret.forget(); @@ -1429,9 +1441,12 @@ EditorBase::InsertNode(nsIContent& aNode, { AutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext); - for (auto& listener : mActionListeners) { - listener->WillInsertNode(aNode.AsDOMNode(), aParent.AsDOMNode(), - aPosition); + { + AutoActionListenerArray listeners(mActionListeners); + for (auto& listener : listeners) { + listener->WillInsertNode(aNode.AsDOMNode(), aParent.AsDOMNode(), + aPosition); + } } RefPtr<InsertNodeTransaction> transaction = @@ -1440,9 +1455,12 @@ EditorBase::InsertNode(nsIContent& aNode, mRangeUpdater.SelAdjInsertNode(aParent.AsDOMNode(), aPosition); - for (auto& listener : mActionListeners) { - listener->DidInsertNode(aNode.AsDOMNode(), aParent.AsDOMNode(), aPosition, - rv); + { + AutoActionListenerArray listeners(mActionListeners); + for (auto& listener : listeners) { + listener->DidInsertNode(aNode.AsDOMNode(), aParent.AsDOMNode(), aPosition, + rv); + } } return rv; @@ -1468,8 +1486,11 @@ EditorBase::SplitNode(nsIContent& aNode, { AutoRules beginRulesSniffing(this, EditAction::splitNode, nsIEditor::eNext); - for (auto& listener : mActionListeners) { - listener->WillSplitNode(aNode.AsDOMNode(), aOffset); + { + AutoActionListenerArray listeners(mActionListeners); + for (auto& listener : listeners) { + listener->WillSplitNode(aNode.AsDOMNode(), aOffset); + } } RefPtr<SplitNodeTransaction> transaction = @@ -1482,9 +1503,12 @@ EditorBase::SplitNode(nsIContent& aNode, mRangeUpdater.SelAdjSplitNode(aNode, aOffset, newNode); nsresult rv = aResult.StealNSResult(); - for (auto& listener : mActionListeners) { - listener->DidSplitNode(aNode.AsDOMNode(), aOffset, GetAsDOMNode(newNode), - rv); + { + AutoActionListenerArray listeners(mActionListeners); + for (auto& listener : listeners) { + listener->DidSplitNode(aNode.AsDOMNode(), aOffset, GetAsDOMNode(newNode), + rv); + } } // Note: result might be a success code, so we can't use Throw() to // set it on aResult. @@ -1520,9 +1544,12 @@ EditorBase::JoinNodes(nsINode& aLeftNode, // Find the number of children of the lefthand node uint32_t oldLeftNodeLen = aLeftNode.Length(); - for (auto& listener : mActionListeners) { - listener->WillJoinNodes(aLeftNode.AsDOMNode(), aRightNode.AsDOMNode(), - parent->AsDOMNode()); + { + AutoActionListenerArray listeners(mActionListeners); + for (auto& listener : listeners) { + listener->WillJoinNodes(aLeftNode.AsDOMNode(), aRightNode.AsDOMNode(), + parent->AsDOMNode()); + } } nsresult rv = NS_OK; @@ -1535,9 +1562,12 @@ EditorBase::JoinNodes(nsINode& aLeftNode, mRangeUpdater.SelAdjJoinNodes(aLeftNode, aRightNode, *parent, offset, (int32_t)oldLeftNodeLen); - for (auto& listener : mActionListeners) { - listener->DidJoinNodes(aLeftNode.AsDOMNode(), aRightNode.AsDOMNode(), - parent->AsDOMNode(), rv); + { + AutoActionListenerArray listeners(mActionListeners); + for (auto& listener : listeners) { + listener->DidJoinNodes(aLeftNode.AsDOMNode(), aRightNode.AsDOMNode(), + parent->AsDOMNode(), rv); + } } return rv; @@ -1558,8 +1588,11 @@ EditorBase::DeleteNode(nsINode* aNode) nsIEditor::ePrevious); // save node location for selection updating code. - for (auto& listener : mActionListeners) { - listener->WillDeleteNode(aNode->AsDOMNode()); + { + AutoActionListenerArray listeners(mActionListeners); + for (auto& listener : listeners) { + listener->WillDeleteNode(aNode->AsDOMNode()); + } } RefPtr<DeleteNodeTransaction> transaction; @@ -1568,8 +1601,11 @@ EditorBase::DeleteNode(nsINode* aNode) rv = DoTransaction(transaction); } - for (auto& listener : mActionListeners) { - listener->DidDeleteNode(aNode->AsDOMNode(), rv); + { + AutoActionListenerArray listeners(mActionListeners); + for (auto& listener : listeners) { + listener->DidDeleteNode(aNode->AsDOMNode(), rv); + } } NS_ENSURE_SUCCESS(rv, rv); @@ -1844,7 +1880,7 @@ void EditorBase::NotifyEditorObservers(NotificationForEditorObservers aNotification) { // Copy the observers since EditAction()s can modify mEditorObservers. - nsTArray<mozilla::OwningNonNull<nsIEditorObserver>> observers(mEditorObservers); + AutoEditorObserverArray observers(mEditorObservers); switch (aNotification) { case eNotifyEditorObserversOfEnd: mIsInEditAction = false; @@ -1976,12 +2012,17 @@ NS_IMETHODIMP EditorBase::DebugDumpContent() { #ifdef DEBUG - nsCOMPtr<nsIDOMHTMLDocument> doc = do_QueryReferent(mDocWeak); - NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED); - - nsCOMPtr<nsIDOMHTMLElement>bodyElem; - doc->GetBody(getter_AddRefs(bodyElem)); - nsCOMPtr<nsIContent> content = do_QueryInterface(bodyElem); + nsCOMPtr<nsIDocument> document = GetDocument(); + if (NS_WARN_IF(!document)) { + return NS_ERROR_NOT_INITIALIZED; + } + nsCOMPtr<nsIDOMHTMLDocument> domHTMLDocument = do_QueryInterface(document); + if (NS_WARN_IF(!domHTMLDocument)) { + return NS_ERROR_NOT_INITIALIZED; + } + nsCOMPtr<nsIDOMHTMLElement> bodyElement; + domHTMLDocument->GetBody(getter_AddRefs(bodyElement)); + nsCOMPtr<nsIContent> content = do_QueryInterface(bodyElement); if (content) { content->List(); } @@ -2295,18 +2336,20 @@ EditorBase::CloneAttributes(Element* aDest, NS_IMETHODIMP EditorBase::ScrollSelectionIntoView(bool aScrollToAnchor) { - nsCOMPtr<nsISelectionController> selCon; - if (NS_SUCCEEDED(GetSelectionController(getter_AddRefs(selCon))) && selCon) { - int16_t region = nsISelectionController::SELECTION_FOCUS_REGION; - - if (aScrollToAnchor) { - region = nsISelectionController::SELECTION_ANCHOR_REGION; - } - - selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, - region, nsISelectionController::SCROLL_OVERFLOW_HIDDEN); + nsCOMPtr<nsISelectionController> selectionController = + GetSelectionController(); + if (!selectionController) { + return NS_OK; } + int16_t region = nsISelectionController::SELECTION_FOCUS_REGION; + if (aScrollToAnchor) { + region = nsISelectionController::SELECTION_ANCHOR_REGION; + } + selectionController->ScrollSelectionIntoView( + nsISelectionController::SELECTION_NORMAL, + region, + nsISelectionController::SCROLL_OVERFLOW_HIDDEN); return NS_OK; } @@ -2505,10 +2548,13 @@ EditorBase::InsertTextIntoTextNodeImpl(const nsAString& aStringToInsert, } // Let listeners know what's up - for (auto& listener : mActionListeners) { - listener->WillInsertText( - static_cast<nsIDOMCharacterData*>(insertedTextNode->AsDOMNode()), - insertedOffset, aStringToInsert); + { + AutoActionListenerArray listeners(mActionListeners); + for (auto& listener : listeners) { + listener->WillInsertText( + static_cast<nsIDOMCharacterData*>(insertedTextNode->AsDOMNode()), + insertedOffset, aStringToInsert); + } } // XXX We may not need these view batches anymore. This is handled at a @@ -2518,10 +2564,13 @@ EditorBase::InsertTextIntoTextNodeImpl(const nsAString& aStringToInsert, EndUpdateViewBatch(); // let listeners know what happened - for (auto& listener : mActionListeners) { - listener->DidInsertText( - static_cast<nsIDOMCharacterData*>(insertedTextNode->AsDOMNode()), - insertedOffset, aStringToInsert, rv); + { + AutoActionListenerArray listeners(mActionListeners); + for (auto& listener : listeners) { + listener->DidInsertText( + static_cast<nsIDOMCharacterData*>(insertedTextNode->AsDOMNode()), + insertedOffset, aStringToInsert, rv); + } } // Added some cruft here for bug 43366. Layout was crashing because we left @@ -2583,8 +2632,7 @@ EditorBase::NotifyDocumentListeners( return NS_OK; } - nsTArray<OwningNonNull<nsIDocumentStateListener>> - listeners(mDocStateListeners); + AutoDocumentStateListenerArray listeners(mDocStateListeners); nsresult rv = NS_OK; switch (aNotificationType) { @@ -2656,19 +2704,25 @@ EditorBase::DeleteText(nsGenericDOMDataNode& aCharData, nsIEditor::ePrevious); // Let listeners know what's up - for (auto& listener : mActionListeners) { - listener->WillDeleteText( - static_cast<nsIDOMCharacterData*>(GetAsDOMNode(&aCharData)), aOffset, - aLength); + { + AutoActionListenerArray listeners(mActionListeners); + for (auto& listener : listeners) { + listener->WillDeleteText( + static_cast<nsIDOMCharacterData*>(GetAsDOMNode(&aCharData)), aOffset, + aLength); + } } nsresult rv = DoTransaction(transaction); // Let listeners know what happened - for (auto& listener : mActionListeners) { - listener->DidDeleteText( - static_cast<nsIDOMCharacterData*>(GetAsDOMNode(&aCharData)), aOffset, - aLength, rv); + { + AutoActionListenerArray listeners(mActionListeners); + for (auto& listener : listeners) { + listener->DidDeleteText( + static_cast<nsIDOMCharacterData*>(GetAsDOMNode(&aCharData)), aOffset, + aLength, rv); + } } return rv; @@ -4034,17 +4088,20 @@ EditorBase::DeleteSelectionImpl(EDirection aAction, if (NS_SUCCEEDED(rv)) { AutoRules beginRulesSniffing(this, EditAction::deleteSelection, aAction); // Notify nsIEditActionListener::WillDelete[Selection|Text|Node] - if (!deleteNode) { - for (auto& listener : mActionListeners) { - listener->WillDeleteSelection(selection); - } - } else if (deleteCharData) { - for (auto& listener : mActionListeners) { - listener->WillDeleteText(deleteCharData, deleteCharOffset, 1); - } - } else { - for (auto& listener : mActionListeners) { - listener->WillDeleteNode(deleteNode->AsDOMNode()); + { + AutoActionListenerArray listeners(mActionListeners); + if (!deleteNode) { + for (auto& listener : listeners) { + listener->WillDeleteSelection(selection); + } + } else if (deleteCharData) { + for (auto& listener : listeners) { + listener->WillDeleteText(deleteCharData, deleteCharOffset, 1); + } + } else { + for (auto& listener : listeners) { + listener->WillDeleteNode(deleteNode->AsDOMNode()); + } } } @@ -4052,17 +4109,20 @@ EditorBase::DeleteSelectionImpl(EDirection aAction, rv = DoTransaction(transaction); // Notify nsIEditActionListener::DidDelete[Selection|Text|Node] - if (!deleteNode) { - for (auto& listener : mActionListeners) { - listener->DidDeleteSelection(selection); - } - } else if (deleteCharData) { - for (auto& listener : mActionListeners) { - listener->DidDeleteText(deleteCharData, deleteCharOffset, 1, rv); - } - } else { - for (auto& listener : mActionListeners) { - listener->DidDeleteNode(deleteNode->AsDOMNode(), rv); + { + AutoActionListenerArray listeners(mActionListeners); + if (!deleteNode) { + for (auto& listener : mActionListeners) { + listener->DidDeleteSelection(selection); + } + } else if (deleteCharData) { + for (auto& listener : mActionListeners) { + listener->DidDeleteText(deleteCharData, deleteCharOffset, 1, rv); + } + } else { + for (auto& listener : mActionListeners) { + listener->DidDeleteNode(deleteNode->AsDOMNode(), rv); + } } } } @@ -4739,22 +4799,27 @@ EditorBase::InitializeSelection(nsIDOMEventTarget* aFocusEventTarget) nsCOMPtr<nsIPresShell> presShell = GetPresShell(); NS_ENSURE_TRUE(presShell, NS_ERROR_NOT_INITIALIZED); - nsCOMPtr<nsISelectionController> selCon; - nsresult rv = GetSelectionController(getter_AddRefs(selCon)); - NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr<nsISelectionController> selectionController = + GetSelectionController(); + if (NS_WARN_IF(!selectionController)) { + return NS_ERROR_FAILURE; + } // Init the caret RefPtr<nsCaret> caret = presShell->GetCaret(); NS_ENSURE_TRUE(caret, NS_ERROR_UNEXPECTED); caret->SetIgnoreUserModify(false); caret->SetSelection(selection); - selCon->SetCaretReadOnly(IsReadonly()); - selCon->SetCaretEnabled(true); + selectionController->SetCaretReadOnly(IsReadonly()); + selectionController->SetCaretEnabled(true); // Init selection - selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON); - selCon->SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL); - selCon->RepaintSelection(nsISelectionController::SELECTION_NORMAL); + selectionController->SetDisplaySelection( + nsISelectionController::SELECTION_ON); + selectionController->SetSelectionFlags( + nsISelectionDisplay::DISPLAY_ALL); + selectionController->RepaintSelection( + nsISelectionController::SELECTION_NORMAL); // If the computed selection root isn't root content, we should set it // as selection ancestor limit. However, if that is root element, it means // there is not limitation of the selection, then, we must set nullptr. @@ -4822,9 +4887,11 @@ private: NS_IMETHODIMP EditorBase::FinalizeSelection() { - nsCOMPtr<nsISelectionController> selCon; - nsresult rv = GetSelectionController(getter_AddRefs(selCon)); - NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr<nsISelectionController> selectionController = + GetSelectionController(); + if (NS_WARN_IF(!selectionController)) { + return NS_ERROR_FAILURE; + } RefPtr<Selection> selection = GetSelection(); NS_ENSURE_STATE(selection); @@ -4834,7 +4901,7 @@ EditorBase::FinalizeSelection() nsCOMPtr<nsIPresShell> presShell = GetPresShell(); NS_ENSURE_TRUE(presShell, NS_ERROR_NOT_INITIALIZED); - selCon->SetCaretEnabled(false); + selectionController->SetCaretEnabled(false); nsFocusManager* fm = nsFocusManager::GetFocusManager(); NS_ENSURE_TRUE(fm, NS_ERROR_NOT_INITIALIZED); @@ -4849,29 +4916,33 @@ EditorBase::FinalizeSelection() ErrorResult ret; if (!doc || !doc->HasFocus(ret)) { // If the document already lost focus, mark the selection as disabled. - selCon->SetDisplaySelection(nsISelectionController::SELECTION_DISABLED); + selectionController->SetDisplaySelection( + nsISelectionController::SELECTION_DISABLED); } else { // Otherwise, mark selection as normal because outside of a // contenteditable element should be selected with normal selection // color after here. - selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON); + selectionController->SetDisplaySelection( + nsISelectionController::SELECTION_ON); } } else if (IsFormWidget() || IsPasswordEditor() || IsReadonly() || IsDisabled() || IsInputFiltered()) { // In <input> or <textarea>, the independent selection should be hidden // while this editor doesn't have focus. - selCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN); + selectionController->SetDisplaySelection( + nsISelectionController::SELECTION_HIDDEN); } else { // Otherwise, although we're not sure how this case happens, the // independent selection should be marked as disabled. - selCon->SetDisplaySelection(nsISelectionController::SELECTION_DISABLED); + selectionController->SetDisplaySelection( + nsISelectionController::SELECTION_DISABLED); } // FinalizeSelection might be called from ContentRemoved even if selection // isn't updated. So we need to call RepaintSelection after updated it. nsContentUtils::AddScriptRunner( - new RepaintSelectionRunner(selCon)); + new RepaintSelectionRunner(selectionController)); return NS_OK; } @@ -5082,8 +5153,11 @@ EditorBase::IsActiveInDOMWindow() nsFocusManager* fm = nsFocusManager::GetFocusManager(); NS_ENSURE_TRUE(fm, false); - nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak); - nsPIDOMWindowOuter* ourWindow = doc->GetWindow(); + nsCOMPtr<nsIDocument> document = GetDocument(); + if (NS_WARN_IF(!document)) { + return false; + } + nsPIDOMWindowOuter* ourWindow = document->GetWindow(); nsCOMPtr<nsPIDOMWindowOuter> win; nsIContent* content = nsFocusManager::GetFocusedDescendant(ourWindow, false, @@ -5191,12 +5265,13 @@ EditorBase::GetIMESelectionStartOffsetIn(nsINode* aTextNode) { MOZ_ASSERT(aTextNode, "aTextNode must not be nullptr"); - nsCOMPtr<nsISelectionController> selectionController; - nsresult rv = GetSelectionController(getter_AddRefs(selectionController)); - NS_ENSURE_SUCCESS(rv, -1); - NS_ENSURE_TRUE(selectionController, -1); + nsCOMPtr<nsISelectionController> selectionController = + GetSelectionController(); + if (NS_WARN_IF(!selectionController)) { + return -1; + } - int32_t minOffset = INT32_MAX; + uint32_t minOffset = UINT32_MAX; static const SelectionType kIMESelectionTypes[] = { SelectionType::eIMERawClause, SelectionType::eIMESelectedRawClause, @@ -5216,15 +5291,11 @@ EditorBase::GetIMESelectionStartOffsetIn(nsINode* aTextNode) if (NS_WARN_IF(range->GetStartParent() != aTextNode)) { // ignore the start offset... } else { - MOZ_ASSERT(range->StartOffset() >= 0, - "start offset shouldn't be negative"); minOffset = std::min(minOffset, range->StartOffset()); } if (NS_WARN_IF(range->GetEndParent() != aTextNode)) { // ignore the end offset... } else { - MOZ_ASSERT(range->EndOffset() >= 0, - "start offset shouldn't be negative"); minOffset = std::min(minOffset, range->EndOffset()); } } diff --git a/editor/libeditor/EditorBase.h b/editor/libeditor/EditorBase.h index dbd00771ef..618da12a8f 100644 --- a/editor/libeditor/EditorBase.h +++ b/editor/libeditor/EditorBase.h @@ -10,11 +10,14 @@ #include "mozFlushType.h" // for mozFlushType enum #include "mozilla/OwningNonNull.h" // for OwningNonNull #include "mozilla/SelectionState.h" // for RangeUpdater, etc. -#include "mozilla/StyleSheet.h" // for StyleSheet +#include "mozilla/StyleSheet.h" // for StyleSheet +#include "mozilla/UniquePtr.h" +#include "mozilla/WeakPtr.h" // for WeakPtr #include "mozilla/dom/Text.h" #include "nsCOMPtr.h" // for already_AddRefed, nsCOMPtr #include "nsCycleCollectionParticipant.h" #include "nsGkAtoms.h" +#include "nsIDocument.h" // for nsIDocument #include "nsIEditor.h" // for nsIEditor::EDirection, etc. #include "nsIEditorIMESupport.h" // for NS_DECL_NSIEDITORIMESUPPORT, etc. #include "nsIObserver.h" // for NS_DECL_NSIOBSERVER, etc. @@ -30,7 +33,6 @@ class nsIAtom; class nsIContent; -class nsIDOMDocument; class nsIDOMEvent; class nsIDOMEventListener; class nsIDOMEventTarget; @@ -115,6 +117,7 @@ class ErrorResult; class InsertNodeTransaction; class InsertTextTransaction; class JoinNodeTransaction; +class PlaceholderTransaction; class RemoveStyleSheetTransaction; class SplitNodeTransaction; class TextComposition; @@ -132,6 +135,57 @@ namespace widget { struct IMEState; } // namespace widget +/** + * CachedWeakPtr stores a pointer to a class which inherits nsIWeakReference. + * If the instance of the class has already been destroyed, this returns + * nullptr. Otherwise, returns cached pointer. + */ +template<class T> +class CachedWeakPtr final +{ +public: + CachedWeakPtr<T>() + : mCache(nullptr) + { + } + + CachedWeakPtr<T>& operator=(T* aObject) + { + mWeakPtr = do_GetWeakReference(aObject); + mCache = aObject; + return *this; + } + CachedWeakPtr<T>& operator=(const nsCOMPtr<T>& aOther) + { + mWeakPtr = do_GetWeakReference(aOther); + mCache = aOther; + return *this; + } + CachedWeakPtr<T>& operator=(already_AddRefed<T>& aOther) + { + nsCOMPtr<T> other = aOther; + mWeakPtr = do_GetWeakReference(other); + mCache = other; + return *this; + } + + bool IsAlive() const { return mWeakPtr && mWeakPtr->IsAlive(); } + + explicit operator bool() const { return mWeakPtr; } + operator T*() const { return get(); } + T* get() const + { + if (mCache && !mWeakPtr->IsAlive()) { + const_cast<CachedWeakPtr<T>*>(this)->mCache = nullptr; + } + return mCache; + } + +private: + nsWeakPtr mWeakPtr; + T* MOZ_NON_OWNING_REF mCache; +}; + #define kMOZEditorBogusNodeAttrAtom nsGkAtoms::mozeditorbogusnode #define kMOZEditorBogusNodeValue NS_LITERAL_STRING("TRUE") @@ -175,6 +229,7 @@ public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(EditorBase, nsIEditor) + bool IsInitialized() const { return !!mDocumentWeak; } already_AddRefed<nsIDOMDocument> GetDOMDocument(); already_AddRefed<nsIDocument> GetDocument(); already_AddRefed<nsIPresShell> GetPresShell(); @@ -452,6 +507,7 @@ protected: */ bool EnsureComposition(WidgetCompositionEvent* aCompositionEvent); + already_AddRefed<nsISelectionController> GetSelectionController(); nsresult GetSelection(SelectionType aSelectionType, nsISelection** aSelection); @@ -844,7 +900,7 @@ public: bool HasIndependentSelection() const { - return !!mSelConWeak; + return !!mSelectionControllerWeak; } /** @@ -950,6 +1006,14 @@ public: } } +private: + // Weak reference to the nsISelectionController. + // Use GetSelectionController() to retrieve actual pointer. + CachedWeakPtr<nsISelectionController> mSelectionControllerWeak; + // Weak reference to the nsIDocument. + // Use GetDocument() to retrieve actual pointer. + CachedWeakPtr<nsIDocument> mDocumentWeak; + protected: enum Tristate { @@ -971,27 +1035,29 @@ protected: // The form field as an event receiver. nsCOMPtr<dom::EventTarget> mEventTarget; nsCOMPtr<nsIDOMEventListener> mEventListener; - // Weak reference to the nsISelectionController. - nsWeakPtr mSelConWeak; // Weak reference to placeholder for begin/end batch purposes. - nsWeakPtr mPlaceHolderTxn; - // Weak reference to the nsIDOMDocument. - nsWeakPtr mDocWeak; + WeakPtr<PlaceholderTransaction> mPlaceholderTransactionWeak; // Name of placeholder transaction. - nsIAtom* mPlaceHolderName; + nsIAtom* mPlaceholderName; // Saved selection state for placeholder transaction batching. - SelectionState* mSelState; + mozilla::UniquePtr<SelectionState> mSelState; nsString* mPhonetic; // IME composition this is not null between compositionstart and // compositionend. RefPtr<TextComposition> mComposition; // Listens to all low level actions on the doc. - nsTArray<OwningNonNull<nsIEditActionListener>> mActionListeners; + typedef AutoTArray<OwningNonNull<nsIEditActionListener>, 5> + AutoActionListenerArray; + AutoActionListenerArray mActionListeners; // Just notify once per high level change. - nsTArray<OwningNonNull<nsIEditorObserver>> mEditorObservers; + typedef AutoTArray<OwningNonNull<nsIEditorObserver>, 3> + AutoEditorObserverArray; + AutoEditorObserverArray mEditorObservers; // Listen to overall doc state (dirty or not, just created, etc.). - nsTArray<OwningNonNull<nsIDocumentStateListener>> mDocStateListeners; + typedef AutoTArray<OwningNonNull<nsIDocumentStateListener>, 1> + AutoDocumentStateListenerArray; + AutoDocumentStateListenerArray mDocStateListeners; // Cached selection for AutoSelectionRestorer. SelectionState mSavedSel; @@ -1006,7 +1072,7 @@ protected: int32_t mUpdateCount; // Nesting count for batching. - int32_t mPlaceHolderBatch; + int32_t mPlaceholderBatch; // The current editor action. EditAction mAction; diff --git a/editor/libeditor/HTMLEditRules.cpp b/editor/libeditor/HTMLEditRules.cpp index 545e22f701..805092eb78 100644 --- a/editor/libeditor/HTMLEditRules.cpp +++ b/editor/libeditor/HTMLEditRules.cpp @@ -16,12 +16,13 @@ #include "mozilla/EditorUtils.h" #include "mozilla/HTMLEditor.h" #include "mozilla/MathAlgorithms.h" +#include "mozilla/Move.h" #include "mozilla/Preferences.h" +#include "mozilla/UniquePtr.h" #include "mozilla/dom/Selection.h" #include "mozilla/dom/Element.h" #include "mozilla/OwningNonNull.h" #include "mozilla/mozalloc.h" -#include "nsAutoPtr.h" #include "nsAString.h" #include "nsAlgorithm.h" #include "nsCRT.h" @@ -426,7 +427,7 @@ HTMLEditRules::AfterEditInner(EditAction action, NS_ENSURE_STATE(selection); nsCOMPtr<nsIDOMNode> rangeStartParent, rangeEndParent; - int32_t rangeStartOffset = 0, rangeEndOffset = 0; + uint32_t rangeStartOffset = 0, rangeEndOffset = 0; // do we have a real range to act on? bool bDamagedRange = false; if (mDocChangeRange) { @@ -534,8 +535,8 @@ HTMLEditRules::AfterEditInner(EditAction action, mHTMLEditor->HandleInlineSpellCheck(action, selection, GetAsDOMNode(mRangeItem->startNode), mRangeItem->startOffset, - rangeStartParent, rangeStartOffset, - rangeEndParent, rangeEndOffset); + rangeStartParent, static_cast<int32_t>(rangeStartOffset), + rangeEndParent, static_cast<int32_t>(rangeEndOffset)); NS_ENSURE_SUCCESS(rv, rv); // detect empty doc @@ -1422,16 +1423,15 @@ HTMLEditRules::WillInsertText(EditAction aAction, if (!mDocChangeRange) { mDocChangeRange = new nsRange(selNode); } - rv = mDocChangeRange->SetStart(selNode, selOffset); - NS_ENSURE_SUCCESS(rv, rv); if (curNode) { - rv = mDocChangeRange->SetEnd(curNode, curOffset); + rv = mDocChangeRange->SetStartAndEnd(selNode, selOffset, + curNode, curOffset); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } else { - rv = mDocChangeRange->SetEnd(selNode, selOffset); + rv = mDocChangeRange->CollapseTo(selNode, selOffset); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -4411,20 +4411,21 @@ HTMLEditRules::CreateStyleForInsertText(Selection& aSelection, NS_ENSURE_STATE(rootElement); // process clearing any styles first - nsAutoPtr<PropItem> item(mHTMLEditor->mTypeInState->TakeClearProperty()); + UniquePtr<PropItem> item = + Move(mHTMLEditor->mTypeInState->TakeClearProperty()); while (item && node != rootElement) { NS_ENSURE_STATE(mHTMLEditor); nsresult rv = mHTMLEditor->ClearStyle(address_of(node), &offset, item->tag, &item->attr); NS_ENSURE_SUCCESS(rv, rv); - item = mHTMLEditor->mTypeInState->TakeClearProperty(); + item = Move(mHTMLEditor->mTypeInState->TakeClearProperty()); weDidSomething = true; } // then process setting any styles int32_t relFontSize = mHTMLEditor->mTypeInState->TakeRelativeFontSize(); - item = mHTMLEditor->mTypeInState->TakeSetProperty(); + item = Move(mHTMLEditor->mTypeInState->TakeSetProperty()); if (item || relFontSize) { // we have at least one style to add; make a new text node to insert style @@ -5093,10 +5094,11 @@ HTMLEditRules::ExpandSelectionForDeletion(Selection& aSelection) // Create a range that represents expanded selection RefPtr<nsRange> range = new nsRange(selStartNode); - nsresult rv = range->SetStart(selStartNode, selStartOffset); - NS_ENSURE_SUCCESS(rv, rv); - rv = range->SetEnd(selEndNode, selEndOffset); - NS_ENSURE_SUCCESS(rv, rv); + nsresult rv = range->SetStartAndEnd(selStartNode, selStartOffset, + selEndNode, selEndOffset); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } // Check if block is entirely inside range if (brBlock) { @@ -5149,9 +5151,8 @@ HTMLEditRules::NormalizeSelection(Selection* inSelection) RefPtr<nsRange> range = inSelection->GetRangeAt(0); NS_ENSURE_TRUE(range, NS_ERROR_NULL_POINTER); nsCOMPtr<nsIDOMNode> startNode, endNode; - int32_t startOffset, endOffset; + uint32_t startOffset, endOffset; nsCOMPtr<nsIDOMNode> newStartNode, newEndNode; - int32_t newStartOffset, newEndOffset; rv = range->GetStartContainer(getter_AddRefs(startNode)); NS_ENSURE_SUCCESS(rv, rv); @@ -5164,22 +5165,22 @@ HTMLEditRules::NormalizeSelection(Selection* inSelection) // adjusted values default to original values newStartNode = startNode; - newStartOffset = startOffset; + uint32_t newStartOffset = startOffset; newEndNode = endNode; - newEndOffset = endOffset; + uint32_t newEndOffset = endOffset; // some locals we need for whitespace code nsCOMPtr<nsINode> unused; - int32_t offset; + int32_t offset = -1; WSType wsType; // let the whitespace code do the heavy lifting - WSRunObject wsEndObj(mHTMLEditor, endNode, endOffset); + WSRunObject wsEndObj(mHTMLEditor, endNode, static_cast<int32_t>(endOffset)); // is there any intervening visible whitespace? if so we can't push selection past that, // it would visibly change maening of users selection nsCOMPtr<nsINode> endNode_(do_QueryInterface(endNode)); - wsEndObj.PriorVisibleNode(endNode_, endOffset, address_of(unused), - &offset, &wsType); + wsEndObj.PriorVisibleNode(endNode_, static_cast<int32_t>(endOffset), + address_of(unused), &offset, &wsType); if (wsType != WSType::text && wsType != WSType::normalWS) { // eThisBlock and eOtherBlock conveniently distinquish cases // of going "down" into a block and "up" out of a block. @@ -5189,36 +5190,44 @@ HTMLEditRules::NormalizeSelection(Selection* inSelection) GetAsDOMNode(mHTMLEditor->GetRightmostChild(wsEndObj.mStartReasonNode, true)); if (child) { - newEndNode = EditorBase::GetNodeLocation(child, &newEndOffset); - ++newEndOffset; // offset *after* child + int32_t offset = -1; + newEndNode = EditorBase::GetNodeLocation(child, &offset); + // offset *after* child + newEndOffset = static_cast<uint32_t>(offset + 1); } // else block is empty - we can leave selection alone here, i think. } else if (wsEndObj.mStartReason == WSType::thisBlock) { // endpoint is just after start of this block nsCOMPtr<nsIDOMNode> child; NS_ENSURE_STATE(mHTMLEditor); - mHTMLEditor->GetPriorHTMLNode(endNode, endOffset, address_of(child)); + mHTMLEditor->GetPriorHTMLNode(endNode, static_cast<int32_t>(endOffset), + address_of(child)); if (child) { - newEndNode = EditorBase::GetNodeLocation(child, &newEndOffset); - ++newEndOffset; // offset *after* child + int32_t offset = -1; + newEndNode = EditorBase::GetNodeLocation(child, &offset); + // offset *after* child + newEndOffset = static_cast<uint32_t>(offset + 1); } // else block is empty - we can leave selection alone here, i think. } else if (wsEndObj.mStartReason == WSType::br) { // endpoint is just after break. lets adjust it to before it. + int32_t offset = -1; newEndNode = EditorBase::GetNodeLocation(GetAsDOMNode(wsEndObj.mStartReasonNode), - &newEndOffset); + &offset); + newEndOffset = static_cast<uint32_t>(offset);; } } // similar dealio for start of range - WSRunObject wsStartObj(mHTMLEditor, startNode, startOffset); + WSRunObject wsStartObj(mHTMLEditor, startNode, + static_cast<int32_t>(startOffset)); // is there any intervening visible whitespace? if so we can't push selection past that, // it would visibly change maening of users selection nsCOMPtr<nsINode> startNode_(do_QueryInterface(startNode)); - wsStartObj.NextVisibleNode(startNode_, startOffset, address_of(unused), - &offset, &wsType); + wsStartObj.NextVisibleNode(startNode_, static_cast<int32_t>(startOffset), + address_of(unused), &offset, &wsType); if (wsType != WSType::text && wsType != WSType::normalWS) { // eThisBlock and eOtherBlock conveniently distinquish cases // of going "down" into a block and "up" out of a block. @@ -5228,23 +5237,31 @@ HTMLEditRules::NormalizeSelection(Selection* inSelection) GetAsDOMNode(mHTMLEditor->GetLeftmostChild(wsStartObj.mEndReasonNode, true)); if (child) { - newStartNode = EditorBase::GetNodeLocation(child, &newStartOffset); + int32_t offset = -1; + newStartNode = EditorBase::GetNodeLocation(child, &offset); + newStartOffset = static_cast<uint32_t>(offset); } // else block is empty - we can leave selection alone here, i think. } else if (wsStartObj.mEndReason == WSType::thisBlock) { // startpoint is just before end of this block nsCOMPtr<nsIDOMNode> child; NS_ENSURE_STATE(mHTMLEditor); - mHTMLEditor->GetNextHTMLNode(startNode, startOffset, address_of(child)); + mHTMLEditor->GetNextHTMLNode(startNode, static_cast<int32_t>(startOffset), + address_of(child)); if (child) { - newStartNode = EditorBase::GetNodeLocation(child, &newStartOffset); + int32_t offset = -1; + newStartNode = EditorBase::GetNodeLocation(child, &offset); + newStartOffset = static_cast<uint32_t>(offset); } // else block is empty - we can leave selection alone here, i think. } else if (wsStartObj.mEndReason == WSType::br) { // startpoint is just before a break. lets adjust it to after it. + int32_t offset = -1; newStartNode = EditorBase::GetNodeLocation(GetAsDOMNode(wsStartObj.mEndReasonNode), - &newStartOffset); + &offset); + // offset *after* break + newStartOffset = static_cast<uint32_t>(offset + 1); ++newStartOffset; // offset *after* break } } @@ -5558,26 +5575,27 @@ HTMLEditRules::PromoteRange(nsRange& aRange, // This is tricky. The basic idea is to push out the range endpoints to // truly enclose the blocks that we will affect. - nsCOMPtr<nsIDOMNode> opStartNode; - nsCOMPtr<nsIDOMNode> opEndNode; + nsCOMPtr<nsIDOMNode> opDOMStartNode; + nsCOMPtr<nsIDOMNode> opDOMEndNode; int32_t opStartOffset, opEndOffset; GetPromotedPoint(kStart, GetAsDOMNode(startNode), startOffset, - aOperationType, address_of(opStartNode), &opStartOffset); + aOperationType, address_of(opDOMStartNode), &opStartOffset); GetPromotedPoint(kEnd, GetAsDOMNode(endNode), endOffset, aOperationType, - address_of(opEndNode), &opEndOffset); + address_of(opDOMEndNode), &opEndOffset); // Make sure that the new range ends up to be in the editable section. if (!htmlEditor->IsDescendantOfEditorRoot( - EditorBase::GetNodeAtRangeOffsetPoint(opStartNode, opStartOffset)) || + EditorBase::GetNodeAtRangeOffsetPoint(opDOMStartNode, opStartOffset)) || !htmlEditor->IsDescendantOfEditorRoot( - EditorBase::GetNodeAtRangeOffsetPoint(opEndNode, opEndOffset - 1))) { + EditorBase::GetNodeAtRangeOffsetPoint(opDOMEndNode, opEndOffset - 1))) { return; } - DebugOnly<nsresult> rv = aRange.SetStart(opStartNode, opStartOffset); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - rv = aRange.SetEnd(opEndNode, opEndOffset); + nsCOMPtr<nsINode> opStartNode = do_QueryInterface(opDOMStartNode); + nsCOMPtr<nsINode> opEndNode = do_QueryInterface(opDOMEndNode); + DebugOnly<nsresult> rv = + aRange.SetStartAndEnd(opStartNode, opStartOffset, opEndNode, opEndOffset); MOZ_ASSERT(NS_SUCCEEDED(rv)); } @@ -7179,42 +7197,44 @@ HTMLEditRules::PinSelectionToNewBlock(Selection* aSelection) return NS_OK; } + if (NS_WARN_IF(!mNewBlock)) { + return NS_ERROR_NULL_POINTER; + } + // get the (collapsed) selection location - nsCOMPtr<nsIDOMNode> selNode, temp; + nsCOMPtr<nsIDOMNode> selNode; int32_t selOffset; NS_ENSURE_STATE(mHTMLEditor); nsresult rv = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset); NS_ENSURE_SUCCESS(rv, rv); - temp = selNode; // use ranges and sRangeHelper to compare sel point to new block nsCOMPtr<nsINode> node = do_QueryInterface(selNode); NS_ENSURE_STATE(node); RefPtr<nsRange> range = new nsRange(node); - rv = range->SetStart(selNode, selOffset); - NS_ENSURE_SUCCESS(rv, rv); - rv = range->SetEnd(selNode, selOffset); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr<nsIContent> block = mNewBlock.get(); - NS_ENSURE_TRUE(block, NS_ERROR_NO_INTERFACE); + rv = range->CollapseTo(node, selOffset); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } bool nodeBefore, nodeAfter; - rv = nsRange::CompareNodeToRange(block, range, &nodeBefore, &nodeAfter); + rv = nsRange::CompareNodeToRange(mNewBlock, range, &nodeBefore, &nodeAfter); NS_ENSURE_SUCCESS(rv, rv); if (nodeBefore && nodeAfter) { return NS_OK; // selection is inside block } else if (nodeBefore) { // selection is after block. put at end of block. - nsCOMPtr<nsIDOMNode> tmp = GetAsDOMNode(mNewBlock); NS_ENSURE_STATE(mHTMLEditor); - tmp = GetAsDOMNode(mHTMLEditor->GetLastEditableChild(*block)); + nsCOMPtr<nsINode> tmp = mHTMLEditor->GetLastEditableChild(*mNewBlock); + if (!tmp) { + tmp = mNewBlock; + } uint32_t endPoint; if (mHTMLEditor->IsTextNode(tmp) || mHTMLEditor->IsContainer(tmp)) { - rv = EditorBase::GetLengthOfDOMNode(tmp, endPoint); - NS_ENSURE_SUCCESS(rv, rv); + endPoint = tmp->Length(); } else { tmp = EditorBase::GetNodeLocation(tmp, (int32_t*)&endPoint); endPoint++; // want to be after this node @@ -7222,9 +7242,11 @@ HTMLEditRules::PinSelectionToNewBlock(Selection* aSelection) return aSelection->Collapse(tmp, (int32_t)endPoint); } else { // selection is before block. put at start of block. - nsCOMPtr<nsIDOMNode> tmp = GetAsDOMNode(mNewBlock); NS_ENSURE_STATE(mHTMLEditor); - tmp = GetAsDOMNode(mHTMLEditor->GetFirstEditableChild(*block)); + nsCOMPtr<nsINode> tmp = mHTMLEditor->GetFirstEditableChild(*mNewBlock); + if (!tmp) { + tmp = mNewBlock; + } int32_t offset; if (mHTMLEditor->IsTextNode(tmp) || mHTMLEditor->IsContainer(tmp)) { @@ -7967,7 +7989,7 @@ HTMLEditRules::UpdateDocChangeRange(nsRange* aRange) NS_ENSURE_SUCCESS(rv, rv); // Positive result means mDocChangeRange start is after aRange start. if (result > 0) { - int32_t startOffset; + uint32_t startOffset; rv = aRange->GetStartOffset(&startOffset); NS_ENSURE_SUCCESS(rv, rv); rv = mDocChangeRange->SetStart(startNode, startOffset); @@ -7981,9 +8003,9 @@ HTMLEditRules::UpdateDocChangeRange(nsRange* aRange) // Negative result means mDocChangeRange end is before aRange end. if (result < 0) { nsCOMPtr<nsIDOMNode> endNode; - int32_t endOffset; rv = aRange->GetEndContainer(getter_AddRefs(endNode)); NS_ENSURE_SUCCESS(rv, rv); + uint32_t endOffset; rv = aRange->GetEndOffset(&endOffset); NS_ENSURE_SUCCESS(rv, rv); rv = mDocChangeRange->SetEnd(endNode, endOffset); @@ -8091,10 +8113,13 @@ HTMLEditRules::DidSplitNode(nsIDOMNode* aExistingRightNode, if (!mListenerEnabled) { return NS_OK; } - nsresult rv = mUtilRange->SetStart(aNewLeftNode, 0); - NS_ENSURE_SUCCESS(rv, rv); - rv = mUtilRange->SetEnd(aExistingRightNode, 0); - NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr<nsINode> newLeftNode = do_QueryInterface(aNewLeftNode); + nsCOMPtr<nsINode> existingRightNode = do_QueryInterface(aExistingRightNode); + nsresult rv = mUtilRange->SetStartAndEnd(newLeftNode, 0, + existingRightNode, 0); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } return UpdateDocChangeRange(mUtilRange); } @@ -8119,11 +8144,12 @@ HTMLEditRules::DidJoinNodes(nsIDOMNode* aLeftNode, if (!mListenerEnabled) { return NS_OK; } + nsCOMPtr<nsINode> rightNode = do_QueryInterface(aRightNode); // assumption that Join keeps the righthand node - nsresult rv = mUtilRange->SetStart(aRightNode, mJoinOffset); - NS_ENSURE_SUCCESS(rv, rv); - rv = mUtilRange->SetEnd(aRightNode, mJoinOffset); - NS_ENSURE_SUCCESS(rv, rv); + nsresult rv = mUtilRange->CollapseTo(rightNode, mJoinOffset); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } return UpdateDocChangeRange(mUtilRange); } @@ -8145,11 +8171,12 @@ HTMLEditRules::DidInsertText(nsIDOMCharacterData* aTextNode, return NS_OK; } int32_t length = aString.Length(); - nsCOMPtr<nsIDOMNode> theNode = do_QueryInterface(aTextNode); - nsresult rv = mUtilRange->SetStart(theNode, aOffset); - NS_ENSURE_SUCCESS(rv, rv); - rv = mUtilRange->SetEnd(theNode, aOffset+length); - NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr<nsINode> theNode = do_QueryInterface(aTextNode); + nsresult rv = mUtilRange->SetStartAndEnd(theNode, aOffset, + theNode, aOffset + length); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } return UpdateDocChangeRange(mUtilRange); } @@ -8170,11 +8197,11 @@ HTMLEditRules::DidDeleteText(nsIDOMCharacterData* aTextNode, if (!mListenerEnabled) { return NS_OK; } - nsCOMPtr<nsIDOMNode> theNode = do_QueryInterface(aTextNode); - nsresult rv = mUtilRange->SetStart(theNode, aOffset); - NS_ENSURE_SUCCESS(rv, rv); - rv = mUtilRange->SetEnd(theNode, aOffset); - NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr<nsINode> theNode = do_QueryInterface(aTextNode); + nsresult rv = mUtilRange->CollapseTo(theNode, aOffset); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } return UpdateDocChangeRange(mUtilRange); } @@ -8189,22 +8216,27 @@ HTMLEditRules::WillDeleteSelection(nsISelection* aSelection) } RefPtr<Selection> selection = aSelection->AsSelection(); // get the (collapsed) selection location - nsCOMPtr<nsIDOMNode> selNode; - int32_t selOffset; - + nsCOMPtr<nsINode> startNode; + int32_t startOffset; NS_ENSURE_STATE(mHTMLEditor); nsresult rv = mHTMLEditor->GetStartNodeAndOffset(selection, - getter_AddRefs(selNode), &selOffset); - NS_ENSURE_SUCCESS(rv, rv); - rv = mUtilRange->SetStart(selNode, selOffset); - NS_ENSURE_SUCCESS(rv, rv); + getter_AddRefs(startNode), &startOffset); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + nsCOMPtr<nsINode> endNode; + int32_t endOffset; NS_ENSURE_STATE(mHTMLEditor); rv = mHTMLEditor->GetEndNodeAndOffset(selection, - getter_AddRefs(selNode), &selOffset); - NS_ENSURE_SUCCESS(rv, rv); - rv = mUtilRange->SetEnd(selNode, selOffset); - NS_ENSURE_SUCCESS(rv, rv); + getter_AddRefs(endNode), &endOffset); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + rv = mUtilRange->SetStartAndEnd(startNode, startOffset, endNode, endOffset); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } return UpdateDocChangeRange(mUtilRange); } diff --git a/editor/libeditor/HTMLEditUtils.cpp b/editor/libeditor/HTMLEditUtils.cpp index 0adc5d5113..aa0afda98c 100644 --- a/editor/libeditor/HTMLEditUtils.cpp +++ b/editor/libeditor/HTMLEditUtils.cpp @@ -583,7 +583,7 @@ HTMLEditUtils::SupportsAlignAttr(nsIDOMNode* aNode) struct ElementInfo final { #ifdef DEBUG - eHTMLTags mTag; + nsHTMLTag mTag; #endif uint32_t mGroup; uint32_t mCanContainGroups; @@ -603,9 +603,11 @@ static const ElementInfo kElements[eHTMLTag_userdefined] = { ELEM(a, true, false, GROUP_SPECIAL, GROUP_INLINE_ELEMENT), ELEM(abbr, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), ELEM(acronym, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), - ELEM(address, true, true, GROUP_BLOCK, - GROUP_INLINE_ELEMENT | GROUP_P), - ELEM(applet, true, true, GROUP_SPECIAL | GROUP_BLOCK, + ELEM(address, true, true, GROUP_BLOCK, GROUP_INLINE_ELEMENT | GROUP_P), + ELEM(applet, + true, + true, + GROUP_SPECIAL | GROUP_BLOCK, GROUP_FLOW_ELEMENT | GROUP_OBJECT_CONTENT), ELEM(area, false, false, GROUP_MAP_CONTENT, GROUP_NONE), ELEM(article, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), @@ -620,19 +622,23 @@ static const ElementInfo kElements[eHTMLTag_userdefined] = { ELEM(blockquote, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), ELEM(body, true, true, GROUP_TOPLEVEL, GROUP_FLOW_ELEMENT), ELEM(br, false, false, GROUP_SPECIAL, GROUP_NONE), - ELEM(button, true, true, GROUP_FORMCONTROL | GROUP_BLOCK, - GROUP_FLOW_ELEMENT), + ELEM(button, true, true, GROUP_FORMCONTROL | GROUP_BLOCK, GROUP_FLOW_ELEMENT), ELEM(canvas, false, false, GROUP_NONE, GROUP_NONE), ELEM(caption, true, true, GROUP_NONE, GROUP_INLINE_ELEMENT), ELEM(center, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), ELEM(cite, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), ELEM(code, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), - ELEM(col, false, false, GROUP_TABLE_CONTENT | GROUP_COLGROUP_CONTENT, + ELEM(col, + false, + false, + GROUP_TABLE_CONTENT | GROUP_COLGROUP_CONTENT, GROUP_NONE), ELEM(colgroup, true, false, GROUP_NONE, GROUP_COLGROUP_CONTENT), - ELEM(content, true, false, GROUP_NONE, GROUP_INLINE_ELEMENT), ELEM(data, true, false, GROUP_PHRASE, GROUP_INLINE_ELEMENT), - ELEM(datalist, true, false, GROUP_PHRASE, + ELEM(datalist, + true, + false, + GROUP_PHRASE, GROUP_OPTIONS | GROUP_INLINE_ELEMENT), ELEM(dd, true, false, GROUP_DL_CONTENT, GROUP_FLOW_ELEMENT), ELEM(del, true, true, GROUP_PHRASE | GROUP_BLOCK, GROUP_FLOW_ELEMENT), @@ -647,33 +653,25 @@ static const ElementInfo kElements[eHTMLTag_userdefined] = { ELEM(embed, false, false, GROUP_NONE, GROUP_NONE), ELEM(fieldset, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), ELEM(figcaption, true, false, GROUP_FIGCAPTION, GROUP_FLOW_ELEMENT), - ELEM(figure, true, true, GROUP_BLOCK, - GROUP_FLOW_ELEMENT | GROUP_FIGCAPTION), + ELEM(figure, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT | GROUP_FIGCAPTION), ELEM(font, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT), ELEM(footer, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), ELEM(form, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), ELEM(frame, false, false, GROUP_FRAME, GROUP_NONE), ELEM(frameset, true, true, GROUP_FRAME, GROUP_FRAME), - ELEM(h1, true, false, GROUP_BLOCK | GROUP_HEADING, - GROUP_INLINE_ELEMENT), - ELEM(h2, true, false, GROUP_BLOCK | GROUP_HEADING, - GROUP_INLINE_ELEMENT), - ELEM(h3, true, false, GROUP_BLOCK | GROUP_HEADING, - GROUP_INLINE_ELEMENT), - ELEM(h4, true, false, GROUP_BLOCK | GROUP_HEADING, - GROUP_INLINE_ELEMENT), - ELEM(h5, true, false, GROUP_BLOCK | GROUP_HEADING, - GROUP_INLINE_ELEMENT), - ELEM(h6, true, false, GROUP_BLOCK | GROUP_HEADING, - GROUP_INLINE_ELEMENT), + ELEM(h1, true, false, GROUP_BLOCK | GROUP_HEADING, GROUP_INLINE_ELEMENT), + ELEM(h2, true, false, GROUP_BLOCK | GROUP_HEADING, GROUP_INLINE_ELEMENT), + ELEM(h3, true, false, GROUP_BLOCK | GROUP_HEADING, GROUP_INLINE_ELEMENT), + ELEM(h4, true, false, GROUP_BLOCK | GROUP_HEADING, GROUP_INLINE_ELEMENT), + ELEM(h5, true, false, GROUP_BLOCK | GROUP_HEADING, GROUP_INLINE_ELEMENT), + ELEM(h6, true, false, GROUP_BLOCK | GROUP_HEADING, GROUP_INLINE_ELEMENT), ELEM(head, true, false, GROUP_TOPLEVEL, GROUP_HEAD_CONTENT), ELEM(header, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), ELEM(hgroup, true, false, GROUP_BLOCK, GROUP_HEADING), ELEM(hr, false, false, GROUP_BLOCK, GROUP_NONE), ELEM(html, true, false, GROUP_TOPLEVEL, GROUP_TOPLEVEL), ELEM(i, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT), - ELEM(iframe, true, true, GROUP_SPECIAL | GROUP_BLOCK, - GROUP_FLOW_ELEMENT), + ELEM(iframe, true, true, GROUP_SPECIAL | GROUP_BLOCK, GROUP_FLOW_ELEMENT), ELEM(image, false, false, GROUP_NONE, GROUP_NONE), ELEM(img, false, false, GROUP_SPECIAL | GROUP_PICTURE_CONTENT, GROUP_NONE), ELEM(input, false, false, GROUP_FORMCONTROL, GROUP_NONE), @@ -699,15 +697,15 @@ static const ElementInfo kElements[eHTMLTag_userdefined] = { ELEM(noembed, false, false, GROUP_NONE, GROUP_NONE), ELEM(noframes, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), ELEM(noscript, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), - ELEM(object, true, true, GROUP_SPECIAL | GROUP_BLOCK, + ELEM(object, + true, + true, + GROUP_SPECIAL | GROUP_BLOCK, GROUP_FLOW_ELEMENT | GROUP_OBJECT_CONTENT), // XXX Can contain self and ul because editor does sublists illegally. - ELEM(ol, true, true, GROUP_BLOCK | GROUP_OL_UL, - GROUP_LI | GROUP_OL_UL), - ELEM(optgroup, true, false, GROUP_SELECT_CONTENT, - GROUP_OPTIONS), - ELEM(option, true, false, - GROUP_SELECT_CONTENT | GROUP_OPTIONS, GROUP_LEAF), + ELEM(ol, true, true, GROUP_BLOCK | GROUP_OL_UL, GROUP_LI | GROUP_OL_UL), + ELEM(optgroup, true, false, GROUP_SELECT_CONTENT, GROUP_OPTIONS), + ELEM(option, true, false, GROUP_SELECT_CONTENT | GROUP_OPTIONS, GROUP_LEAF), ELEM(output, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT), ELEM(p, true, false, GROUP_BLOCK | GROUP_P, GROUP_INLINE_ELEMENT), ELEM(param, false, false, GROUP_OBJECT_CONTENT, GROUP_NONE), @@ -723,12 +721,11 @@ static const ElementInfo kElements[eHTMLTag_userdefined] = { ELEM(ruby, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), ELEM(s, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT), ELEM(samp, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), - ELEM(script, true, false, GROUP_HEAD_CONTENT | GROUP_SPECIAL, - GROUP_LEAF), + ELEM(script, true, false, GROUP_HEAD_CONTENT | GROUP_SPECIAL, GROUP_LEAF), ELEM(section, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT), ELEM(select, true, false, GROUP_FORMCONTROL, GROUP_SELECT_CONTENT), - ELEM(shadow, true, false, GROUP_NONE, GROUP_INLINE_ELEMENT), ELEM(small, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT), + ELEM(slot, true, false, GROUP_NONE, GROUP_FLOW_ELEMENT), ELEM(source, false, false, GROUP_PICTURE_CONTENT, GROUP_NONE), ELEM(span, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT), ELEM(strike, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT), @@ -752,8 +749,7 @@ static const ElementInfo kElements[eHTMLTag_userdefined] = { ELEM(tt, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT), ELEM(u, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT), // XXX Can contain self and ol because editor does sublists illegally. - ELEM(ul, true, true, GROUP_BLOCK | GROUP_OL_UL, - GROUP_LI | GROUP_OL_UL), + ELEM(ul, true, true, GROUP_BLOCK | GROUP_OL_UL, GROUP_LI | GROUP_OL_UL), ELEM(var, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT), ELEM(video, false, false, GROUP_NONE, GROUP_NONE), ELEM(wbr, false, false, GROUP_NONE, GROUP_NONE), @@ -794,7 +790,7 @@ HTMLEditUtils::CanContain(int32_t aParent, int32_t aChild) // Special-case button. if (aParent == eHTMLTag_button) { - static const eHTMLTags kButtonExcludeKids[] = { + static const nsHTMLTag kButtonExcludeKids[] = { eHTMLTag_a, eHTMLTag_fieldset, eHTMLTag_form, diff --git a/editor/libeditor/HTMLEditor.cpp b/editor/libeditor/HTMLEditor.cpp index 532da7a150..c2f0bdc6dd 100644 --- a/editor/libeditor/HTMLEditor.cpp +++ b/editor/libeditor/HTMLEditor.cpp @@ -63,13 +63,13 @@ #include "nsIWidget.h" #include "nsIFrame.h" -#include "nsIParserService.h" #include "mozilla/dom/Selection.h" #include "mozilla/dom/DocumentFragment.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/Event.h" #include "mozilla/dom/EventTarget.h" #include "mozilla/dom/HTMLBodyElement.h" +#include "nsElementTable.h" #include "nsTextFragment.h" #include "nsContentList.h" #include "mozilla/StyleSheet.h" @@ -165,7 +165,7 @@ HTMLEditor::~HTMLEditor() // free any default style propItems RemoveAllDefaultProperties(); - if (mLinkHandler && mDocWeak) { + if (mLinkHandler && IsInitialized()) { nsCOMPtr<nsIPresShell> ps = GetPresShell(); if (ps && ps->GetPresContext()) { @@ -287,7 +287,7 @@ HTMLEditor::Init(nsIDOMDocument* aDoc, } // Init the HTML-CSS utils - mCSSEditUtils = new CSSEditUtils(this); + mCSSEditUtils = MakeUnique<CSSEditUtils>(this); // disable links nsCOMPtr<nsIPresShell> presShell = GetPresShell(); @@ -335,7 +335,7 @@ HTMLEditor::PreDestroy(bool aDestroyingFrames) return NS_OK; } - nsCOMPtr<nsINode> document = do_QueryReferent(mDocWeak); + nsCOMPtr<nsIDocument> document = GetDocument(); if (document) { document->RemoveMutationObserver(this); } @@ -365,7 +365,7 @@ HTMLEditor::UpdateRootElement() } else { // If there is no HTML body element, // we should use the document root element instead. - nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(mDocWeak); + nsCOMPtr<nsIDOMDocument> doc = GetDOMDocument(); if (doc) { doc->GetDocumentElement(getter_AddRefs(rootElement)); } @@ -430,8 +430,9 @@ HTMLEditor::CreateEventListeners() nsresult HTMLEditor::InstallEventListeners() { - NS_ENSURE_TRUE(mDocWeak && mEventListener, - NS_ERROR_NOT_INITIALIZED); + if (NS_WARN_IF(!IsInitialized()) || NS_WARN_IF(!mEventListener)) { + return NS_ERROR_NOT_INITIALIZED; + } // NOTE: HTMLEditor doesn't need to initialize mEventTarget here because // the target must be document node and it must be referenced as weak pointer. @@ -444,7 +445,7 @@ HTMLEditor::InstallEventListeners() void HTMLEditor::RemoveEventListeners() { - if (!mDocWeak) { + if (!IsInitialized()) { return; } @@ -506,7 +507,8 @@ HTMLEditor::InitRules() NS_IMETHODIMP HTMLEditor::BeginningOfDocument() { - if (!mDocWeak) { + // XXX Why doesn't this check if the document is alive? + if (!IsInitialized()) { return NS_ERROR_NOT_INITIALIZED; } @@ -688,49 +690,6 @@ HTMLEditor::HandleKeyPressEvent(nsIDOMKeyEvent* aKeyEvent) return TypedText(str, eTypedText); } -static void -AssertParserServiceIsCorrect(nsIAtom* aTag, bool aIsBlock) -{ -#ifdef DEBUG - // Check this against what we would have said with the old code: - if (aTag == nsGkAtoms::p || - aTag == nsGkAtoms::div || - aTag == nsGkAtoms::blockquote || - aTag == nsGkAtoms::h1 || - aTag == nsGkAtoms::h2 || - aTag == nsGkAtoms::h3 || - aTag == nsGkAtoms::h4 || - aTag == nsGkAtoms::h5 || - aTag == nsGkAtoms::h6 || - aTag == nsGkAtoms::ul || - aTag == nsGkAtoms::ol || - aTag == nsGkAtoms::dl || - aTag == nsGkAtoms::noscript || - aTag == nsGkAtoms::form || - aTag == nsGkAtoms::hr || - aTag == nsGkAtoms::table || - aTag == nsGkAtoms::fieldset || - aTag == nsGkAtoms::address || - aTag == nsGkAtoms::col || - aTag == nsGkAtoms::colgroup || - aTag == nsGkAtoms::li || - aTag == nsGkAtoms::dt || - aTag == nsGkAtoms::dd || - aTag == nsGkAtoms::legend) { - if (!aIsBlock) { - nsAutoString assertmsg (NS_LITERAL_STRING("Parser and editor disagree on blockness: ")); - - nsAutoString tagName; - aTag->ToString(tagName); - assertmsg.Append(tagName); - char* assertstr = ToNewCString(assertmsg); - NS_ASSERTION(aIsBlock, assertstr); - free(assertstr); - } - } -#endif // DEBUG -} - /** * Returns true if the id represents an element of block type. * Can be used to determine if a new paragraph should be started. @@ -740,8 +699,8 @@ HTMLEditor::NodeIsBlockStatic(const nsINode* aElement) { MOZ_ASSERT(aElement); - // Nodes we know we want to treat as block - // even though the parser says they're not: + // We want to treat these as block nodes even though nsHTMLElement says + // they're not. if (aElement->IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::head, nsGkAtoms::tbody, @@ -750,27 +709,13 @@ HTMLEditor::NodeIsBlockStatic(const nsINode* aElement) nsGkAtoms::tr, nsGkAtoms::th, nsGkAtoms::td, - nsGkAtoms::li, nsGkAtoms::dt, - nsGkAtoms::dd, - nsGkAtoms::pre)) { + nsGkAtoms::dd)) { return true; } - bool isBlock; -#ifdef DEBUG - // XXX we can't use DebugOnly here because VC++ is stupid (bug 802884) - nsresult rv = -#endif - nsContentUtils::GetParserService()-> - IsBlock(nsContentUtils::GetParserService()->HTMLAtomTagToId( - aElement->NodeInfo()->NameAtom()), - isBlock); - MOZ_ASSERT(rv == NS_OK); - - AssertParserServiceIsCorrect(aElement->NodeInfo()->NameAtom(), isBlock); - - return isBlock; + return nsHTMLElement::IsBlock( + nsHTMLTags::AtomTagToId(aElement->NodeInfo()->NameAtom())); } nsresult @@ -1183,11 +1128,13 @@ HTMLEditor::ReplaceHeadContentsWithHTML(const nsAString& aSourceToInsert) // Do not use AutoRules -- rules code won't let us insert in <head>. Use // the head node as a parent and delete/insert directly. - nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak); - NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED); + nsCOMPtr<nsIDocument> document = GetDocument(); + if (NS_WARN_IF(!document)) { + return NS_ERROR_NOT_INITIALIZED; + } RefPtr<nsContentList> nodeList = - doc->GetElementsByTagName(NS_LITERAL_STRING("head")); + document->GetElementsByTagName(NS_LITERAL_STRING("head")); NS_ENSURE_TRUE(nodeList, NS_ERROR_NULL_POINTER); nsCOMPtr<nsIContent> headNode = nodeList->Item(0); @@ -2401,21 +2348,19 @@ HTMLEditor::GetSelectedElement(const nsAString& aTagName, NS_ENSURE_STATE(range); nsCOMPtr<nsIDOMNode> startParent; - int32_t startOffset, endOffset; nsresult rv = range->GetStartContainer(getter_AddRefs(startParent)); NS_ENSURE_SUCCESS(rv, rv); - rv = range->GetStartOffset(&startOffset); - NS_ENSURE_SUCCESS(rv, rv); + uint32_t startOffset = range->StartOffset(); nsCOMPtr<nsIDOMNode> endParent; rv = range->GetEndContainer(getter_AddRefs(endParent)); NS_ENSURE_SUCCESS(rv, rv); - rv = range->GetEndOffset(&endOffset); - NS_ENSURE_SUCCESS(rv, rv); + uint32_t endOffset = range->EndOffset(); // Optimization for a single selected element if (startParent && startParent == endParent && endOffset - startOffset == 1) { - nsCOMPtr<nsIDOMNode> selectedNode = GetChildAt(startParent, startOffset); + nsCOMPtr<nsIDOMNode> selectedNode = + GetChildAt(startParent, static_cast<int32_t>(startOffset)); NS_ENSURE_SUCCESS(rv, NS_OK); if (selectedNode) { selectedNode->GetNodeName(domTagName); @@ -2705,7 +2650,7 @@ HTMLEditor::InsertLinkAroundSelection(nsIDOMElement* aAnchorElement) nsresult HTMLEditor::SetHTMLBackgroundColor(const nsAString& aColor) { - NS_PRECONDITION(mDocWeak, "Missing Editor DOM Document"); + MOZ_ASSERT(IsInitialized(), "The HTMLEditor hasn't been initialized yet"); // Find a selected or enclosing table element to set background on nsCOMPtr<nsIDOMElement> element; @@ -2753,7 +2698,7 @@ HTMLEditor::SetBodyAttribute(const nsAString& aAttribute, { // TODO: Check selection for Cell, Row, Column or table and do color on appropriate level - NS_ASSERTION(mDocWeak, "Missing Editor DOM Document"); + MOZ_ASSERT(IsInitialized(), "The HTMLEditor hasn't been initialized yet"); // Set the background color attribute on the body tag nsCOMPtr<nsIDOMElement> bodyElement = do_QueryInterface(GetRoot()); @@ -2832,7 +2777,9 @@ HTMLEditor::ReplaceStyleSheet(const nsAString& aURL) } // Make sure the pres shell doesn't disappear during the load. - NS_ENSURE_TRUE(mDocWeak, NS_ERROR_NOT_INITIALIZED); + if (NS_WARN_IF(!IsInitialized())) { + return NS_ERROR_NOT_INITIALIZED; + } nsCOMPtr<nsIPresShell> ps = GetPresShell(); NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED); @@ -2941,7 +2888,9 @@ HTMLEditor::RemoveOverrideStyleSheet(const nsAString& aURL) NS_ENSURE_TRUE(sheet, NS_OK); /// Don't fail if sheet not found - NS_ENSURE_TRUE(mDocWeak, NS_ERROR_NOT_INITIALIZED); + if (NS_WARN_IF(!IsInitialized())) { + return NS_ERROR_NOT_INITIALIZED; + } nsCOMPtr<nsIPresShell> ps = GetPresShell(); NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED); @@ -2960,8 +2909,8 @@ HTMLEditor::EnableStyleSheet(const nsAString& aURL, NS_ENSURE_TRUE(sheet, NS_OK); // Don't fail if sheet not found // Ensure the style sheet is owned by our document. - nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak); - sheet->SetOwningDocument(doc); + nsCOMPtr<nsIDocument> document = GetDocument(); + sheet->SetAssociatedDocument(document, StyleSheet::NotOwnedByDocument); if (sheet->IsServo()) { // XXXheycam ServoStyleSheets don't support being enabled/disabled yet. @@ -2982,8 +2931,8 @@ HTMLEditor::EnableExistingStyleSheet(const nsAString& aURL) } // Ensure the style sheet is owned by our document. - nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak); - sheet->SetOwningDocument(doc); + nsCOMPtr<nsIDocument> document = GetDocument(); + sheet->SetAssociatedDocument(document, StyleSheet::NotOwnedByDocument); if (sheet->IsServo()) { // XXXheycam ServoStyleSheets don't support being enabled/disabled yet. @@ -3284,8 +3233,8 @@ HTMLEditor::DoContentInserted(nsIDocument* aDocument, sibling = sibling->GetNextSibling(); } } - nsresult rv = range->Set(aContainer, aIndexInContainer, - aContainer, endIndex); + nsresult rv = range->SetStartAndEnd(aContainer, aIndexInContainer, + aContainer, endIndex); if (NS_SUCCEEDED(rv)) { mInlineSpellChecker->SpellCheckRange(range); } @@ -3374,13 +3323,12 @@ HTMLEditor::GetIsSelectionEditable(bool* aIsSelectionEditable) static nsresult SetSelectionAroundHeadChildren(Selection* aSelection, - nsIWeakReference* aDocWeak) + nsCOMPtr<nsIDocument>& aDocument) { - // Set selection around <head> node - nsCOMPtr<nsIDocument> doc = do_QueryReferent(aDocWeak); - NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED); + MOZ_ASSERT(aDocument); - dom::Element* headNode = doc->GetHeadElement(); + // Set selection around <head> node + dom::Element* headNode = aDocument->GetHeadElement(); NS_ENSURE_STATE(headNode); // Collapse selection to before first child of the head, @@ -3401,7 +3349,11 @@ HTMLEditor::GetHeadContentsAsHTML(nsAString& aOutputString) // Save current selection AutoSelectionRestorer selectionRestorer(selection, this); - nsresult rv = SetSelectionAroundHeadChildren(selection, mDocWeak); + nsCOMPtr<nsIDocument> document = GetDocument(); + if (NS_WARN_IF(!document)) { + return NS_ERROR_NOT_INITIALIZED; + } + nsresult rv = SetSelectionAroundHeadChildren(selection, document); NS_ENSURE_SUCCESS(rv, rv); rv = OutputToString(NS_LITERAL_STRING("text/html"), @@ -3529,17 +3481,15 @@ bool HTMLEditor::TagCanContainTag(nsIAtom& aParentTag, nsIAtom& aChildTag) { - nsIParserService* parserService = nsContentUtils::GetParserService(); - int32_t childTagEnum; // XXX Should this handle #cdata-section too? if (&aChildTag == nsGkAtoms::textTagName) { childTagEnum = eHTMLTag_text; } else { - childTagEnum = parserService->HTMLAtomTagToId(&aChildTag); + childTagEnum = nsHTMLTags::AtomTagToId(&aChildTag); } - int32_t parentTagEnum = parserService->HTMLAtomTagToId(&aParentTag); + int32_t parentTagEnum = nsHTMLTags::AtomTagToId(&aParentTag); return HTMLEditUtils::CanContain(parentTagEnum, childTagEnum); } @@ -3553,8 +3503,7 @@ HTMLEditor::IsContainer(nsINode* aNode) if (aNode->IsNodeOfType(nsINode::eTEXT)) { tagEnum = eHTMLTag_text; } else { - tagEnum = - nsContentUtils::GetParserService()->HTMLStringTagToId(aNode->NodeName()); + tagEnum = nsHTMLTags::StringTagToId(aNode->NodeName()); } return HTMLEditUtils::IsContainer(tagEnum); @@ -3581,15 +3530,11 @@ HTMLEditor::SelectEntireDocument(Selection* aSelection) // Protect the edit rules object from dying nsCOMPtr<nsIEditRules> rules(mRules); - // get editor root node - nsCOMPtr<nsIDOMElement> rootElement = do_QueryInterface(GetRoot()); - // is doc empty? - bool bDocIsEmpty; - nsresult rv = rules->DocumentIsEmpty(&bDocIsEmpty); - NS_ENSURE_SUCCESS(rv, rv); + if (rules->DocumentIsEmpty()) { + // get editor root node + Element* rootElement = GetRoot(); - if (bDocIsEmpty) { // if its empty dont select entire doc - that would select the bogus node return aSelection->Collapse(rootElement, 0); } @@ -4275,10 +4220,11 @@ HTMLEditor::IsVisTextNode(nsIContent* aNode, uint32_t length = aNode->TextLength(); if (aSafeToAskFrames) { - nsCOMPtr<nsISelectionController> selCon; - nsresult rv = GetSelectionController(getter_AddRefs(selCon)); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE); + nsCOMPtr<nsISelectionController> selectionController = + GetSelectionController(); + if (NS_WARN_IF(!selectionController)) { + return NS_ERROR_FAILURE; + } bool isVisible = false; // ask the selection controller for information about whether any // of the data in the node is really rendered. This is really @@ -4286,7 +4232,8 @@ HTMLEditor::IsVisTextNode(nsIContent* aNode, // So we put a call in the selection controller interface, since it's already // in bed with frames anyway. (this is a fix for bug 22227, and a // partial fix for bug 46209) - rv = selCon->CheckVisibilityContent(aNode, 0, length, &isVisible); + nsresult rv = selectionController->CheckVisibilityContent(aNode, 0, length, + &isVisible); NS_ENSURE_SUCCESS(rv, rv); if (isVisible) { *outIsEmptyNode = false; @@ -4802,7 +4749,9 @@ HTMLEditor::GetElementOrigin(nsIDOMElement* aElement, aX = 0; aY = 0; - NS_ENSURE_TRUE(mDocWeak, NS_ERROR_NOT_INITIALIZED); + if (NS_WARN_IF(!IsInitialized())) { + return NS_ERROR_NOT_INITIALIZED; + } nsCOMPtr<nsIPresShell> ps = GetPresShell(); NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED); @@ -4944,28 +4893,29 @@ HTMLEditor::GetReturnInParagraphCreatesNewParagraph(bool* aCreatesNewParagraph) already_AddRefed<nsIContent> HTMLEditor::GetFocusedContent() { - NS_ENSURE_TRUE(mDocWeak, nullptr); - nsFocusManager* fm = nsFocusManager::GetFocusManager(); NS_ENSURE_TRUE(fm, nullptr); nsCOMPtr<nsIContent> focusedContent = fm->GetFocusedContent(); - nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak); - bool inDesignMode = doc->HasFlag(NODE_IS_EDITABLE); + nsCOMPtr<nsIDocument> document = GetDocument(); + if (NS_WARN_IF(!document)) { + return nullptr; + } + bool inDesignMode = document->HasFlag(NODE_IS_EDITABLE); if (!focusedContent) { // in designMode, nobody gets focus in most cases. if (inDesignMode && OurWindowHasFocus()) { - nsCOMPtr<nsIContent> docRoot = doc->GetRootElement(); - return docRoot.forget(); + nsCOMPtr<nsIContent> rootContent = document->GetRootElement(); + return rootContent.forget(); } return nullptr; } if (inDesignMode) { return OurWindowHasFocus() && - nsContentUtils::ContentIsDescendantOf(focusedContent, doc) ? - focusedContent.forget() : nullptr; + nsContentUtils::ContentIsDescendantOf(focusedContent, document) ? + focusedContent.forget() : nullptr; } // We're HTML editor for contenteditable @@ -4988,28 +4938,32 @@ HTMLEditor::GetFocusedContentForIME() return nullptr; } - nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak); - NS_ENSURE_TRUE(doc, nullptr); - return doc->HasFlag(NODE_IS_EDITABLE) ? nullptr : focusedContent.forget(); + nsCOMPtr<nsIDocument> document = GetDocument(); + if (NS_WARN_IF(!document)) { + return nullptr; + } + return document->HasFlag(NODE_IS_EDITABLE) ? nullptr : + focusedContent.forget(); } bool HTMLEditor::IsActiveInDOMWindow() { - NS_ENSURE_TRUE(mDocWeak, false); - nsFocusManager* fm = nsFocusManager::GetFocusManager(); NS_ENSURE_TRUE(fm, false); - nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak); - bool inDesignMode = doc->HasFlag(NODE_IS_EDITABLE); + nsCOMPtr<nsIDocument> document = GetDocument(); + if (NS_WARN_IF(!document)) { + return false; + } + bool inDesignMode = document->HasFlag(NODE_IS_EDITABLE); // If we're in designMode, we're always active in the DOM window. if (inDesignMode) { return true; } - nsPIDOMWindowOuter* ourWindow = doc->GetWindow(); + nsPIDOMWindowOuter* ourWindow = document->GetWindow(); nsCOMPtr<nsPIDOMWindowOuter> win; nsIContent* content = nsFocusManager::GetFocusedDescendant(ourWindow, false, @@ -5032,12 +4986,12 @@ HTMLEditor::IsActiveInDOMWindow() Element* HTMLEditor::GetActiveEditingHost() { - NS_ENSURE_TRUE(mDocWeak, nullptr); - - nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak); - NS_ENSURE_TRUE(doc, nullptr); - if (doc->HasFlag(NODE_IS_EDITABLE)) { - return doc->GetBodyElement(); + nsCOMPtr<nsIDocument> document = GetDocument(); + if (NS_WARN_IF(!document)) { + return nullptr; + } + if (document->HasFlag(NODE_IS_EDITABLE)) { + return document->GetBodyElement(); } // We're HTML editor for contenteditable @@ -5066,8 +5020,8 @@ HTMLEditor::GetDOMEventTarget() // Don't use getDocument here, because we have no way of knowing // whether Init() was ever called. So we need to get the document // ourselves, if it exists. - NS_PRECONDITION(mDocWeak, "This editor has not been initialized yet"); - nsCOMPtr<mozilla::dom::EventTarget> target = do_QueryReferent(mDocWeak); + MOZ_ASSERT(IsInitialized(), "The HTMLEditor has not been initialized yet"); + nsCOMPtr<mozilla::dom::EventTarget> target = GetDocument(); return target.forget(); } @@ -5121,12 +5075,16 @@ HTMLEditor::NotifyRootChanged() nsresult HTMLEditor::GetBodyElement(nsIDOMHTMLElement** aBody) { - NS_PRECONDITION(mDocWeak, "bad state, null mDocWeak"); - nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryReferent(mDocWeak); - if (!htmlDoc) { + MOZ_ASSERT(IsInitialized(), "The HTMLEditor hasn't been initialized yet"); + nsCOMPtr<nsIDocument> document = GetDocument(); + if (NS_WARN_IF(!document)) { + return NS_ERROR_NOT_INITIALIZED; + } + nsCOMPtr<nsIDOMHTMLDocument> domHTMLDocument = do_QueryInterface(document); + if (!domHTMLDocument) { return NS_ERROR_NOT_INITIALIZED; } - return htmlDoc->GetBody(aBody); + return domHTMLDocument->GetBody(aBody); } already_AddRefed<nsINode> @@ -5146,14 +5104,13 @@ HTMLEditor::GetFocusedNode() return node.forget(); } - nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak); - return doc.forget(); + nsCOMPtr<nsIDocument> document = GetDocument(); + return document.forget(); } bool HTMLEditor::OurWindowHasFocus() { - NS_ENSURE_TRUE(mDocWeak, false); nsIFocusManager* fm = nsFocusManager::GetFocusManager(); NS_ENSURE_TRUE(fm, false); nsCOMPtr<mozIDOMWindowProxy> focusedWindow; @@ -5161,8 +5118,11 @@ HTMLEditor::OurWindowHasFocus() if (!focusedWindow) { return false; } - nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak); - nsPIDOMWindowOuter* ourWindow = doc->GetWindow(); + nsCOMPtr<nsIDocument> document = GetDocument(); + if (NS_WARN_IF(!document)) { + return false; + } + nsPIDOMWindowOuter* ourWindow = document->GetWindow(); return ourWindow == focusedWindow; } @@ -5180,12 +5140,14 @@ HTMLEditor::IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent) return true; } - NS_ENSURE_TRUE(mDocWeak, false); - nsCOMPtr<nsIDOMEventTarget> target = aGUIEvent->GetDOMEventTarget(); NS_ENSURE_TRUE(target, false); - nsCOMPtr<nsIDocument> document = do_QueryReferent(mDocWeak); + nsCOMPtr<nsIDocument> document = GetDocument(); + if (NS_WARN_IF(!document)) { + return false; + } + if (document->HasFlag(NODE_IS_EDITABLE)) { // If this editor is in designMode and the event target is the document, // the event is for this editor. diff --git a/editor/libeditor/HTMLEditor.h b/editor/libeditor/HTMLEditor.h index dfcdd8d6b3..dc1a41b70a 100644 --- a/editor/libeditor/HTMLEditor.h +++ b/editor/libeditor/HTMLEditor.h @@ -10,11 +10,11 @@ #include "mozilla/CSSEditUtils.h" #include "mozilla/StyleSheet.h" #include "mozilla/TextEditor.h" +#include "mozilla/UniquePtr.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/File.h" #include "nsAttrName.h" -#include "nsAutoPtr.h" #include "nsCOMPtr.h" #include "nsIContentFilter.h" #include "nsICSSLoaderObserver.h" @@ -896,7 +896,7 @@ protected: bool mCRInParagraphCreatesParagraph; bool mCSSAware; - nsAutoPtr<CSSEditUtils> mCSSEditUtils; + UniquePtr<CSSEditUtils> mCSSEditUtils; // Used by GetFirstSelectedCell and GetNextSelectedCell int32_t mSelectedCellIndex; diff --git a/editor/libeditor/HTMLEditorDataTransfer.cpp b/editor/libeditor/HTMLEditorDataTransfer.cpp index c56fbead77..0c01bdd1c5 100644 --- a/editor/libeditor/HTMLEditorDataTransfer.cpp +++ b/editor/libeditor/HTMLEditorDataTransfer.cpp @@ -145,14 +145,13 @@ HTMLEditor::LoadHTML(const nsAString& aInputString) rv = range->GetStartContainer(getter_AddRefs(parent)); NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER); - int32_t childOffset; - rv = range->GetStartOffset(&childOffset); - NS_ENSURE_SUCCESS(rv, rv); + uint32_t childOffset = range->StartOffset(); nsCOMPtr<nsIDOMNode> nodeToInsert; docfrag->GetFirstChild(getter_AddRefs(nodeToInsert)); while (nodeToInsert) { - rv = InsertNode(nodeToInsert, parent, childOffset++); + rv = InsertNode(nodeToInsert, parent, + static_cast<int32_t>(childOffset++)); NS_ENSURE_SUCCESS(rv, rv); docfrag->GetFirstChild(getter_AddRefs(nodeToInsert)); } diff --git a/editor/libeditor/HTMLStyleEditor.cpp b/editor/libeditor/HTMLStyleEditor.cpp index 6a1ffe8b41..7d1217069d 100644 --- a/editor/libeditor/HTMLStyleEditor.cpp +++ b/editor/libeditor/HTMLStyleEditor.cpp @@ -541,9 +541,11 @@ HTMLEditor::SplitStyleAboveRange(nsRange* inRange, NS_ENSURE_SUCCESS(rv, rv); // reset the range - rv = inRange->SetStart(startNode, startOffset); - NS_ENSURE_SUCCESS(rv, rv); - return inRange->SetEnd(endNode, endOffset); + rv = inRange->SetStartAndEnd(startNode, startOffset, endNode, endOffset); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + return NS_OK; } nsresult @@ -885,10 +887,11 @@ HTMLEditor::PromoteRangeIfStartsOrEndsInNamedAnchor(nsRange& aRange) endOffset = endNode ? endNode->IndexOf(parent) + 1 : 0; } - nsresult rv = aRange.SetStart(startNode, startOffset); - NS_ENSURE_SUCCESS(rv, rv); - rv = aRange.SetEnd(endNode, endOffset); - NS_ENSURE_SUCCESS(rv, rv); + nsresult rv = aRange.SetStartAndEnd(startNode, startOffset, + endNode, endOffset); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } return NS_OK; } @@ -918,10 +921,11 @@ HTMLEditor::PromoteInlineRange(nsRange& aRange) endNode = parent; } - nsresult rv = aRange.SetStart(startNode, startOffset); - NS_ENSURE_SUCCESS(rv, rv); - rv = aRange.SetEnd(endNode, endOffset); - NS_ENSURE_SUCCESS(rv, rv); + nsresult rv = aRange.SetStartAndEnd(startNode, startOffset, + endNode, endOffset); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } return NS_OK; } @@ -1074,7 +1078,7 @@ HTMLEditor::GetInlinePropertyBase(nsIAtom& aProperty, if (content->GetAsText()) { if (!isCollapsed && first && firstNodeInRange) { firstNodeInRange = false; - if (range->StartOffset() == (int32_t)content->Length()) { + if (range->StartOffset() == content->Length()) { continue; } } else if (content == endNode && !endOffset) { diff --git a/editor/libeditor/HTMLTableEditor.cpp b/editor/libeditor/HTMLTableEditor.cpp index 778bf1d2da..b26466179b 100644 --- a/editor/libeditor/HTMLTableEditor.cpp +++ b/editor/libeditor/HTMLTableEditor.cpp @@ -2594,7 +2594,6 @@ HTMLEditor::GetCellIndexes(nsIDOMElement* aCell, aCell = cell; } - NS_ENSURE_TRUE(mDocWeak, NS_ERROR_NOT_INITIALIZED); nsCOMPtr<nsIPresShell> ps = GetPresShell(); NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED); @@ -2933,11 +2932,10 @@ HTMLEditor::GetCellFromRange(nsRange* aRange, NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(startParent, NS_ERROR_FAILURE); - int32_t startOffset; - rv = aRange->GetStartOffset(&startOffset); - NS_ENSURE_SUCCESS(rv, rv); + uint32_t startOffset = aRange->StartOffset(); - nsCOMPtr<nsIDOMNode> childNode = GetChildAt(startParent, startOffset); + nsCOMPtr<nsIDOMNode> childNode = + GetChildAt(startParent, static_cast<int32_t>(startOffset)); // This means selection is probably at a text node (or end of doc?) if (!childNode) { return NS_ERROR_FAILURE; @@ -2948,15 +2946,11 @@ HTMLEditor::GetCellFromRange(nsRange* aRange, NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(startParent, NS_ERROR_FAILURE); - int32_t endOffset; - rv = aRange->GetEndOffset(&endOffset); - NS_ENSURE_SUCCESS(rv, rv); - // If a cell is deleted, the range is collapse - // (startOffset == endOffset) + // (startOffset == aRange->EndOffset()) // so tell caller the cell wasn't found if (startParent == endParent && - endOffset == startOffset+1 && + aRange->EndOffset() == startOffset+1 && HTMLEditUtils::IsTableCell(childNode)) { // Should we also test if frame is selected? (Use GetCellDataAt()) // (Let's not for now -- more efficient) diff --git a/editor/libeditor/PlaceholderTransaction.cpp b/editor/libeditor/PlaceholderTransaction.cpp index fa808afadb..142a85075f 100644 --- a/editor/libeditor/PlaceholderTransaction.cpp +++ b/editor/libeditor/PlaceholderTransaction.cpp @@ -8,6 +8,7 @@ #include "CompositionTransaction.h" #include "mozilla/EditorBase.h" #include "mozilla/dom/Selection.h" +#include "mozilla/Move.h" #include "nsGkAtoms.h" #include "nsQueryObject.h" @@ -15,13 +16,18 @@ namespace mozilla { using namespace dom; -PlaceholderTransaction::PlaceholderTransaction() +PlaceholderTransaction::PlaceholderTransaction( + EditorBase& aEditorBase, + nsIAtom* aName, + UniquePtr<SelectionState> aSelState) : mAbsorb(true) , mForwarding(nullptr) , mCompositionTransaction(nullptr) , mCommitted(false) - , mEditorBase(nullptr) + , mStartSel(Move(aSelState)) + , mEditorBase(&aEditorBase) { + mName = aName; } PlaceholderTransaction::~PlaceholderTransaction() @@ -50,26 +56,12 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PlaceholderTransaction) NS_INTERFACE_MAP_ENTRY(nsIAbsorbingTransaction) - NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_INTERFACE_MAP_END_INHERITING(EditAggregateTransaction) NS_IMPL_ADDREF_INHERITED(PlaceholderTransaction, EditAggregateTransaction) NS_IMPL_RELEASE_INHERITED(PlaceholderTransaction, EditAggregateTransaction) NS_IMETHODIMP -PlaceholderTransaction::Init(nsIAtom* aName, - SelectionState* aSelState, - EditorBase* aEditorBase) -{ - NS_ENSURE_TRUE(aEditorBase && aSelState, NS_ERROR_NULL_POINTER); - - mName = aName; - mStartSel = aSelState; - mEditorBase = aEditorBase; - return NS_OK; -} - -NS_IMETHODIMP PlaceholderTransaction::DoTransaction() { return NS_OK; diff --git a/editor/libeditor/PlaceholderTransaction.h b/editor/libeditor/PlaceholderTransaction.h index 8193239be2..5fa58a1e93 100644 --- a/editor/libeditor/PlaceholderTransaction.h +++ b/editor/libeditor/PlaceholderTransaction.h @@ -8,12 +8,13 @@ #include "EditAggregateTransaction.h" #include "mozilla/EditorUtils.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/WeakPtr.h" #include "nsIAbsorbingTransaction.h" #include "nsIDOMNode.h" #include "nsCOMPtr.h" #include "nsWeakPtr.h" #include "nsWeakReference.h" -#include "nsAutoPtr.h" namespace mozilla { @@ -26,14 +27,18 @@ class CompositionTransaction; * transactions it has absorbed. */ -class PlaceholderTransaction final : public EditAggregateTransaction, - public nsIAbsorbingTransaction, - public nsSupportsWeakReference +class PlaceholderTransaction final + : public EditAggregateTransaction + , public nsIAbsorbingTransaction + , public SupportsWeakPtr<PlaceholderTransaction> { public: + MOZ_DECLARE_WEAKREFERENCE_TYPENAME(PlaceholderTransaction) + NS_DECL_ISUPPORTS_INHERITED - PlaceholderTransaction(); + PlaceholderTransaction(EditorBase& aEditorBase, nsIAtom* aName, + UniquePtr<SelectionState> aSelState); NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PlaceholderTransaction, EditAggregateTransaction) @@ -46,9 +51,6 @@ public: // ------------ nsIAbsorbingTransaction ----------------------- - NS_IMETHOD Init(nsIAtom* aName, SelectionState* aSelState, - EditorBase* aEditorBase) override; - NS_IMETHOD GetTxnName(nsIAtom** aName) override; NS_IMETHOD StartSelectionEquals(SelectionState* aSelState, @@ -61,6 +63,11 @@ public: NS_IMETHOD Commit() override; + NS_IMETHOD_(PlaceholderTransaction*) AsPlaceholderTransaction() override + { + return this; + } + nsresult RememberEndingSelection(); protected: @@ -80,7 +87,7 @@ protected: // restore the selection properly. // Use a pointer because this is constructed before we exist. - nsAutoPtr<SelectionState> mStartSel; + UniquePtr<SelectionState> mStartSel; SelectionState mEndSel; // The editor for this transaction. diff --git a/editor/libeditor/SelectionState.cpp b/editor/libeditor/SelectionState.cpp index f9ad5947a8..057e048758 100644 --- a/editor/libeditor/SelectionState.cpp +++ b/editor/libeditor/SelectionState.cpp @@ -686,7 +686,8 @@ already_AddRefed<nsRange> RangeItem::GetRange() { RefPtr<nsRange> range = new nsRange(startNode); - if (NS_FAILED(range->Set(startNode, startOffset, endNode, endOffset))) { + if (NS_FAILED(range->SetStartAndEnd(startNode, startOffset, + endNode, endOffset))) { return nullptr; } return range.forget(); diff --git a/editor/libeditor/TextEditRules.cpp b/editor/libeditor/TextEditRules.cpp index 8f8f34e8b8..35d4a2f3dd 100644 --- a/editor/libeditor/TextEditRules.cpp +++ b/editor/libeditor/TextEditRules.cpp @@ -329,13 +329,10 @@ TextEditRules::DidDoAction(Selection* aSelection, } } -NS_IMETHODIMP -TextEditRules::DocumentIsEmpty(bool* aDocumentIsEmpty) +NS_IMETHODIMP_(bool) +TextEditRules::DocumentIsEmpty() { - NS_ENSURE_TRUE(aDocumentIsEmpty, NS_ERROR_NULL_POINTER); - - *aDocumentIsEmpty = (mBogusNode != nullptr); - return NS_OK; + return (mBogusNode != nullptr); } void diff --git a/editor/libeditor/TextEditRules.h b/editor/libeditor/TextEditRules.h index 6d4915f151..208e14d23f 100644 --- a/editor/libeditor/TextEditRules.h +++ b/editor/libeditor/TextEditRules.h @@ -66,7 +66,7 @@ public: bool* aCancel, bool* aHandled) override; NS_IMETHOD DidDoAction(Selection* aSelection, RulesInfo* aInfo, nsresult aResult) override; - NS_IMETHOD DocumentIsEmpty(bool* aDocumentIsEmpty) override; + NS_IMETHOD_(bool) DocumentIsEmpty() override; NS_IMETHOD DocumentModified() override; protected: diff --git a/editor/libeditor/TextEditor.cpp b/editor/libeditor/TextEditor.cpp index 1e855d7699..c3cfa4a721 100644 --- a/editor/libeditor/TextEditor.cpp +++ b/editor/libeditor/TextEditor.cpp @@ -864,7 +864,7 @@ TextEditor::UpdateIMEComposition(WidgetCompositionEvent* aCompositionChangeEvent // of NotifiyEditorObservers(eNotifyEditorObserversOfEnd) or // NotifiyEditorObservers(eNotifyEditorObserversOfCancel) which notifies // TextComposition of a selection change. - MOZ_ASSERT(!mPlaceHolderBatch, + MOZ_ASSERT(!mPlaceholderBatch, "UpdateIMEComposition() must be called without place holder batch"); TextComposition::CompositionChangeEventHandlingMarker compositionChangeEventHandlingMarker(mComposition, aCompositionChangeEvent); @@ -913,7 +913,8 @@ TextEditor::GetDocumentIsEmpty(bool* aDocumentIsEmpty) // Protect the edit rules object from dying nsCOMPtr<nsIEditRules> rules(mRules); - return rules->DocumentIsEmpty(aDocumentIsEmpty); + *aDocumentIsEmpty = rules->DocumentIsEmpty(); + return NS_OK; } NS_IMETHODIMP @@ -1237,7 +1238,7 @@ TextEditor::GetAndInitDocEncoder(const nsAString& aFormatType, nsCOMPtr<nsIDocumentEncoder> docEncoder (do_CreateInstance(formatType.get(), &rv)); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr<nsIDOMDocument> domDoc = do_QueryReferent(mDocWeak); + nsCOMPtr<nsIDOMDocument> domDoc = GetDOMDocument(); NS_ASSERTION(domDoc, "Need a document"); rv = docEncoder->Init(domDoc, aFormatType, aFlags); @@ -1580,8 +1581,7 @@ TextEditor::SelectEntireDocument(Selection* aSelection) nsCOMPtr<nsIEditRules> rules(mRules); // is doc empty? - bool bDocIsEmpty; - if (NS_SUCCEEDED(rules->DocumentIsEmpty(&bDocIsEmpty)) && bDocIsEmpty) { + if (rules->DocumentIsEmpty()) { // get root node nsCOMPtr<nsIDOMElement> rootElement = do_QueryInterface(GetRoot()); NS_ENSURE_TRUE(rootElement, NS_ERROR_FAILURE); diff --git a/editor/libeditor/TypeInState.cpp b/editor/libeditor/TypeInState.cpp index ce43e5e4dc..840139feed 100644 --- a/editor/libeditor/TypeInState.cpp +++ b/editor/libeditor/TypeInState.cpp @@ -193,7 +193,7 @@ TypeInState::ClearProp(nsIAtom* aProp, * TakeClearProperty() hands back next property item on the clear list. * Caller assumes ownership of PropItem and must delete it. */ -PropItem* +UniquePtr<PropItem> TypeInState::TakeClearProperty() { size_t count = mClearedArray.Length(); @@ -204,14 +204,14 @@ TypeInState::TakeClearProperty() --count; // indices are zero based PropItem* propItem = mClearedArray[count]; mClearedArray.RemoveElementAt(count); - return propItem; + return UniquePtr<PropItem>(propItem); } /** * TakeSetProperty() hands back next poroperty item on the set list. * Caller assumes ownership of PropItem and must delete it. */ -PropItem* +UniquePtr<PropItem> TypeInState::TakeSetProperty() { size_t count = mSetArray.Length(); @@ -221,7 +221,7 @@ TypeInState::TakeSetProperty() count--; // indices are zero based PropItem* propItem = mSetArray[count]; mSetArray.RemoveElementAt(count); - return propItem; + return UniquePtr<PropItem>(propItem); } /** diff --git a/editor/libeditor/TypeInState.h b/editor/libeditor/TypeInState.h index 540b2d9c11..e1b949508c 100644 --- a/editor/libeditor/TypeInState.h +++ b/editor/libeditor/TypeInState.h @@ -6,6 +6,7 @@ #ifndef TypeInState_h #define TypeInState_h +#include "mozilla/UniquePtr.h" #include "nsCOMPtr.h" #include "nsCycleCollectionParticipant.h" #include "nsISelectionListener.h" @@ -63,13 +64,13 @@ public: * TakeClearProperty() hands back next property item on the clear list. * Caller assumes ownership of PropItem and must delete it. */ - PropItem* TakeClearProperty(); + UniquePtr<PropItem> TakeClearProperty(); /** * TakeSetProperty() hands back next property item on the set list. * Caller assumes ownership of PropItem and must delete it. */ - PropItem* TakeSetProperty(); + UniquePtr<PropItem> TakeSetProperty(); /** * TakeRelativeFontSize() hands back relative font value, which is then diff --git a/editor/libeditor/WSRunObject.cpp b/editor/libeditor/WSRunObject.cpp index 39ac3fee86..0748f09e53 100644 --- a/editor/libeditor/WSRunObject.cpp +++ b/editor/libeditor/WSRunObject.cpp @@ -1315,7 +1315,7 @@ WSRunObject::DeleteChars(nsINode* aStartNode, if (!range) { range = new nsRange(aStartNode); nsresult rv = - range->Set(aStartNode, aStartOffset, aEndNode, aEndOffset); + range->SetStartAndEnd(aStartNode, aStartOffset, aEndNode, aEndOffset); NS_ENSURE_SUCCESS(rv, rv); } bool nodeBefore, nodeAfter; diff --git a/editor/libeditor/crashtests/1348851.html b/editor/libeditor/crashtests/1348851.html new file mode 100644 index 0000000000..d618f049ee --- /dev/null +++ b/editor/libeditor/crashtests/1348851.html @@ -0,0 +1,19 @@ +<!DOCTYPE> +<html> +<head> +<meta charset="UTF-8"> +<script> +function boom(){ + document.designMode = "on"; + document.execCommand("insertlinebreak"); + document.designMode = "off"; + document.designMode = "on"; + document.execCommand("insertunorderedlist"); +} +addEventListener("DOMContentLoaded", boom); +</script> +</head> +<body style="display:flex;"> +<!--comment--> +</body> +</html> diff --git a/editor/libeditor/crashtests/crashtests.list b/editor/libeditor/crashtests/crashtests.list index 3fbc6b196d..7b1c57dbf3 100644 --- a/editor/libeditor/crashtests/crashtests.list +++ b/editor/libeditor/crashtests/crashtests.list @@ -68,4 +68,5 @@ load 1158452.html load 1158651.html load 1244894.xhtml load 1272490.html +load 1348851.html load 1317704.html diff --git a/editor/libeditor/nsIAbsorbingTransaction.h b/editor/libeditor/nsIAbsorbingTransaction.h index e22caed4ae..06329f3d49 100644 --- a/editor/libeditor/nsIAbsorbingTransaction.h +++ b/editor/libeditor/nsIAbsorbingTransaction.h @@ -23,6 +23,7 @@ class nsIAtom; namespace mozilla { class EditorBase; +class PlaceholderTransaction; class SelectionState; } // namespace mozilla @@ -35,9 +36,6 @@ public: NS_DECLARE_STATIC_IID_ACCESSOR(NS_IABSORBINGTRANSACTION_IID) - NS_IMETHOD Init(nsIAtom* aName, mozilla::SelectionState* aSelState, - mozilla::EditorBase* aEditorBase) = 0; - NS_IMETHOD EndPlaceHolderBatch()=0; NS_IMETHOD GetTxnName(nsIAtom **aName)=0; @@ -48,6 +46,9 @@ public: NS_IMETHOD ForwardEndBatchTo(nsIAbsorbingTransaction *aForwardingAddress)=0; NS_IMETHOD Commit()=0; + + NS_IMETHOD_(mozilla::PlaceholderTransaction*) + AsPlaceholderTransaction() = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsIAbsorbingTransaction, diff --git a/editor/libeditor/nsIEditRules.h b/editor/libeditor/nsIEditRules.h index b186895aec..a493145cc5 100644 --- a/editor/libeditor/nsIEditRules.h +++ b/editor/libeditor/nsIEditRules.h @@ -59,7 +59,7 @@ public: bool* aHandled) = 0; NS_IMETHOD DidDoAction(mozilla::dom::Selection* aSelection, mozilla::RulesInfo* aInfo, nsresult aResult) = 0; - NS_IMETHOD DocumentIsEmpty(bool* aDocumentIsEmpty) = 0; + NS_IMETHOD_(bool) DocumentIsEmpty() = 0; NS_IMETHOD DocumentModified() = 0; }; diff --git a/editor/txtsvc/nsFilteredContentIterator.cpp b/editor/txtsvc/nsFilteredContentIterator.cpp index c8ea734c4a..cccfb90ada 100644 --- a/editor/txtsvc/nsFilteredContentIterator.cpp +++ b/editor/txtsvc/nsFilteredContentIterator.cpp @@ -240,13 +240,15 @@ ContentIsInTraversalRange(nsRange* aRange, nsIDOMNode* aNextNode, bool aIsPreMod nsCOMPtr<nsIDOMNode> sNode; nsCOMPtr<nsIDOMNode> eNode; - int32_t sOffset; - int32_t eOffset; + uint32_t sOffset; + uint32_t eOffset; aRange->GetStartContainer(getter_AddRefs(sNode)); aRange->GetStartOffset(&sOffset); aRange->GetEndContainer(getter_AddRefs(eNode)); aRange->GetEndOffset(&eOffset); - return ContentIsInTraversalRange(content, aIsPreMode, sNode, sOffset, eNode, eOffset); + return ContentIsInTraversalRange(content, aIsPreMode, + sNode, static_cast<int32_t>(sOffset), + eNode, static_cast<int32_t>(eOffset)); } //------------------------------------------------------------ diff --git a/editor/txtsvc/nsTextServicesDocument.cpp b/editor/txtsvc/nsTextServicesDocument.cpp index e0c779683b..23a1bec3f7 100644 --- a/editor/txtsvc/nsTextServicesDocument.cpp +++ b/editor/txtsvc/nsTextServicesDocument.cpp @@ -406,11 +406,13 @@ nsTextServicesDocument::ExpandRangeToWordBoundaries(nsIDOMRange *aRange) // Now adjust the range so that it uses our new // end points. - - rv = range->SetEnd(rngEndNode, rngEndOffset); - NS_ENSURE_SUCCESS(rv, rv); - - return range->SetStart(rngStartNode, rngStartOffset); + nsCOMPtr<nsINode> startNode = do_QueryInterface(rngStartNode); + nsCOMPtr<nsINode> endNode = do_QueryInterface(rngEndNode); + rv = range->SetStartAndEnd(startNode, rngStartOffset, endNode, rngEndOffset); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + return NS_OK; } NS_IMETHODIMP @@ -508,7 +510,6 @@ nsTextServicesDocument::LastSelectedBlock(TSDBlockSelectionStatus *aSelStatus, nsCOMPtr<nsIContentIterator> iter; RefPtr<nsRange> range; nsCOMPtr<nsIDOMNode> parent; - int32_t rangeCount, offset; if (isCollapsed) { // We have a caret. Check if the caret is in a text node. @@ -535,6 +536,7 @@ nsTextServicesDocument::LastSelectedBlock(TSDBlockSelectionStatus *aSelStatus, return NS_ERROR_FAILURE; } + uint32_t offset; rv = range->GetStartOffset(&offset); if (NS_FAILED(rv)) { @@ -594,8 +596,9 @@ nsTextServicesDocument::LastSelectedBlock(TSDBlockSelectionStatus *aSelStatus, // position to the end of the document, then walk forwards // till you find a text node, then find the beginning of it's block. - rv = CreateDocumentContentRootToNodeOffsetRange(parent, offset, false, - getter_AddRefs(range)); + rv = CreateDocumentContentRootToNodeOffsetRange( + parent, static_cast<int32_t>(offset), false, + getter_AddRefs(range)); if (NS_FAILED(rv)) { UNLOCK_DOC(this); @@ -688,6 +691,7 @@ nsTextServicesDocument::LastSelectedBlock(TSDBlockSelectionStatus *aSelStatus, // beginning of its text block, and make it the current // block. + int32_t rangeCount; rv = selection->GetRangeCount(&rangeCount); if (NS_FAILED(rv)) { @@ -795,6 +799,7 @@ nsTextServicesDocument::LastSelectedBlock(TSDBlockSelectionStatus *aSelStatus, return NS_ERROR_FAILURE; } + uint32_t offset; rv = range->GetEndOffset(&offset); if (NS_FAILED(rv)) { @@ -802,8 +807,8 @@ nsTextServicesDocument::LastSelectedBlock(TSDBlockSelectionStatus *aSelStatus, return rv; } - rv = CreateDocumentContentRootToNodeOffsetRange(parent, offset, false, - getter_AddRefs(range)); + rv = CreateDocumentContentRootToNodeOffsetRange( + parent, static_cast<int32_t>(offset), false, getter_AddRefs(range)); if (NS_FAILED(rv)) { UNLOCK_DOC(this); @@ -2375,14 +2380,16 @@ nsTextServicesDocument::GetCollapsedSelection(nsITextServicesDocument::TSDBlockS nsCOMPtr<nsINode> parent = do_QueryInterface(domParent); MOZ_ASSERT(parent); - int32_t offset; + uint32_t offset; rv = range->GetStartOffset(&offset); NS_ENSURE_SUCCESS(rv, rv); int32_t e1s1 = nsContentUtils::ComparePoints(eStart->mNode, eStartOffset, - domParent, offset); + domParent, + static_cast<int32_t>(offset)); int32_t e2s1 = nsContentUtils::ComparePoints(eEnd->mNode, eEndOffset, - domParent, offset); + domParent, + static_cast<int32_t>(offset)); if (e1s1 > 0 || e2s1 < 0) { // We're done if the caret is outside the current text block. @@ -2399,8 +2406,8 @@ nsTextServicesDocument::GetCollapsedSelection(nsITextServicesDocument::TSDBlockS NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE); if (entry->mNode == domParent.get() && - entry->mNodeOffset <= offset && - offset <= entry->mNodeOffset + entry->mLength) { + entry->mNodeOffset <= static_cast<int32_t>(offset) && + static_cast<int32_t>(offset) <= entry->mNodeOffset + entry->mLength) { *aSelStatus = nsITextServicesDocument::eBlockContains; *aSelOffset = entry->mStrOffset + (offset - entry->mNodeOffset); *aSelLength = 0; @@ -2438,7 +2445,7 @@ nsTextServicesDocument::GetCollapsedSelection(nsITextServicesDocument::TSDBlockS // If the parent has children, position the iterator // on the child that is to the left of the offset. - uint32_t childIndex = (uint32_t)offset; + uint32_t childIndex = offset; if (childIndex > 0) { uint32_t numChildren = parent->GetChildCount(); @@ -2524,8 +2531,8 @@ nsTextServicesDocument::GetCollapsedSelection(nsITextServicesDocument::TSDBlockS NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE); if (entry->mNode == node->AsDOMNode() && - entry->mNodeOffset <= offset && - offset <= entry->mNodeOffset + entry->mLength) { + entry->mNodeOffset <= static_cast<int32_t>(offset) && + static_cast<int32_t>(offset) <= entry->mNodeOffset + entry->mLength) { *aSelStatus = nsITextServicesDocument::eBlockContains; *aSelOffset = entry->mStrOffset + (offset - entry->mNodeOffset); *aSelLength = 0; @@ -2815,9 +2822,12 @@ nsTextServicesDocument::GetRangeEndPoints(nsRange* aRange, NS_ENSURE_TRUE(aStartParent, NS_ERROR_FAILURE); - rv = aRange->GetStartOffset(aStartOffset); - - NS_ENSURE_SUCCESS(rv, rv); + uint32_t offset; + rv = aRange->GetStartOffset(&offset); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + *aStartOffset = static_cast<int32_t>(offset); rv = aRange->GetEndContainer(aEndParent); @@ -2825,7 +2835,12 @@ nsTextServicesDocument::GetRangeEndPoints(nsRange* aRange, NS_ENSURE_TRUE(aEndParent, NS_ERROR_FAILURE); - return aRange->GetEndOffset(aEndOffset); + rv = aRange->GetEndOffset(&offset); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + *aEndOffset = static_cast<int32_t>(offset); + return NS_OK; } nsresult diff --git a/embedding/components/find/nsFind.cpp b/embedding/components/find/nsFind.cpp index 63304dafb9..89221251ea 100644 --- a/embedding/components/find/nsFind.cpp +++ b/embedding/components/find/nsFind.cpp @@ -609,7 +609,7 @@ nsFind::NextNode(nsIDOMRange* aSearchRange, // beginning/end of the search range. nsCOMPtr<nsIDOMNode> startNode; nsCOMPtr<nsIDOMNode> endNode; - int32_t startOffset, endOffset; + uint32_t startOffset, endOffset; if (aContinueOk) { #ifdef DEBUG_FIND printf("Match in progress: continuing past endpoint\n"); @@ -645,7 +645,8 @@ nsFind::NextNode(nsIDOMRange* aSearchRange, } } - rv = InitIterator(startNode, startOffset, endNode, endOffset); + rv = InitIterator(startNode, static_cast<int32_t>(startOffset), + endNode, static_cast<int32_t>(endOffset)); NS_ENSURE_SUCCESS(rv, rv); if (!aStartPoint) { aStartPoint = aSearchRange; @@ -665,14 +666,18 @@ nsFind::NextNode(nsIDOMRange* aSearchRange, if (mFindBackward) { aStartPoint->GetEndContainer(getter_AddRefs(node)); if (mIterNode.get() == node.get()) { - aStartPoint->GetEndOffset(&mIterOffset); + uint32_t endOffset; + aStartPoint->GetEndOffset(&endOffset); + mIterOffset = static_cast<int32_t>(endOffset); } else { mIterOffset = -1; // sign to start from end } } else { aStartPoint->GetStartContainer(getter_AddRefs(node)); if (mIterNode.get() == node.get()) { - aStartPoint->GetStartOffset(&mIterOffset); + uint32_t startOffset; + aStartPoint->GetStartOffset(&startOffset); + mIterOffset = static_cast<int32_t>(startOffset); } else { mIterOffset = 0; } @@ -997,7 +1002,7 @@ nsFind::Find(const nsAString& aPatText, nsIDOMRange* aSearchRange, // Get the end point, so we know when to end searches: nsCOMPtr<nsIDOMNode> endNode; - int32_t endOffset; + uint32_t endOffset; aEndPoint->GetEndContainer(getter_AddRefs(endNode)); aEndPoint->GetEndOffset(&endOffset); @@ -1132,8 +1137,8 @@ nsFind::Find(const nsAString& aPatText, nsIDOMRange* aSearchRange, // Have we gone past the endpoint yet? If we have, and we're not in the // middle of a match, return. if (mIterNode == endNode && - ((mFindBackward && findex < endOffset) || - (!mFindBackward && findex > endOffset))) { + ((mFindBackward && findex < static_cast<int32_t>(endOffset)) || + (!mFindBackward && findex > static_cast<int32_t>(endOffset)))) { ResetAll(); return NS_OK; } diff --git a/embedding/components/find/nsWebBrowserFind.cpp b/embedding/components/find/nsWebBrowserFind.cpp index aadc66f8eb..5aefba975f 100644 --- a/embedding/components/find/nsWebBrowserFind.cpp +++ b/embedding/components/find/nsWebBrowserFind.cpp @@ -518,7 +518,7 @@ nsWebBrowserFind::GetSearchLimits(nsIDOMRange* aSearchRange, nsCOMPtr<nsIDOMRange> range; nsCOMPtr<nsIDOMNode> node; - int32_t offset; + uint32_t offset; // Forward, not wrapping: SelEnd to DocEnd if (!mFindBackwards && !aWrap) { diff --git a/extensions/spellcheck/src/mozInlineSpellChecker.cpp b/extensions/spellcheck/src/mozInlineSpellChecker.cpp index 96011a37e3..f423adff82 100644 --- a/extensions/spellcheck/src/mozInlineSpellChecker.cpp +++ b/extensions/spellcheck/src/mozInlineSpellChecker.cpp @@ -164,16 +164,23 @@ mozInlineSpellStatus::InitForEditorChange( NS_ENSURE_SUCCESS(rv, rv); if (cmpResult < 0) { // previous anchor node is before the current anchor - rv = mRange->SetStart(aPreviousNode, aPreviousOffset); - NS_ENSURE_SUCCESS(rv, rv); - rv = mRange->SetEnd(aAnchorNode, aAnchorOffset); + nsCOMPtr<nsINode> previousNode = do_QueryInterface(aPreviousNode); + nsCOMPtr<nsINode> anchorNode = do_QueryInterface(aAnchorNode); + rv = mRange->SetStartAndEnd(previousNode, aPreviousOffset, + anchorNode, aAnchorOffset); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } } else { // previous anchor node is after (or the same as) the current anchor - rv = mRange->SetStart(aAnchorNode, aAnchorOffset); - NS_ENSURE_SUCCESS(rv, rv); - rv = mRange->SetEnd(aPreviousNode, aPreviousOffset); + nsCOMPtr<nsINode> previousNode = do_QueryInterface(aPreviousNode); + nsCOMPtr<nsINode> anchorNode = do_QueryInterface(aAnchorNode); + rv = mRange->SetStartAndEnd(anchorNode, aAnchorOffset, + previousNode, aPreviousOffset); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } } - NS_ENSURE_SUCCESS(rv, rv); // On insert save this range: DoSpellCheck optimizes things in this range. // Otherwise, just leave this nullptr. @@ -349,19 +356,18 @@ mozInlineSpellStatus::FinishNavigationEvent(mozInlineSpellWordUtil& aWordUtil) NS_ASSERTION(mAnchorRange, "No anchor for navigation!"); nsCOMPtr<nsIDOMNode> newAnchorNode, oldAnchorNode; - int32_t newAnchorOffset, oldAnchorOffset; // get the DOM position of the old caret, the range should be collapsed nsresult rv = mOldNavigationAnchorRange->GetStartContainer( getter_AddRefs(oldAnchorNode)); NS_ENSURE_SUCCESS(rv, rv); - rv = mOldNavigationAnchorRange->GetStartOffset(&oldAnchorOffset); - NS_ENSURE_SUCCESS(rv, rv); + uint32_t oldAnchorOffset = mOldNavigationAnchorRange->StartOffset(); // find the word on the old caret position, this is the one that we MAY need // to check RefPtr<nsRange> oldWord; - rv = aWordUtil.GetRangeForWord(oldAnchorNode, oldAnchorOffset, + rv = aWordUtil.GetRangeForWord(oldAnchorNode, + static_cast<int32_t>(oldAnchorOffset), getter_AddRefs(oldWord)); NS_ENSURE_SUCCESS(rv, rv); @@ -373,15 +379,16 @@ mozInlineSpellStatus::FinishNavigationEvent(mozInlineSpellWordUtil& aWordUtil) // get the DOM position of the new caret, the range should be collapsed rv = mAnchorRange->GetStartContainer(getter_AddRefs(newAnchorNode)); NS_ENSURE_SUCCESS(rv, rv); - rv = mAnchorRange->GetStartOffset(&newAnchorOffset); - NS_ENSURE_SUCCESS(rv, rv); + uint32_t newAnchorOffset = mAnchorRange->StartOffset(); // see if the new cursor position is in the word of the old cursor position bool isInRange = false; if (! mForceNavigationWordCheck) { - rv = oldWord->IsPointInRange(newAnchorNode, - newAnchorOffset + mNewNavigationPositionOffset, - &isInRange); + rv = oldWord->IsPointInRange( + newAnchorNode, + static_cast<int32_t>( + newAnchorOffset + mNewNavigationPositionOffset), + &isInRange); NS_ENSURE_SUCCESS(rv, rv); } @@ -414,11 +421,9 @@ mozInlineSpellStatus::FillNoCheckRangeFromAnchor( nsresult rv = mAnchorRange->GetStartContainer(getter_AddRefs(anchorNode)); NS_ENSURE_SUCCESS(rv, rv); - int32_t anchorOffset; - rv = mAnchorRange->GetStartOffset(&anchorOffset); - NS_ENSURE_SUCCESS(rv, rv); - - return aWordUtil.GetRangeForWord(anchorNode, anchorOffset, + uint32_t anchorOffset = mAnchorRange->StartOffset(); + return aWordUtil.GetRangeForWord(anchorNode, + static_cast<int32_t>(anchorOffset), getter_AddRefs(mNoCheckRange)); } @@ -454,17 +459,19 @@ mozInlineSpellStatus::GetDocument(nsIDOMDocument** aDocument) nsresult mozInlineSpellStatus::PositionToCollapsedRange(nsIDOMDocument* aDocument, - nsIDOMNode* aNode, int32_t aOffset, nsIDOMRange** aRange) + nsIDOMNode* aNode, + int32_t aOffset, + nsRange** aRange) { *aRange = nullptr; - nsCOMPtr<nsIDOMRange> range; - nsresult rv = aDocument->CreateRange(getter_AddRefs(range)); - NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr<nsINode> documentNode = do_QueryInterface(aDocument); + RefPtr<nsRange> range = new nsRange(documentNode); - rv = range->SetStart(aNode, aOffset); - NS_ENSURE_SUCCESS(rv, rv); - rv = range->SetEnd(aNode, aOffset); - NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); + nsresult rv = range->CollapseTo(node, aOffset); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } range.swap(*aRange); return NS_OK; @@ -1164,9 +1171,8 @@ mozInlineSpellChecker::MakeSpellCheckRange( NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); - nsCOMPtr<nsIDOMRange> range; - rv = doc->CreateRange(getter_AddRefs(range)); - NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr<nsINode> documentNode = do_QueryInterface(doc); + RefPtr<nsRange> range = new nsRange(documentNode); // possibly use full range of the editor nsCOMPtr<nsIDOMElement> rootElem; @@ -1198,15 +1204,23 @@ mozInlineSpellChecker::MakeSpellCheckRange( if (aStartNode == aEndNode && aStartOffset == aEndOffset) return NS_OK; - rv = range->SetStart(aStartNode, aStartOffset); - NS_ENSURE_SUCCESS(rv, rv); - if (aEndOffset) - rv = range->SetEnd(aEndNode, aEndOffset); - else - rv = range->SetEndAfter(aEndNode); - NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr<nsINode> startNode = do_QueryInterface(aStartNode); + nsCOMPtr<nsINode> endNode = do_QueryInterface(aEndNode); + if (aEndOffset) { + rv = range->SetStartAndEnd(startNode, aStartOffset, endNode, aEndOffset); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } else { + uint32_t endOffset; + endNode = nsRange::GetParentAndOffsetAfter(endNode, &endOffset); + rv = range->SetStartAndEnd(startNode, aStartOffset, endNode, endOffset); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } - *aRange = static_cast<nsRange*>(range.forget().take()); + range.swap(*aRange); return NS_OK; } diff --git a/extensions/spellcheck/src/mozInlineSpellChecker.h b/extensions/spellcheck/src/mozInlineSpellChecker.h index 86d91c2c07..52261f22be 100644 --- a/extensions/spellcheck/src/mozInlineSpellChecker.h +++ b/extensions/spellcheck/src/mozInlineSpellChecker.h @@ -85,7 +85,7 @@ public: // (such as for the intial check of everything). // // For mOp == eOpNavigation, this is the NEW position of the cursor - nsCOMPtr<nsIDOMRange> mAnchorRange; + RefPtr<nsRange> mAnchorRange; // ----- // The following members are only for navigation events and are only @@ -93,7 +93,7 @@ public: // ----- // this is the OLD position of the cursor - nsCOMPtr<nsIDOMRange> mOldNavigationAnchorRange; + RefPtr<nsRange> mOldNavigationAnchorRange; // Set when we should force checking the current word. See // mozInlineSpellChecker::HandleNavigationEvent for a description of why we @@ -111,7 +111,7 @@ protected: nsresult GetDocument(nsIDOMDocument** aDocument); nsresult PositionToCollapsedRange(nsIDOMDocument* aDocument, nsIDOMNode* aNode, int32_t aOffset, - nsIDOMRange** aRange); + nsRange** aRange); }; class mozInlineSpellChecker final : public nsIInlineSpellChecker, diff --git a/extensions/spellcheck/src/mozInlineSpellWordUtil.cpp b/extensions/spellcheck/src/mozInlineSpellWordUtil.cpp index 3aef1533d1..460ac46b85 100644 --- a/extensions/spellcheck/src/mozInlineSpellWordUtil.cpp +++ b/extensions/spellcheck/src/mozInlineSpellWordUtil.cpp @@ -336,9 +336,11 @@ mozInlineSpellWordUtil::MakeRange(NodeOffset aBegin, NodeOffset aEnd, return NS_ERROR_NOT_INITIALIZED; RefPtr<nsRange> range = new nsRange(aBegin.mNode); - nsresult rv = range->Set(aBegin.mNode, aBegin.mOffset, - aEnd.mNode, aEnd.mOffset); - NS_ENSURE_SUCCESS(rv, rv); + nsresult rv = range->SetStartAndEnd(aBegin.mNode, aBegin.mOffset, + aEnd.mNode, aEnd.mOffset); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } range.forget(aRange); return NS_OK; diff --git a/js/xpconnect/tests/mochitest/test_bug1094930.html b/js/xpconnect/tests/mochitest/test_bug1094930.html index 674edfe475..285865db4c 100644 --- a/js/xpconnect/tests/mochitest/test_bug1094930.html +++ b/js/xpconnect/tests/mochitest/test_bug1094930.html @@ -15,15 +15,15 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1094930 <p id="display"></p> <script type="application/javascript"> SimpleTest.waitForExplicitFinish(); - var proto = { - connectedCallback: function() { + class XFoo extends frames[0].HTMLElement { + connectedCallback() { ok(true, "connectedCallback was called"); - SimpleTest.finish() + SimpleTest.finish(); } }; - var f = document.registerElement.call(frames[0].document, "x-foo", { prototype: proto }); - frames[0].document.firstChild.appendChild(new f()); + customElements.define.call(frames[0].customElements, "x-foo", XFoo); + frames[0].document.firstChild.appendChild(new XFoo()); </script> </body> </html> diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp index 124b5535e0..5c599e1ef6 100644 --- a/layout/base/RestyleManager.cpp +++ b/layout/base/RestyleManager.cpp @@ -146,8 +146,10 @@ RestyleManager::RestyleElement(Element* aElement, } if (aMinHint & nsChangeHint_ReconstructFrame) { - FrameConstructor()->RecreateFramesForContent(aElement, false, - nsCSSFrameConstructor::REMOVE_FOR_RECONSTRUCTION, nullptr); + FrameConstructor()->RecreateFramesForContent( + aElement, + nsCSSFrameConstructor::InsertionKind::Sync, + nsCSSFrameConstructor::REMOVE_FOR_RECONSTRUCTION); } else if (aPrimaryFrame) { ComputeAndProcessStyleChange(aPrimaryFrame, aMinHint, aRestyleTracker, aRestyleHint, aRestyleHintData); @@ -246,14 +248,14 @@ ElementForStyleContext(nsIContent* aParentContent, // Forwarded nsIDocumentObserver method, to handle restyling (and // passing the notification to the frame). -nsresult +void RestyleManager::ContentStateChanged(nsIContent* aContent, EventStates aStateMask) { // XXXbz it would be good if this function only took Elements, but // we'd have to make ESM guarantee that usefully. if (!aContent->IsElement()) { - return NS_OK; + return; } Element* aElement = aContent->AsElement(); @@ -263,7 +265,6 @@ RestyleManager::ContentStateChanged(nsIContent* aContent, ContentStateChangedInternal(aElement, aStateMask, &changeHint, &restyleHint); PostRestyleEvent(aElement, restyleHint, changeHint); - return NS_OK; } // Forwarded nsIMutationObserver method, to handle restyling. @@ -1062,6 +1063,25 @@ ElementForStyleContext(nsIContent* aParentContent, return f->GetContent()->AsElement(); } + Element* frameElement = aFrame->GetContent()->AsElement(); + if (frameElement->IsNativeAnonymous() && + nsCSSPseudoElements::PseudoElementIsJSCreatedNAC(aPseudoType)) { + // NAC-implemented pseudos use the closest non-NAC element as their + // element to inherit from. + // + // FIXME(heycam): In theory we shouldn't need to limit this only to + // JS-created pseudo-implementing NAC, as all pseudo-implementing + // should use the closest non-native anonymous ancestor element as + // its originating element. But removing that part of the condition + // reveals some bugs in style resultion with display:contents and + // XBL. See bug 1345809. + Element* originatingElement = + nsContentUtils::GetClosestNonNativeAnonymousAncestor(frameElement); + if (originatingElement) { + return originatingElement; + } + } + if (aParentContent) { return aParentContent->AsElement(); } @@ -1811,7 +1831,7 @@ ElementRestyler::ConditionallyRestyleUndisplayedNodes( } for (UndisplayedNode* undisplayed = aUndisplayed; undisplayed; - undisplayed = undisplayed->mNext) { + undisplayed = undisplayed->getNext()) { if (!undisplayed->mContent->IsElement()) { continue; @@ -2341,6 +2361,16 @@ ElementRestyler::ComputeRestyleResultFromFrame(nsIFrame* aSelf, return; } + // Each NAC element inherits from the first non-NAC ancestor, so child + // NAC may inherit from our parent instead of us. That means we can't + // cull traversal if our style context didn't change. + if (aSelf->GetContent() && aSelf->GetContent()->IsNativeAnonymous()) { + LOG_RESTYLE_CONTINUE("native anonymous content"); + aRestyleResult = RestyleResult::eContinue; + aCanStopWithStyleChange = false; + return; + } + // Style changes might have moved children between the two nsLetterFrames // (the one matching ::first-letter and the one containing the rest of the // content). Continue restyling to the children of the nsLetterFrame so @@ -3442,7 +3472,7 @@ ElementRestyler::RestyleUndisplayedNodes(nsRestyleHint aChildRestyleHint, if (undisplayed) { pusher.PushAncestorAndStyleScope(undisplayedParent); } - for (; undisplayed; undisplayed = undisplayed->mNext) { + for (; undisplayed; undisplayed = undisplayed->getNext()) { NS_ASSERTION(undisplayedParent || undisplayed->mContent == mPresContext->Document()->GetRootElement(), @@ -3459,7 +3489,7 @@ ElementRestyler::RestyleUndisplayedNodes(nsRestyleHint aChildRestyleHint, // not have a frame and would not otherwise be pushed as an ancestor. nsIContent* parent = undisplayed->mContent->GetParent(); TreeMatchContext::AutoAncestorPusher insertionPointPusher(mTreeMatchContext); - if (parent && nsContentUtils::IsContentInsertionPoint(parent)) { + if (parent && parent->IsActiveChildrenElement()) { insertionPointPusher.PushAncestorAndStyleScope(parent); } @@ -3598,14 +3628,14 @@ ElementRestyler::MustReframeForPseudo(CSSPseudoElementType aPseudoType, // Check for a ::before pseudo style and the absence of a ::before content, // but only if aFrame is null or is the first continuation/ib-split. if ((aFrame && !nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(aFrame)) || - nsLayoutUtils::GetBeforeFrameForContent(aGenConParentFrame, aContent)) { + nsLayoutUtils::GetBeforeFrame(aContent)) { return false; } } else { // Similarly for ::after, but check for being the last continuation/ // ib-split. if ((aFrame && nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame)) || - nsLayoutUtils::GetAfterFrameForContent(aGenConParentFrame, aContent)) { + nsLayoutUtils::GetAfterFrame(aContent)) { return false; } } @@ -3685,7 +3715,7 @@ ElementRestyler::RestyleContentChildren(nsIFrame* aParent, // nsPageFrame that does not have a content. nsIContent* parent = child->GetContent() ? child->GetContent()->GetParent() : nullptr; TreeMatchContext::AutoAncestorPusher insertionPointPusher(mTreeMatchContext); - if (parent && nsContentUtils::IsContentInsertionPoint(parent)) { + if (parent && parent->IsActiveChildrenElement()) { insertionPointPusher.PushAncestorAndStyleScope(parent); } diff --git a/layout/base/RestyleManager.h b/layout/base/RestyleManager.h index e22fe90581..3b60b331a6 100644 --- a/layout/base/RestyleManager.h +++ b/layout/base/RestyleManager.h @@ -59,8 +59,8 @@ public: // Forwarded nsIDocumentObserver method, to handle restyling (and // passing the notification to the frame). - nsresult ContentStateChanged(nsIContent* aContent, - EventStates aStateMask); + void ContentStateChanged(nsIContent* aContent, + EventStates aStateMask); // Forwarded nsIMutationObserver method, to handle restyling. void AttributeWillChange(Element* aElement, diff --git a/layout/base/RestyleManagerBase.cpp b/layout/base/RestyleManagerBase.cpp index 6ef048a19e..1a3cae4bfe 100644 --- a/layout/base/RestyleManagerBase.cpp +++ b/layout/base/RestyleManagerBase.cpp @@ -1249,8 +1249,10 @@ if (!mDestroyedFrames) { // We could also have problems with triggering of CSS transitions // on elements whose frames are reconstructed, since we depend on // the reconstruction happening synchronously. - frameConstructor->RecreateFramesForContent(content, false, - nsCSSFrameConstructor::REMOVE_FOR_RECONSTRUCTION, nullptr); + frameConstructor->RecreateFramesForContent( + content, + nsCSSFrameConstructor::InsertionKind::Sync, + nsCSSFrameConstructor::REMOVE_FOR_RECONSTRUCTION); } else { NS_ASSERTION(frame, "This shouldn't happen"); diff --git a/layout/base/RestyleManagerHandle.h b/layout/base/RestyleManagerHandle.h index 8be04d12c1..bee1fc6d9e 100644 --- a/layout/base/RestyleManagerHandle.h +++ b/layout/base/RestyleManagerHandle.h @@ -125,8 +125,8 @@ public: nsIContent* aChild); inline void RestyleForAppend(nsIContent* aContainer, nsIContent* aFirstNewContent); - inline nsresult ContentStateChanged(nsIContent* aContent, - EventStates aStateMask); + inline void ContentStateChanged(nsIContent* aContent, + EventStates aStateMask); inline void AttributeWillChange(dom::Element* aElement, int32_t aNameSpaceID, nsIAtom* aAttribute, diff --git a/layout/base/RestyleManagerHandleInlines.h b/layout/base/RestyleManagerHandleInlines.h index cc374edd52..8d6ca9142b 100644 --- a/layout/base/RestyleManagerHandleInlines.h +++ b/layout/base/RestyleManagerHandleInlines.h @@ -122,7 +122,7 @@ RestyleManagerHandle::Ptr::RestyleForAppend(nsIContent* aContainer, FORWARD(RestyleForAppend, (aContainer, aFirstNewContent)); } -nsresult +void RestyleManagerHandle::Ptr::ContentStateChanged(nsIContent* aContent, EventStates aStateMask) { diff --git a/layout/base/ServoRestyleManager.cpp b/layout/base/ServoRestyleManager.cpp index 42ca23bb10..0659ab8571 100644 --- a/layout/base/ServoRestyleManager.cpp +++ b/layout/base/ServoRestyleManager.cpp @@ -277,24 +277,17 @@ ServoRestyleManager::FrameForPseudoElement(const nsIContent* aContent, nsIAtom* aPseudoTagOrNull) { MOZ_ASSERT_IF(aPseudoTagOrNull, aContent->IsElement()); - nsIFrame* primaryFrame = aContent->GetPrimaryFrame(); if (!aPseudoTagOrNull) { - return primaryFrame; - } - - if (!primaryFrame) { - return nullptr; + return aContent->GetPrimaryFrame(); } - // NOTE: we probably need to special-case display: contents here. Gecko's - // RestyleManager passes the primary frame of the parent instead. if (aPseudoTagOrNull == nsCSSPseudoElements::before) { - return nsLayoutUtils::GetBeforeFrameForContent(primaryFrame, aContent); + return nsLayoutUtils::GetBeforeFrame(aContent); } if (aPseudoTagOrNull == nsCSSPseudoElements::after) { - return nsLayoutUtils::GetAfterFrameForContent(primaryFrame, aContent); + return nsLayoutUtils::GetAfterFrame(aContent); } MOZ_CRASH("Unkown pseudo-element given to " @@ -513,12 +506,12 @@ ServoRestyleManager::ContentRemoved(nsINode* aContainer, NS_WARNING("stylo: ServoRestyleManager::ContentRemoved not implemented"); } -nsresult +void ServoRestyleManager::ContentStateChanged(nsIContent* aContent, EventStates aChangedBits) { if (!aContent->IsElement()) { - return NS_OK; + return; } Element* aElement = aContent->AsElement(); @@ -552,7 +545,6 @@ ServoRestyleManager::ContentStateChanged(nsIContent* aContent, snapshot->AddState(previousState); PostRestyleEvent(aElement, restyleHint, changeHint); - return NS_OK; } void diff --git a/layout/base/ServoRestyleManager.h b/layout/base/ServoRestyleManager.h index 6856171c18..b6bb63d085 100644 --- a/layout/base/ServoRestyleManager.h +++ b/layout/base/ServoRestyleManager.h @@ -63,8 +63,7 @@ public: nsIContent* aChild); void RestyleForAppend(nsIContent* aContainer, nsIContent* aFirstNewContent); - nsresult ContentStateChanged(nsIContent* aContent, - EventStates aStateMask); + void ContentStateChanged(nsIContent* aContent, EventStates aStateMask); void AttributeWillChange(dom::Element* aElement, int32_t aNameSpaceID, nsIAtom* aAttribute, diff --git a/layout/base/crashtests/1261351-iframe.html b/layout/base/crashtests/1261351-iframe.html index 82c1e25fae..a0484f3325 100644 --- a/layout/base/crashtests/1261351-iframe.html +++ b/layout/base/crashtests/1261351-iframe.html @@ -3,23 +3,26 @@ 'use strict'; // -sp-context: content (function () { - let proto = Object.create(HTMLDivElement.prototype); - proto.template = `<style></style>`; - proto.createdCallback = function() { - let shadow = this.createShadowRoot(); - if (this.template) { - let te = document.createElement('template'); - te.innerHTML = this.template; - shadow.appendChild(document.importNode(te.content, true)); - } - }; + class UiComponentTest extends HTMLDivElement { + constructor() { + super(); + this.template = `<style></style>`; + } - let UiComponentTest = document.registerElement('ui-component-test', { - prototype: proto, - }); + connectedCallback() { + let shadow = this.createShadowRoot(); + if (this.template) { + let te = document.createElement('template'); + te.innerHTML = this.template; + shadow.appendChild(document.importNode(te.content, true)); + } + } + }; - let uic = new UiComponentTest(); - document.body.appendChild(uic); + customElements.define('ui-component-test', UiComponentTest, { extend: 'div'} ); + + let uic = new UiComponentTest(); + document.body.appendChild(uic); })(); </script> diff --git a/layout/base/crashtests/1404789-1.html b/layout/base/crashtests/1404789-1.html new file mode 100644 index 0000000000..bd577ae24d --- /dev/null +++ b/layout/base/crashtests/1404789-1.html @@ -0,0 +1,16 @@ +<!doctype html> +<script> + // Test for content redistribution outside of the document. + // Passes if it doesn't assert. + let host = document.createElement('div'); + host.innerHTML = "<div id='foo'></div>"; + + let shadowRoot = host.createShadowRoot(); + shadowRoot.innerHTML = "<content select='#foo'></content>"; + + host.firstElementChild.removeAttribute('id'); + + // Move to the document, do the same. + document.documentElement.appendChild(host); + host.firstElementChild.setAttribute('id', 'foo'); +</script> diff --git a/layout/base/crashtests/1404789-2.html b/layout/base/crashtests/1404789-2.html new file mode 100644 index 0000000000..667618141b --- /dev/null +++ b/layout/base/crashtests/1404789-2.html @@ -0,0 +1,2 @@ +<!doctype html> +<iframe style="display: none" src="1404789-1.html"></iframe> diff --git a/layout/base/crashtests/1419762.html b/layout/base/crashtests/1419762.html new file mode 100644 index 0000000000..08a56106db --- /dev/null +++ b/layout/base/crashtests/1419762.html @@ -0,0 +1,15 @@ +<style id='style_1'> + :first-child { display: table-column-group; } +</style> +<script> + try { o1 = document.createElement('isindex') } catch(e) { } + try { o2 = document.createElement('input') } catch(e) { } + try { o3 = document.createElement('optgroup') } catch(e) { } + try { o4 = document.createElement('col') } catch(e) { } + try { document.documentElement.appendChild(o1) } catch(e) { } + try { o1.appendChild(o2) } catch(e) { } + try { o1.appendChild(o3) } catch(e) { } + try { document.documentElement.offsetTop; } catch (e) { } + try { document.documentElement.appendChild(o4) } catch(e) { } + try { document.styleSheets[0].insertRule('optgroup::first-line { list-style-type: japanese-formal; }', 0); } catch(e) { } +</script> diff --git a/layout/base/crashtests/crashtests.list b/layout/base/crashtests/crashtests.list index 6ded4ff3f2..9b09d1c843 100644 --- a/layout/base/crashtests/crashtests.list +++ b/layout/base/crashtests/crashtests.list @@ -483,3 +483,6 @@ load 1308793.svg load 1308848-1.html load 1308848-2.html asserts(0-1) load 1343606.html # bug 1343948 +load 1404789-1.html +load 1404789-2.html +load 1419762.html diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 37cd3e45ee..fc458b5ebf 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -43,6 +43,7 @@ #include "nsContainerFrame.h" #include "nsNameSpaceManager.h" #include "nsIComboboxControlFrame.h" +#include "nsComboboxControlFrame.h" #include "nsIListControlFrame.h" #include "nsIDOMCharacterData.h" #include "nsPlaceholderFrame.h" @@ -691,10 +692,9 @@ nsAbsoluteItems::nsAbsoluteItems(nsContainerFrame* aContainingBlock) void nsAbsoluteItems::AddChild(nsIFrame* aChild) { - NS_ASSERTION(aChild->PresContext()->FrameManager()-> - GetPlaceholderFrameFor(aChild), - "Child without placeholder being added to nsAbsoluteItems?"); aChild->AddStateBits(NS_FRAME_OUT_OF_FLOW); + NS_ASSERTION(aChild->GetPlaceholderFrame(), + "Child without placeholder being added to nsAbsoluteItems?"); nsFrameItems::AddChild(aChild); } @@ -1831,11 +1831,7 @@ nsCSSFrameConstructor::CreateGeneratedContentItem(nsFrameConstructorState& aStat aPseudoElement == CSSPseudoElementType::after, "unexpected aPseudoElement"); - // XXXbz is this ever true? - if (!aParentContent->IsElement()) { - NS_ERROR("Bogus generated content parent"); - return; - } + MOZ_ASSERT(aParentContent->IsElement()); StyleSetHandle styleSet = mPresShell->StyleSet(); @@ -1863,7 +1859,15 @@ nsCSSFrameConstructor::CreateGeneratedContentItem(nsFrameConstructorState& aStat nsresult rv = NS_NewXMLElement(getter_AddRefs(container), nodeInfo.forget()); if (NS_FAILED(rv)) return; + + // Cleared when the pseudo is unbound from the tree, so no need to store a + // strong reference, nor a destructor. + nsIAtom* property = isBefore + ? nsGkAtoms::beforePseudoProperty : nsGkAtoms::afterPseudoProperty; + aParentContent->SetProperty(property, container.get()); + container->SetIsNativeAnonymousRoot(); + container->SetPseudoElementType(aPseudoElement); // If the parent is in a shadow tree, make sure we don't // bind with a document because shadow roots and its descendants @@ -3027,14 +3031,12 @@ nsCSSFrameConstructor::CreatePlaceholderFrameFor(nsIPresShell* aPresShell, placeholderFrame->Init(aContent, aParentFrame, aPrevInFlow); - // The placeholder frame has a pointer back to the out-of-flow frame + // Associate the placeholder/out-of-flow with each other. placeholderFrame->SetOutOfFlowFrame(aFrame); + aFrame->SetProperty(nsIFrame::PlaceholderFrameProperty(), placeholderFrame); aFrame->AddStateBits(NS_FRAME_OUT_OF_FLOW); - // Add mapping from absolutely positioned frame to its placeholder frame - aPresShell->FrameManager()->RegisterPlaceholderFrame(placeholderFrame); - return placeholderFrame; } @@ -3075,7 +3077,7 @@ nsCSSFrameConstructor::ConstructSelectFrame(nsFrameConstructorState& aState, // The drop-down list's frame is created explicitly. The combobox frame shares its content // with the drop-down list. nsFrameState flags = NS_BLOCK_FLOAT_MGR; - nsContainerFrame* comboboxFrame = + nsComboboxControlFrame* comboboxFrame = NS_NewComboboxControlFrame(mPresShell, styleContext, flags); // Save the history state so we don't restore during construction @@ -3090,10 +3092,6 @@ nsCSSFrameConstructor::ConstructSelectFrame(nsFrameConstructorState& aState, aState.AddChild(comboboxFrame, aFrameItems, content, styleContext, aParentFrame); - nsIComboboxControlFrame* comboBox = do_QueryFrame(comboboxFrame); - NS_ASSERTION(comboBox, "NS_NewComboboxControlFrame returned frame that " - "doesn't implement nsIComboboxControlFrame"); - // Resolve pseudo element style for the dropdown list RefPtr<nsStyleContext> listStyle; listStyle = mPresShell->StyleSet()-> @@ -3108,7 +3106,7 @@ nsCSSFrameConstructor::ConstructSelectFrame(nsFrameConstructorState& aState, listControlFrame->SetComboboxFrame(comboboxFrame); } // Notify combobox that it should use the listbox as it's popup - comboBox->SetDropDown(listFrame); + comboboxFrame->SetDropDown(listFrame); NS_ASSERTION(!listFrame->IsAbsPosContainingBlock(), "Ended up with positioned dropdown list somehow."); @@ -3129,10 +3127,29 @@ nsCSSFrameConstructor::ConstructSelectFrame(nsFrameConstructorState& aState, // Create display and button frames from the combobox's anonymous content. // The anonymous content is appended to existing anonymous content for this // element (the scrollbars). - nsFrameItems childItems; - CreateAnonymousFrames(aState, content, comboboxFrame, - aItem.mPendingBinding, childItems); + + // nsComboboxControlFrame needs special frame creation behavior for its first + // piece of anonymous content, which means that we can't take the normal + // ProcessChildren path. + AutoTArray<nsIAnonymousContentCreator::ContentInfo, 2> newAnonymousItems; + DebugOnly<nsresult> rv = GetAnonymousContent(content, comboboxFrame, newAnonymousItems); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + MOZ_ASSERT(newAnonymousItems.Length() == 2); + + // Manually create a frame for the special NAC. + MOZ_ASSERT(newAnonymousItems[0].mContent == comboboxFrame->GetDisplayNode()); + newAnonymousItems.RemoveElementAt(0); + nsIFrame* customFrame = comboboxFrame->CreateFrameForDisplayNode(); + MOZ_ASSERT(customFrame); + customFrame->AddStateBits(NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT); + childItems.AddChild(customFrame); + + // The other piece of NAC can take the normal path. + FrameConstructionItemList fcItems; + AddFCItemsForAnonymousContent(aState, comboboxFrame, newAnonymousItems, + fcItems); + ConstructFramesFromItemList(aState, fcItems, comboboxFrame, childItems); comboboxFrame->SetInitialChildList(kPrincipalList, childItems); @@ -3172,7 +3189,7 @@ nsCSSFrameConstructor::ConstructSelectFrame(nsFrameConstructorState& aState, * But the select tag should really be fixed to use GFX scrollbars that can * be create with BuildScrollFrame. */ -nsresult +void nsCSSFrameConstructor::InitializeSelectFrame(nsFrameConstructorState& aState, nsContainerFrame* scrollFrame, nsContainerFrame* scrolledFrame, @@ -3218,7 +3235,6 @@ nsCSSFrameConstructor::InitializeSelectFrame(nsFrameConstructorState& aState, // Set the scrolled frame's initial child lists scrolledFrame->SetInitialChildList(kPrincipalList, childItems); - return NS_OK; } nsIFrame* @@ -3805,7 +3821,7 @@ nsCSSFrameConstructor::ConstructFrameFromItemInternal(FrameConstructionItem& aIt // AutoDisplayContentsAncestorPusher above.) TreeMatchContext::AutoAncestorPusher insertionPointPusher(aState.mTreeMatchContext); - if (adcp.IsEmpty() && parent && nsContentUtils::IsContentInsertionPoint(parent)) { + if (adcp.IsEmpty() && parent && parent->IsActiveChildrenElement()) { if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) { insertionPointPusher.PushAncestorAndStyleScope(parent); } else { @@ -4094,70 +4110,6 @@ nsCSSFrameConstructor::ConstructFrameFromItemInternal(FrameConstructionItem& aIt } } -// after the node has been constructed and initialized create any -// anonymous content a node needs. -nsresult -nsCSSFrameConstructor::CreateAnonymousFrames(nsFrameConstructorState& aState, - nsIContent* aParent, - nsContainerFrame* aParentFrame, - PendingBinding* aPendingBinding, - nsFrameItems& aChildItems) -{ - AutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> newAnonymousItems; - nsresult rv = GetAnonymousContent(aParent, aParentFrame, newAnonymousItems); - NS_ENSURE_SUCCESS(rv, rv); - - uint32_t count = newAnonymousItems.Length(); - if (count == 0) { - return NS_OK; - } - - nsFrameConstructorState::PendingBindingAutoPusher pusher(aState, - aPendingBinding); - TreeMatchContext::AutoAncestorPusher ancestorPusher(aState.mTreeMatchContext); - if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) { - ancestorPusher.PushAncestorAndStyleScope(aParent->AsElement()); - } else { - ancestorPusher.PushStyleScope(aParent->AsElement()); - } - - nsIAnonymousContentCreator* creator = do_QueryFrame(aParentFrame); - NS_ASSERTION(creator, - "How can that happen if we have nodes to construct frames for?"); - - InsertionPoint insertion(aParentFrame, aParent); - for (uint32_t i=0; i < count; i++) { - nsIContent* content = newAnonymousItems[i].mContent; - NS_ASSERTION(content, "null anonymous content?"); - NS_ASSERTION(!newAnonymousItems[i].mStyleContext, "Unexpected style context"); - NS_ASSERTION(newAnonymousItems[i].mChildren.IsEmpty(), - "This method is not currently used with frames that implement " - "nsIAnonymousContentCreator::CreateAnonymousContent to " - "output a list where the items have their own children"); - - nsIFrame* newFrame = creator->CreateFrameFor(content); - if (newFrame) { - NS_ASSERTION(content->GetPrimaryFrame(), - "Content must have a primary frame now"); - newFrame->AddStateBits(NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT); - aChildItems.AddChild(newFrame); - } else { - FrameConstructionItemList items; - { - // Skip parent display based style-fixup during our - // AddFrameConstructionItems() call: - TreeMatchContext::AutoParentDisplayBasedStyleFixupSkipper - parentDisplayBasedStyleFixupSkipper(aState.mTreeMatchContext); - - AddFrameConstructionItems(aState, content, true, insertion, items); - } - ConstructFramesFromItemList(aState, items, aParentFrame, aChildItems); - } - } - - return NS_OK; -} - static void SetFlagsOnSubtree(nsIContent *aNode, uintptr_t aFlagsToSet) { @@ -4209,6 +4161,13 @@ ConnectAnonymousTreeDescendants(nsIContent* aParent, } } +void SetNativeAnonymousBitOnDescendants(nsIContent *aRoot) +{ + for (nsIContent* curr = aRoot; curr; curr = curr->GetNextNode(aRoot)) { + curr->SetFlags(NODE_IS_NATIVE_ANONYMOUS); + } +} + nsresult nsCSSFrameConstructor::GetAnonymousContent(nsIContent* aParent, nsIFrame* aParentFrame, @@ -4230,16 +4189,32 @@ nsCSSFrameConstructor::GetAnonymousContent(nsIContent* aParent, nsIContent* content = aContent[i].mContent; NS_ASSERTION(content, "null anonymous content?"); - // least-surprise CSS binding until we do the SVG specified - // cascading rules for <svg:use> - bug 265894 - if (aParentFrame->GetType() == nsGkAtoms::svgUseFrame) { + ConnectAnonymousTreeDescendants(content, aContent[i].mChildren); + + nsIAtom* parentFrameType = aParentFrame->GetType(); + if (parentFrameType == nsGkAtoms::svgUseFrame) { + // least-surprise CSS binding until we do the SVG specified + // cascading rules for <svg:use> - bug 265894 content->SetFlags(NODE_IS_ANONYMOUS_ROOT); } else { content->SetIsNativeAnonymousRoot(); + // Don't mark descendants of the custom content container + // as native anonymous. When canvas custom content is initially + // created and appended to the custom content container, in + // nsIDocument::InsertAnonymousContent, it is not considered native + // anonymous content. But if we end up reframing the root element, + // we will re-create the nsCanvasFrame, and we would end up in here, + // marking it as NAC. Existing uses of canvas custom content would + // break if it becomes NAC (since each element starts inheriting + // styles from its closest non-NAC ancestor, rather than from its + // parent). + if (!(parentFrameType == nsGkAtoms::canvasFrame && + content == static_cast<nsCanvasFrame*>(aParentFrame) + ->GetCustomContentContainer())) { + SetNativeAnonymousBitOnDescendants(content); + } } - ConnectAnonymousTreeDescendants(content, aContent[i].mChildren); - bool anonContentIsEditable = content->HasFlag(NODE_IS_EDITABLE); // If the parent is in a shadow tree, make sure we don't @@ -4264,11 +4239,9 @@ nsCSSFrameConstructor::GetAnonymousContent(nsIContent* aParent, } if (ServoStyleSet* styleSet = mPresShell->StyleSet()->GetAsServo()) { - // Eagerly compute styles for the anonymous content tree, but only do so - // if the content doesn't have an explicit style context (if it does, we - // don't need the normal computed values). + // Eagerly compute styles for the anonymous content tree. for (auto& info : aContent) { - if (!info.mStyleContext) { + if (info.mContent->IsElement()) { styleSet->StyleNewSubtree(info.mContent); } } @@ -4574,11 +4547,25 @@ nsCSSFrameConstructor::BeginBuildingScrollFrame(nsFrameConstructorState& aState, // if there are any anonymous children for the scroll frame, create // frames for them. - // Pass a null pending binding: we don't care how constructors for any of - // this anonymous content order with anything else. It's never been - // consistent anyway. - CreateAnonymousFrames(aState, aContent, gfxScrollFrame, nullptr, - anonymousItems); + // + // We can't take the normal ProcessChildren path, because the NAC needs to + // be parented to the scrollframe, and everything else needs to be parented + // to the scrolledframe. + AutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> scrollNAC; + DebugOnly<nsresult> rv = GetAnonymousContent(aContent, gfxScrollFrame, scrollNAC); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + if (scrollNAC.Length() > 0) { + TreeMatchContext::AutoAncestorPusher ancestorPusher(aState.mTreeMatchContext); + if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) { + ancestorPusher.PushAncestorAndStyleScope(aContent->AsElement()); + } else { + ancestorPusher.PushStyleScope(aContent->AsElement()); + } + + FrameConstructionItemList items; + AddFCItemsForAnonymousContent(aState, gfxScrollFrame, scrollNAC, items); + ConstructFramesFromItemList(aState, items, gfxScrollFrame, anonymousItems); + } aNewFrame = gfxScrollFrame; @@ -5033,22 +5020,46 @@ nsCSSFrameConstructor::ResolveStyleContext(const InsertionPoint& aInsertion, already_AddRefed<nsStyleContext> nsCSSFrameConstructor::ResolveStyleContext(nsStyleContext* aParentStyleContext, nsIContent* aContent, - nsFrameConstructorState* aState) + nsFrameConstructorState* aState, + Element* aOriginatingElementOrNull) { StyleSetHandle styleSet = mPresShell->StyleSet(); aContent->OwnerDoc()->FlushPendingLinkUpdates(); RefPtr<nsStyleContext> result; if (aContent->IsElement()) { - if (aState) { - result = styleSet->ResolveStyleFor(aContent->AsElement(), - aParentStyleContext, - aState->mTreeMatchContext); + auto pseudoType = aContent->AsElement()->GetPseudoElementType(); + if (pseudoType == CSSPseudoElementType::NotPseudo) { + MOZ_ASSERT(!aOriginatingElementOrNull); + if (aState) { + result = styleSet->ResolveStyleFor(aContent->AsElement(), + aParentStyleContext, + aState->mTreeMatchContext); + } else { + result = styleSet->ResolveStyleFor(aContent->AsElement(), + aParentStyleContext); + } } else { - result = styleSet->ResolveStyleFor(aContent->AsElement(), - aParentStyleContext); + MOZ_ASSERT(aContent->IsInNativeAnonymousSubtree()); + if (!aOriginatingElementOrNull) { + // For pseudo-implementing NAC created by JS using the ChromeOnly + // document.createElement(..., { pseudo: ... }) API, we find the + // originating element by lookup the tree until we find a non-NAC + // ancestor. (These are the correct semantics for C++-generated pseudo- + // implementing NAC as well, but for those cases we already have a + // correct originating element passed in.) + MOZ_ASSERT(nsCSSPseudoElements::PseudoElementIsJSCreatedNAC(pseudoType)); + aOriginatingElementOrNull = + nsContentUtils::GetClosestNonNativeAnonymousAncestor(aContent->AsElement()); + } + MOZ_ASSERT(aOriginatingElementOrNull); + result = styleSet->ResolvePseudoElementStyle(aOriginatingElementOrNull, + pseudoType, + aParentStyleContext, + aContent->AsElement()); } } else { + MOZ_ASSERT(!aOriginatingElementOrNull); NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT), "shouldn't waste time creating style contexts for " "comments and processing instructions"); @@ -6065,9 +6076,10 @@ nsCSSFrameConstructor::AddFrameConstructionItemsInternal(nsFrameConstructorState static void AddGenConPseudoToFrame(nsIFrame* aOwnerFrame, nsIContent* aContent) { - NS_ASSERTION(nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(aOwnerFrame), - "property should only be set on first continuation/ib-sibling"); + // FIXME(emilio): Remove this property, and use the frame of the generated + // content itself to tear the content down? It should be quite simpler. + aOwnerFrame = nsLayoutUtils::FirstContinuationOrIBSplitSibling(aOwnerFrame); nsIFrame::ContentArray* value = aOwnerFrame->GetProperty(nsIFrame::GenConProperty()); if (!value) { @@ -6198,16 +6210,16 @@ IsRootBoxFrame(nsIFrame *aFrame) return (aFrame->GetType() == nsGkAtoms::rootFrame); } -nsresult +void nsCSSFrameConstructor::ReconstructDocElementHierarchy() { Element* rootElement = mDocument->GetRootElement(); if (!rootElement) { /* nothing to do */ - return NS_OK; + return; } - return RecreateFramesForContent(rootElement, false, REMOVE_FOR_RECONSTRUCTION, - nullptr); + RecreateFramesForContent(rootElement, InsertionKind::Sync, + REMOVE_FOR_RECONSTRUCTION); } nsContainerFrame* @@ -6312,129 +6324,24 @@ nsCSSFrameConstructor::GetFloatContainingBlock(nsIFrame* aFrame) } /** - * This function will check whether aContainer has :after generated content. - * If so, appending to it should actually insert. The return value is the - * parent to use for newly-appended content. *aAfterFrame points to the :after - * frame before which appended content should go, if there is one. - */ -static nsContainerFrame* -AdjustAppendParentForAfterContent(nsFrameManager* aFrameManager, - nsIContent* aContainer, - nsContainerFrame* aParentFrame, - nsIContent* aChild, - nsIFrame** aAfterFrame) -{ - // If the parent frame has any pseudo-elements or aContainer is a - // display:contents node then we need to walk through the child - // frames to find the first one that is either a ::after frame for an - // ancestor of aChild or a frame that is for a node later in the - // document than aChild and return that in aAfterFrame. - if (aParentFrame->GetGenConPseudos() || - nsLayoutUtils::HasPseudoStyle(aContainer, aParentFrame->StyleContext(), - CSSPseudoElementType::after, - aParentFrame->PresContext()) || - aFrameManager->GetDisplayContentsStyleFor(aContainer)) { - nsIFrame* afterFrame = nullptr; - nsContainerFrame* parent = - static_cast<nsContainerFrame*>(aParentFrame->LastContinuation()); - bool done = false; - while (!done && parent) { - // Ensure that all normal flow children are on the principal child list. - parent->DrainSelfOverflowList(); - - nsIFrame* child = parent->GetChildList(nsIFrame::kPrincipalList).LastChild(); - if (child && child->IsPseudoFrame(aContainer) && - !child->IsGeneratedContentFrame()) { - // Drill down into non-generated pseudo frames of aContainer. - nsContainerFrame* childAsContainer = do_QueryFrame(child); - if (childAsContainer) { - parent = nsLayoutUtils::LastContinuationWithChild(childAsContainer); - continue; - } - } - - for (; child; child = child->GetPrevSibling()) { - nsIContent* c = child->GetContent(); - if (child->IsGeneratedContentFrame()) { - nsIContent* p = c->GetParent(); - if (c->NodeInfo()->NameAtom() == nsGkAtoms::mozgeneratedcontentafter) { - if (!nsContentUtils::ContentIsDescendantOf(aChild, p) && - p != aContainer && - nsContentUtils::PositionIsBefore(p, aChild)) { - // ::after generated content for content earlier in the doc and not - // for an ancestor. "p != aContainer" may seem redundant but it - // checks if the ::after belongs to the XBL insertion point we're - // inserting aChild into (in which case ContentIsDescendantOf is - // false even though p == aContainer). - // See layout/reftests/bugs/482592-1a.xhtml for an example of that. - done = true; - break; - } - } else if (nsContentUtils::PositionIsBefore(p, aChild)) { - // Non-::after generated content for content earlier in the doc. - done = true; - break; - } - } else if (nsContentUtils::PositionIsBefore(c, aChild)) { - // Content is before aChild. - done = true; - break; - } - afterFrame = child; - } - - parent = static_cast<nsContainerFrame*>(parent->GetPrevContinuation()); - } - if (afterFrame) { - *aAfterFrame = afterFrame; - return afterFrame->GetParent(); - } - } - - *aAfterFrame = nullptr; - - if (IsFramePartOfIBSplit(aParentFrame)) { - // We might be in a situation where the last part of the {ib} split was - // empty. Since we have no ::after pseudo-element, we do in fact want to be - // appending to that last part, so advance to it if needed. Note that here - // aParentFrame is the result of a GetLastIBSplitSibling call, so must be - // either the last or next to last ib-split sibling. - nsContainerFrame* trailingInline = GetIBSplitSibling(aParentFrame); - if (trailingInline) { - aParentFrame = trailingInline; - } - - // Always make sure to look at the last continuation of the frame - // for the {ib} case, even if that continuation is empty. We - // don't do this for the non-ib-split-frame case, since in the - // other cases appending to the last nonempty continuation is fine - // and in fact not doing that can confuse code that doesn't know - // to pull kids from continuations other than its next one. - aParentFrame = - static_cast<nsContainerFrame*>(aParentFrame->LastContinuation()); - } - - return aParentFrame; -} - -/** * This function will get the previous sibling to use for an append operation. - * it takes a parent frame (must not be null) and its :after frame (may be - * null). + * + * It takes a parent frame (must not be null) and the next insertion sibling, if + * the parent content is display: contents or has ::after content (may be null). */ static nsIFrame* -FindAppendPrevSibling(nsIFrame* aParentFrame, nsIFrame* aAfterFrame) +FindAppendPrevSibling(nsIFrame* aParentFrame, nsIFrame* aNextSibling) { - if (aAfterFrame) { - NS_ASSERTION(aAfterFrame->GetParent() == aParentFrame, "Wrong parent"); - NS_ASSERTION(aAfterFrame->GetPrevSibling() || - aParentFrame->PrincipalChildList().FirstChild() == aAfterFrame, - ":after frame must be on the principal child list here"); - return aAfterFrame->GetPrevSibling(); - } - aParentFrame->DrainSelfOverflowList(); + if (aNextSibling) { + MOZ_ASSERT(aNextSibling->GetParent() == aParentFrame, "Wrong parent"); + MOZ_ASSERT(aNextSibling->GetPrevSibling() || + aParentFrame->PrincipalChildList().FirstChild() == aNextSibling, + "next sibling must be on the principal child list here"); + return aNextSibling->GetPrevSibling(); + } + return aParentFrame->GetChildList(kPrincipalList).LastChild(); } @@ -6457,7 +6364,7 @@ GetInsertNextSibling(nsIFrame* aParentFrame, nsIFrame* aPrevSibling) * appending flowed frames to a parent's principal child list. It handles the * case where the parent is the trailing inline of an {ib} split. */ -nsresult +void nsCSSFrameConstructor::AppendFramesToParent(nsFrameConstructorState& aState, nsContainerFrame* aParentFrame, nsFrameItems& aFrameList, @@ -6542,13 +6449,11 @@ nsCSSFrameConstructor::AppendFramesToParent(nsFrameConstructorState& aStat return AppendFramesToParent(aState, aParentFrame->GetParent(), ibSiblings, aParentFrame, true); } - - return NS_OK; + return; } // Insert the frames after our aPrevSibling InsertFrames(aParentFrame, kPrincipalList, aPrevSibling, aFrameList); - return NS_OK; } #define UNSET_DISPLAY static_cast<StyleDisplay>(255) @@ -6650,111 +6555,147 @@ nsCSSFrameConstructor::IsValidSibling(nsIFrame* aSibling, return true; } +// FIXME(emilio): If we ever kill IsValidSibling() we can simplify this quite a +// bit (no need to pass aTargetContent or aTargetContentDisplay, and the +// adjust() calls can be responsibility of the caller). +template<nsCSSFrameConstructor::SiblingDirection aDirection> nsIFrame* -nsCSSFrameConstructor::FindFrameForContentSibling(nsIContent* aContent, - nsIContent* aTargetContent, - StyleDisplay& aTargetContentDisplay, - nsContainerFrame* aParentFrame, - bool aPrevSibling) -{ - nsIFrame* sibling = aContent->GetPrimaryFrame(); - if (!sibling && GetDisplayContentsStyleFor(aContent)) { - // A display:contents node - check if it has a ::before / ::after frame... - sibling = aPrevSibling ? - nsLayoutUtils::GetAfterFrameForContent(aParentFrame, aContent) : - nsLayoutUtils::GetBeforeFrameForContent(aParentFrame, aContent); - if (!sibling) { - // ... then recurse into children ... - const bool forward = !aPrevSibling; - FlattenedChildIterator iter(aContent, forward); - sibling = aPrevSibling ? - FindPreviousSibling(iter, aTargetContent, aTargetContentDisplay, aParentFrame) : - FindNextSibling(iter, aTargetContent, aTargetContentDisplay, aParentFrame); - } - if (!sibling) { - // ... then ::after / ::before on the opposite end. - sibling = aPrevSibling ? - nsLayoutUtils::GetBeforeFrameForContent(aParentFrame, aContent) : - nsLayoutUtils::GetAfterFrameForContent(aParentFrame, aContent); - } - if (!sibling) { - return nullptr; +nsCSSFrameConstructor::FindSiblingInternal( + FlattenedChildIterator aIter, + nsIContent* aTargetContent, + StyleDisplay& aTargetContentDisplay) +{ + auto adjust = [&](nsIFrame* aPotentialSiblingFrame) -> nsIFrame* { + return AdjustSiblingFrame( + aPotentialSiblingFrame, aTargetContent, aTargetContentDisplay, + aDirection); + }; + + auto nextDomSibling = [](FlattenedChildIterator& aIter) -> nsIContent* { + return aDirection == SiblingDirection::Forward + ? aIter.GetNextChild() : aIter.GetPreviousChild(); + }; + + auto getNearPseudo = [](const nsIContent* aContent) -> nsIFrame* { + return aDirection == SiblingDirection::Forward + ? nsLayoutUtils::GetBeforeFrame(aContent) + : nsLayoutUtils::GetAfterFrame(aContent); + }; + + auto getFarPseudo = [](const nsIContent* aContent) -> nsIFrame* { + return aDirection == SiblingDirection::Forward + ? nsLayoutUtils::GetAfterFrame(aContent) + : nsLayoutUtils::GetBeforeFrame(aContent); + }; + + while (nsIContent* sibling = nextDomSibling(aIter)) { + if (nsIFrame* primaryFrame = sibling->GetPrimaryFrame()) { + // XXX the GetContent() == sibling check is needed due to bug 135040. + // Remove it once that's fixed. + if (primaryFrame->GetContent() == sibling) { + if (nsIFrame* frame = adjust(primaryFrame)) { + return frame; + } + } + } + + if (GetDisplayContentsStyleFor(sibling)) { + if (nsIFrame* frame = adjust(getNearPseudo(sibling))) { + return frame; + } + + const bool startFromBeginning = aDirection == SiblingDirection::Forward; + FlattenedChildIterator iter(sibling, startFromBeginning); + nsIFrame* sibling = FindSiblingInternal<aDirection>( + iter, aTargetContent, aTargetContentDisplay); + if (sibling) { + return sibling; + } } - } else if (!sibling || sibling->GetContent() != aContent) { - // XXX the GetContent() != aContent check is needed due to bug 135040. - // Remove it once that's fixed. - return nullptr; } - // If the frame is out-of-flow, GetPrimaryFrame() will have returned the - // out-of-flow frame; we want the placeholder. - if (sibling->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { - nsIFrame* placeholderFrame = GetPlaceholderFrameFor(sibling); - NS_ASSERTION(placeholderFrame, "no placeholder for out-of-flow frame"); - sibling = placeholderFrame; + return adjust(getFarPseudo(aIter.Parent())); +} + +nsIFrame* +nsCSSFrameConstructor::AdjustSiblingFrame( + nsIFrame* aSibling, + nsIContent* aTargetContent, + mozilla::StyleDisplay& aTargetContentDisplay, + SiblingDirection aDirection) +{ + if (!aSibling) { + return nullptr; } - // The frame we have now should never be a continuation - NS_ASSERTION(!sibling->GetPrevContinuation(), "How did that happen?"); + if (aSibling->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { + aSibling = aSibling->GetPlaceholderFrame(); + MOZ_ASSERT(aSibling); + } - if (aPrevSibling) { - // The frame may be a ib-split frame (a split inline frame that - // contains a block). Get the last part of that split. - if (IsFramePartOfIBSplit(sibling)) { - sibling = GetLastIBSplitSibling(sibling, true); + MOZ_ASSERT(!aSibling->GetPrevContinuation(), "How?"); + if (aDirection == SiblingDirection::Backward) { + // The frame may be a ib-split frame (a split inline frame that contains a + // block). Get the last part of that split. + if (IsFramePartOfIBSplit(aSibling)) { + aSibling = GetLastIBSplitSibling(aSibling, true); } // The frame may have a continuation. If so, we want the last // non-overflow-container continuation as our previous sibling. - sibling = sibling->GetTailContinuation(); + aSibling = aSibling->GetTailContinuation(); } - if (aTargetContent && - !IsValidSibling(sibling, aTargetContent, aTargetContentDisplay)) { - sibling = nullptr; + if (!IsValidSibling(aSibling, aTargetContent, aTargetContentDisplay)) { + return nullptr; } - return sibling; + return aSibling; } nsIFrame* -nsCSSFrameConstructor::FindPreviousSibling(FlattenedChildIterator aIter, - nsIContent* aTargetContent, - StyleDisplay& aTargetContentDisplay, - nsContainerFrame* aParentFrame) +nsCSSFrameConstructor::FindPreviousSibling(const FlattenedChildIterator& aIter, + StyleDisplay& aTargetContentDisplay) { - // Note: not all content objects are associated with a frame (e.g., if it's - // `display: none') so keep looking until we find a previous frame. - while (nsIContent* sibling = aIter.GetPreviousChild()) { - MOZ_ASSERT(sibling != aTargetContent); - nsIFrame* prevSibling = - FindFrameForContentSibling(sibling, aTargetContent, aTargetContentDisplay, - aParentFrame, true); - if (prevSibling) { - // Found a previous sibling, we're done! - return prevSibling; - } - } + return FindSibling<SiblingDirection::Backward>(aIter, aTargetContentDisplay); +} - return nullptr; +nsIFrame* +nsCSSFrameConstructor::FindNextSibling(const FlattenedChildIterator& aIter, + StyleDisplay& aTargetContentDisplay) +{ + return FindSibling<SiblingDirection::Forward>(aIter, aTargetContentDisplay); } +template<nsCSSFrameConstructor::SiblingDirection aDirection> nsIFrame* -nsCSSFrameConstructor::FindNextSibling(FlattenedChildIterator aIter, - nsIContent* aTargetContent, - StyleDisplay& aTargetContentDisplay, - nsContainerFrame* aParentFrame) +nsCSSFrameConstructor::FindSibling(const FlattenedChildIterator& aIter, + StyleDisplay& aTargetContentDisplay) { - while (nsIContent* sibling = aIter.GetNextChild()) { - MOZ_ASSERT(sibling != aTargetContent); - nsIFrame* nextSibling = - FindFrameForContentSibling(sibling, aTargetContent, aTargetContentDisplay, - aParentFrame, false); + nsIContent* targetContent = aIter.Get(); + nsIFrame* sibling = + FindSiblingInternal<aDirection>(aIter, targetContent, aTargetContentDisplay); + if (sibling) { + return sibling; + } + + // Our siblings (if any) do not have a frame to guide us. The frame for the + // target content should be inserted whereever a frame for the container would + // be inserted. This is needed when inserting into display: contents nodes. + const nsIContent* current = aIter.Parent(); + while (GetDisplayContentsStyleFor(current)) { + const nsIContent* parent = current->GetFlattenedTreeParent(); + MOZ_ASSERT(parent, "No display: contents on the root"); - if (nextSibling) { - // We found a next sibling, we're done! - return nextSibling; + FlattenedChildIterator iter(parent); + iter.Seek(current); + sibling = FindSiblingInternal<aDirection>( + iter, targetContent, aTargetContentDisplay); + if (sibling) { + return sibling; } + + current = parent; } return nullptr; @@ -6820,8 +6761,7 @@ nsCSSFrameConstructor::GetInsertionPrevSibling(InsertionPoint* aInsertion, // Note that FindPreviousSibling is passed the iterator by value, so that // the later usage of the iterator starts from the same place. StyleDisplay childDisplay = UNSET_DISPLAY; - nsIFrame* prevSibling = - FindPreviousSibling(iter, iter.Get(), childDisplay, aInsertion->mParentFrame); + nsIFrame* prevSibling = FindPreviousSibling(iter, childDisplay); // Now, find the geometric parent so that we can handle // continuations properly. Use the prev sibling if we have it; @@ -6834,48 +6774,20 @@ nsCSSFrameConstructor::GetInsertionPrevSibling(InsertionPoint* aInsertion, iter.Seek(aEndSkipChild); iter.GetPreviousChild(); } - nsIFrame* nextSibling = - FindNextSibling(iter, iter.Get(), childDisplay, aInsertion->mParentFrame); - if (GetDisplayContentsStyleFor(aInsertion->mContainer)) { - if (!nextSibling) { - // Our siblings (if any) does not have a frame to guide us. - // The frame for aChild should be inserted whereever a frame for - // the container would be inserted. This is needed when inserting - // into nested display:contents nodes. - nsIContent* child = aInsertion->mContainer; - nsIContent* parent = child->GetParent(); - aInsertion->mParentFrame = - ::GetAdjustedParentFrame(aInsertion->mParentFrame, - aInsertion->mParentFrame->GetType(), - parent); - InsertionPoint fakeInsertion(aInsertion->mParentFrame, parent); - nsIFrame* result = GetInsertionPrevSibling(&fakeInsertion, child, aIsAppend, - aIsRangeInsertSafe, nullptr, nullptr); - MOZ_ASSERT(aInsertion->mParentFrame->GetContent() == - fakeInsertion.mParentFrame->GetContent()); - // fakeInsertion.mParentFrame may now be a continuation of the frame - // we started with in the ctor above. - aInsertion->mParentFrame = fakeInsertion.mParentFrame; - return result; - } - - prevSibling = nextSibling->GetPrevSibling(); - } - - if (nextSibling) { + if (nsIFrame* nextSibling = FindNextSibling(iter, childDisplay)) { aInsertion->mParentFrame = nextSibling->GetParent()->GetContentInsertionFrame(); } else { // No previous or next sibling, so treat this like an appended frame. *aIsAppend = true; if (IsFramePartOfIBSplit(aInsertion->mParentFrame)) { // Since we're appending, we'll walk to the last anonymous frame - // that was created for the broken inline frame. But don't walk - // to the trailing inline if it's empty; stop at the block. + // that was created for the broken inline frame. We can walk to the + // trailing inline, since we know this is a real append, and not an + // insert (that would've been handled by `FindNextSibling`). aInsertion->mParentFrame = - GetLastIBSplitSibling(aInsertion->mParentFrame, false); + GetLastIBSplitSibling(aInsertion->mParentFrame, true); } - // Get continuation that parents the last child. This MUST be done - // before the AdjustAppendParentForAfterContent call. + // Get continuation that parents the last child. aInsertion->mParentFrame = nsLayoutUtils::LastContinuationWithChild(aInsertion->mParentFrame); // Deal with fieldsets @@ -6883,12 +6795,7 @@ nsCSSFrameConstructor::GetInsertionPrevSibling(InsertionPoint* aInsertion, ::GetAdjustedParentFrame(aInsertion->mParentFrame, aInsertion->mParentFrame->GetType(), aChild); - nsIFrame* appendAfterFrame; - aInsertion->mParentFrame = - ::AdjustAppendParentForAfterContent(this, aInsertion->mContainer, - aInsertion->mParentFrame, - aChild, &appendAfterFrame); - prevSibling = ::FindAppendPrevSibling(aInsertion->mParentFrame, appendAfterFrame); + prevSibling = ::FindAppendPrevSibling(aInsertion->mParentFrame, nullptr); } } @@ -7004,7 +6911,8 @@ nsCSSFrameConstructor::ReframeTextIfNeeded(nsIContent* aParentContent, } NS_ASSERTION(!aContent->GetPrimaryFrame(), "Text node has a frame and NS_CREATE_FRAME_IF_NON_WHITESPACE"); - ContentInserted(aParentContent, aContent, nullptr, false); + const bool allowLazyConstruction = true; + ContentInserted(aParentContent, aContent, nullptr, allowLazyConstruction); } // For inserts aChild should be valid, for appends it should be null. @@ -7014,11 +6922,8 @@ nsCSSFrameConstructor::MaybeConstructLazily(Operation aOperation, nsIContent* aContainer, nsIContent* aChild) { - // XXXmats no lazy frames for display:contents direct descendants yet - // (Mozilla bug 979782). if (mPresShell->GetPresContext()->IsChrome() || !aContainer || - aContainer->IsInNativeAnonymousSubtree() || aContainer->IsXULElement() || - GetDisplayContentsStyleFor(aContainer)) { + aContainer->IsInNativeAnonymousSubtree() || aContainer->IsXULElement()) { return false; } @@ -7054,8 +6959,7 @@ nsCSSFrameConstructor::MaybeConstructLazily(Operation aOperation, // hit a node with a leaf frame. // // Also, it's fine if one of the nodes without primary frame is a display: - // contents node except if it's the direct ancestor of the children we're - // recreating frames for. + // contents node. bool noPrimaryFrame = false; bool needsFrameBitSet = false; #endif @@ -7287,8 +7191,9 @@ nsCSSFrameConstructor::MaybeRecreateForFrameset(nsIFrame* aParentFrame, cur = cur->GetNextSibling()) { if (IsSpecialFramesetChild(cur)) { // Just reframe the parent, since framesets are weird like that. - RecreateFramesForContent(aParentFrame->GetContent(), false, - REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(aParentFrame->GetContent(), + InsertionKind::Sync, + REMOVE_FOR_RECONSTRUCTION); return true; } } @@ -7296,7 +7201,7 @@ nsCSSFrameConstructor::MaybeRecreateForFrameset(nsIFrame* aParentFrame, return false; } -nsresult +void nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer, nsIContent* aFirstNewContent, bool aAllowLazyConstruction) @@ -7339,8 +7244,8 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer, if (tag == nsGkAtoms::treechildren || tag == nsGkAtoms::treeitem || tag == nsGkAtoms::treerow) - return NS_OK; + return; } #endif // MOZ_XUL @@ -7353,21 +7258,21 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer, //XXXsmaug This is super unefficient! nsIContent* bindingParent = aContainer->GetBindingParent(); LAYOUT_PHASE_TEMP_EXIT(); - nsresult rv = RecreateFramesForContent(bindingParent, false, - REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(bindingParent, InsertionKind::Sync, + REMOVE_FOR_RECONSTRUCTION); LAYOUT_PHASE_TEMP_REENTER(); - return rv; + return; } // See comment in ContentRangeInserted for why this is necessary. if (!GetContentInsertionFrameFor(aContainer) && !aContainer->IsActiveChildrenElement()) { - return NS_OK; + return; } if (aAllowLazyConstruction && MaybeConstructLazily(CONTENTAPPEND, aContainer, aFirstNewContent)) { - return NS_OK; + return; } LAYOUT_PHASE_TEMP_EXIT(); @@ -7377,13 +7282,13 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer, nsContainerFrame*& parentFrame = insertion.mParentFrame; LAYOUT_PHASE_TEMP_REENTER(); if (!parentFrame) { - return NS_OK; + return; } LAYOUT_PHASE_TEMP_EXIT(); if (MaybeRecreateForFrameset(parentFrame, aFirstNewContent, nullptr)) { LAYOUT_PHASE_TEMP_REENTER(); - return NS_OK; + return; } LAYOUT_PHASE_TEMP_REENTER(); @@ -7391,21 +7296,22 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer, // Nothing to do here; we shouldn't be constructing kids of leaves // Clear lazy bits so we don't try to construct again. ClearLazyBits(aFirstNewContent, nullptr); - return NS_OK; + return; } if (parentFrame->IsFrameOfType(nsIFrame::eMathML)) { LAYOUT_PHASE_TEMP_EXIT(); - nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), false, - REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(parentFrame->GetContent(), + InsertionKind::Sync, + REMOVE_FOR_RECONSTRUCTION); LAYOUT_PHASE_TEMP_REENTER(); - return rv; + return; } // If the frame we are manipulating is a ib-split frame (that is, one // that's been created as a result of a block-in-inline situation) then we // need to append to the last ib-split sibling, not to the frame itself. - bool parentIBSplit = IsFramePartOfIBSplit(parentFrame); + const bool parentIBSplit = IsFramePartOfIBSplit(parentFrame); if (parentIBSplit) { #ifdef DEBUG if (gNoisyContentUpdates) { @@ -7431,38 +7337,79 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer, parentFrame->GetType() != nsGkAtoms::detailsFrame, "Parent frame should not be fieldset or details!"); - // Deal with possible :after generated content on the parent - nsIFrame* parentAfterFrame; - parentFrame = - ::AdjustAppendParentForAfterContent(this, insertion.mContainer, parentFrame, - aFirstNewContent, &parentAfterFrame); + // Deal with possible :after generated content on the parent, or display: + // contents. + nsIFrame* nextSibling = nullptr; + if (GetDisplayContentsStyleFor(insertion.mContainer) || + nsLayoutUtils::GetAfterFrame(insertion.mContainer)) { + FlattenedChildIterator iter(insertion.mContainer); + iter.Seek(insertion.mContainer->GetLastChild()); + StyleDisplay unused = UNSET_DISPLAY; + nextSibling = FindNextSibling(iter, unused); + } - // Create some new frames - nsFrameConstructorState state(mPresShell, - GetAbsoluteContainingBlock(parentFrame, FIXED_POS), - GetAbsoluteContainingBlock(parentFrame, ABS_POS), - GetFloatContainingBlock(parentFrame)); - state.mTreeMatchContext.InitAncestors(aContainer->AsElement()); + if (nextSibling) { + parentFrame = nextSibling->GetParent()->GetContentInsertionFrame(); + } else if (parentIBSplit) { + // We might be in a situation where the last part of the {ib} split was + // empty. Since we have no ::after pseudo-element, we do in fact want to be + // appending to that last part, so advance to it if needed. Note that here + // aParentFrame is the result of a GetLastIBSplitSibling call, so must be + // either the last or next to last ib-split sibling. + if (nsContainerFrame* trailingInline = GetIBSplitSibling(parentFrame)) { + parentFrame = trailingInline; + } - // See if the containing block has :first-letter style applied. - bool haveFirstLetterStyle = false, haveFirstLineStyle = false; - nsContainerFrame* containingBlock = state.mFloatedItems.containingBlock; - if (containingBlock) { - haveFirstLetterStyle = HasFirstLetterStyle(containingBlock); - haveFirstLineStyle = - ShouldHaveFirstLineStyle(containingBlock->GetContent(), - containingBlock->StyleContext()); + // Always make sure to look at the last continuation of the frame + // for the {ib} case, even if that continuation is empty. We + // don't do this for the non-ib-split-frame case, since in the + // other cases appending to the last nonempty continuation is fine + // and in fact not doing that can confuse code that doesn't know + // to pull kids from continuations other than its next one. + parentFrame = + static_cast<nsContainerFrame*>(parentFrame->LastContinuation()); } + nsContainerFrame* containingBlock = GetFloatContainingBlock(parentFrame); + + // See if the containing block has :first-letter style applied. + const bool haveFirstLetterStyle = + containingBlock && HasFirstLetterStyle(containingBlock); + + const bool haveFirstLineStyle = + containingBlock && + ShouldHaveFirstLineStyle(containingBlock->GetContent(), + containingBlock->StyleContext()); + if (haveFirstLetterStyle) { + nsWeakFrame wf(nextSibling); // Before we get going, remove the current letter frames - RemoveLetterFrames(state.mPresShell, containingBlock); + RemoveLetterFrames(mPresShell, containingBlock); + + // Reget nextSibling, since we may have killed it. + if (nextSibling && !wf) { + FlattenedChildIterator iter(insertion.mContainer); + iter.Seek(insertion.mContainer->GetLastChild()); + StyleDisplay unused = UNSET_DISPLAY; + nextSibling = FindNextSibling(iter, unused); + if (nextSibling) { + parentFrame = nextSibling->GetParent()->GetContentInsertionFrame(); + containingBlock = GetFloatContainingBlock(parentFrame); + } + } } + // Create some new frames + nsFrameConstructorState state(mPresShell, + GetAbsoluteContainingBlock(parentFrame, FIXED_POS), + GetAbsoluteContainingBlock(parentFrame, ABS_POS), + containingBlock); + state.mTreeMatchContext.InitAncestors(aContainer->AsElement()); + nsIAtom* frameType = parentFrame->GetType(); FlattenedChildIterator iter(aContainer); - bool haveNoXBLChildren = (!iter.XBLInvolved() || !iter.GetNextChild()); + const bool haveNoXBLChildren = !iter.XBLInvolved() || !iter.GetNextChild(); FrameConstructionItemList items; if (aFirstNewContent->GetPreviousSibling() && GetParentType(frameType) == eTypeBlock && @@ -7487,7 +7434,7 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer, AddFrameConstructionItems(state, child, false, insertion, items); } - nsIFrame* prevSibling = ::FindAppendPrevSibling(parentFrame, parentAfterFrame); + nsIFrame* prevSibling = ::FindAppendPrevSibling(parentFrame, nextSibling); // Perform special check for diddling around with the frames in // a ib-split inline frame. @@ -7497,7 +7444,7 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer, if (WipeContainingBlock(state, containingBlock, parentFrame, items, true, prevSibling)) { LAYOUT_PHASE_TEMP_REENTER(); - return NS_OK; + return; } LAYOUT_PHASE_TEMP_REENTER(); @@ -7510,8 +7457,11 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer, !prevSibling->IsInlineOutside() || prevSibling->GetType() == nsGkAtoms::brFrame); // :after content can't be <br> so no need to check it - items.SetLineBoundaryAtEnd(!parentAfterFrame || - !parentAfterFrame->IsInlineOutside()); + // + // FIXME(emilio): A display: contents sibling could! Write a test-case and + // fix. + items.SetLineBoundaryAtEnd( + !nextSibling || !nextSibling->IsInlineOutside()); } // To suppress whitespace-only text frames, we have to verify that // our container's DOM child list matches its flattened tree child list. @@ -7584,7 +7534,7 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer, } #endif - return NS_OK; + return; } #ifdef MOZ_XUL @@ -7627,17 +7577,17 @@ bool NotifyListBoxBody(nsPresContext* aPresContext, } #endif // MOZ_XUL -nsresult +void nsCSSFrameConstructor::ContentInserted(nsIContent* aContainer, nsIContent* aChild, nsILayoutHistoryState* aFrameState, bool aAllowLazyConstruction) { - return ContentRangeInserted(aContainer, - aChild, - aChild->GetNextSibling(), - aFrameState, - aAllowLazyConstruction); + ContentRangeInserted(aContainer, + aChild, + aChild->GetNextSibling(), + aFrameState, + aAllowLazyConstruction); } // ContentRangeInserted handles creating frames for a range of nodes that @@ -7658,7 +7608,7 @@ nsCSSFrameConstructor::ContentInserted(nsIContent* aContainer, // in the caption list, while skipping any nodes in the range being inserted // (because when we treat the caption frames the other nodes have had their // frames constructed but not yet inserted into the frame tree). -nsresult +void nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, nsIContent* aStartChild, nsIContent* aEndChild, @@ -7715,7 +7665,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, // The insert case in NotifyListBoxBody // doesn't use "old next sibling". aStartChild, nullptr, nullptr, CONTENT_INSERTED)) { - return NS_OK; + return; } } else { // We don't handle a range insert to a listbox parent, issue single @@ -7724,7 +7674,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, IssueSingleInsertNofications(aContainer, aStartChild, aEndChild, aAllowLazyConstruction); LAYOUT_PHASE_TEMP_REENTER(); - return NS_OK; + return; } } #endif // MOZ_XUL @@ -7739,7 +7689,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, if (aStartChild != docElement) { // Not the root element; just bail out - return NS_OK; + return; } NS_PRECONDITION(nullptr == mRootElementFrame, @@ -7776,7 +7726,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, } #endif - return NS_OK; + return; } if (aContainer->HasFlag(NODE_IS_IN_SHADOW_TREE) && @@ -7789,10 +7739,10 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, //XXXsmaug This is super unefficient! nsIContent* bindingParent = aContainer->GetBindingParent(); LAYOUT_PHASE_TEMP_EXIT(); - nsresult rv = RecreateFramesForContent(bindingParent, false, - REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(bindingParent, InsertionKind::Sync, + REMOVE_FOR_RECONSTRUCTION); LAYOUT_PHASE_TEMP_REENTER(); - return rv; + return; } // Put 'parentFrame' inside a scope so we don't confuse it with @@ -7803,7 +7753,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, // a parent. While its uncommon to change the structure of the default content itself, a label, // for example, can be reframed by having its value attribute set or removed. if (!parentFrame && !aContainer->IsActiveChildrenElement()) { - return NS_OK; + return; } // Otherwise, we've got parent content. Find its frame. @@ -7812,7 +7762,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, if (aAllowLazyConstruction && MaybeConstructLazily(CONTENTINSERT, aContainer, aStartChild)) { - return NS_OK; + return; } } @@ -7832,7 +7782,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, } if (!insertion.mParentFrame) { - return NS_OK; + return; } bool isAppend, isRangeInsertSafe; @@ -7846,16 +7796,14 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, IssueSingleInsertNofications(aContainer, aStartChild, aEndChild, aAllowLazyConstruction); LAYOUT_PHASE_TEMP_REENTER(); - return NS_OK; + return; } - nsIContent* container = insertion.mParentFrame->GetContent(); - nsIAtom* frameType = insertion.mParentFrame->GetType(); LAYOUT_PHASE_TEMP_EXIT(); if (MaybeRecreateForFrameset(insertion.mParentFrame, aStartChild, aEndChild)) { LAYOUT_PHASE_TEMP_REENTER(); - return NS_OK; + return; } LAYOUT_PHASE_TEMP_REENTER(); @@ -7872,10 +7820,11 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, // and if so, proceed. But we'd have to extend nsFieldSetFrame // to locate this legend in the inserted frames and extract it. LAYOUT_PHASE_TEMP_EXIT(); - nsresult rv = RecreateFramesForContent(insertion.mParentFrame->GetContent(), false, - REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(insertion.mParentFrame->GetContent(), + InsertionKind::Sync, + REMOVE_FOR_RECONSTRUCTION); LAYOUT_PHASE_TEMP_REENTER(); - return rv; + return; } // We should only get here with details when doing a single insertion because @@ -7887,26 +7836,27 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, // expensive to recreate the entire details frame, but it's the simplest way // to handle the insertion. LAYOUT_PHASE_TEMP_EXIT(); - nsresult rv = - RecreateFramesForContent(insertion.mParentFrame->GetContent(), false, - REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(insertion.mParentFrame->GetContent(), + InsertionKind::Sync, + REMOVE_FOR_RECONSTRUCTION); LAYOUT_PHASE_TEMP_REENTER(); - return rv; + return; } // Don't construct kids of leaves if (insertion.mParentFrame->IsLeaf()) { // Clear lazy bits so we don't try to construct again. ClearLazyBits(aStartChild, aEndChild); - return NS_OK; + return; } if (insertion.mParentFrame->IsFrameOfType(nsIFrame::eMathML)) { LAYOUT_PHASE_TEMP_EXIT(); - nsresult rv = RecreateFramesForContent(insertion.mParentFrame->GetContent(), false, - REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(insertion.mParentFrame->GetContent(), + InsertionKind::Sync, + REMOVE_FOR_RECONSTRUCTION); LAYOUT_PHASE_TEMP_REENTER(); - return rv; + return; } nsFrameConstructorState state(mPresShell, @@ -7956,7 +7906,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, // the placeholder frame. if (insertion.mParentFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { nsPlaceholderFrame* placeholderFrame = - GetPlaceholderFrameFor(insertion.mParentFrame); + insertion.mParentFrame->GetPlaceholderFrame(); NS_ASSERTION(placeholderFrame, "No placeholder for out-of-flow?"); insertion.mParentFrame = placeholderFrame->GetParent(); } else { @@ -7983,30 +7933,13 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, IssueSingleInsertNofications(aContainer, aStartChild, aEndChild, aAllowLazyConstruction); LAYOUT_PHASE_TEMP_REENTER(); - return NS_OK; + return; } - container = insertion.mParentFrame->GetContent(); frameType = insertion.mParentFrame->GetType(); } } - if (!prevSibling) { - // We're inserting the new frames as the first child. See if the - // parent has a :before pseudo-element - nsIFrame* firstChild = insertion.mParentFrame->PrincipalChildList().FirstChild(); - - if (firstChild && - nsLayoutUtils::IsGeneratedContentFor(container, firstChild, - nsCSSPseudoElements::before)) { - // Insert the new frames after the last continuation of the :before - prevSibling = firstChild->GetTailContinuation(); - insertion.mParentFrame = prevSibling->GetParent()->GetContentInsertionFrame(); - // Don't change isAppend here; we'll can call AppendFrames as needed, and - // the change to our prevSibling doesn't affect that. - } - } - FrameConstructionItemList items; ParentType parentType = GetParentType(frameType); FlattenedChildIterator iter(aContainer); @@ -8051,7 +7984,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, if (WipeContainingBlock(state, containingBlock, insertion.mParentFrame, items, isAppend, prevSibling)) { LAYOUT_PHASE_TEMP_REENTER(); - return NS_OK; + return; } LAYOUT_PHASE_TEMP_REENTER(); @@ -8075,65 +8008,11 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, } } - // If the parent of our current prevSibling is different from the frame we'll - // actually use as the parent, then the calculated insertion point is now - // invalid and as it is unknown where to insert correctly we append instead - // (bug 341858). - // This can affect our prevSibling and isAppend, but should not have any - // effect on the WipeContainingBlock above, since this should only happen - // when neither parent is a ib-split frame and should not affect whitespace - // handling inside table-related frames (and in fact, can only happen when - // one of the parents is a table wrapper and one is an inner table or when the - // parent is a fieldset or fieldset content frame). So it won't affect the - // {ib} or XUL box cases in WipeContainingBlock(), and the table pseudo - // handling will only be affected by us maybe thinking we're not inserting - // at the beginning, whereas we really are. That would have made us reframe - // unnecessarily, but that's ok. - // XXXbz we should push our frame construction item code up higher, so we - // know what our items are by the time we start figuring out previous - // siblings - if (prevSibling && frameItems.NotEmpty() && - frameItems.FirstChild()->GetParent() != prevSibling->GetParent()) { -#ifdef DEBUG - nsIFrame* frame1 = frameItems.FirstChild()->GetParent(); - nsIFrame* frame2 = prevSibling->GetParent(); - NS_ASSERTION(!IsFramePartOfIBSplit(frame1) && - !IsFramePartOfIBSplit(frame2), - "Neither should be ib-split"); - NS_ASSERTION((frame1->GetType() == nsGkAtoms::tableFrame && - frame2->GetType() == nsGkAtoms::tableWrapperFrame) || - (frame1->GetType() == nsGkAtoms::tableWrapperFrame && - frame2->GetType() == nsGkAtoms::tableFrame) || - frame1->GetType() == nsGkAtoms::fieldSetFrame || - (frame1->GetParent() && - frame1->GetParent()->GetType() == nsGkAtoms::fieldSetFrame), - "Unexpected frame types"); -#endif - isAppend = true; - nsIFrame* appendAfterFrame; - insertion.mParentFrame = - ::AdjustAppendParentForAfterContent(this, container, - frameItems.FirstChild()->GetParent(), - aStartChild, &appendAfterFrame); - prevSibling = ::FindAppendPrevSibling(insertion.mParentFrame, appendAfterFrame); - } - - if (haveFirstLineStyle && insertion.mParentFrame == containingBlock) { + if (haveFirstLineStyle && insertion.mParentFrame == containingBlock && isAppend) { // It's possible that the new frame goes into a first-line // frame. Look at it and see... - if (isAppend) { - // Use append logic when appending - AppendFirstLineFrames(state, containingBlock->GetContent(), - containingBlock, frameItems); - } - else { - // Use more complicated insert logic when inserting - // XXXbz this method is a no-op, so it's easy for the args being passed - // here to make no sense without anyone noticing... If it ever stops - // being a no-op, vet them carefully! - InsertFirstLineFrames(state, container, containingBlock, &insertion.mParentFrame, - prevSibling, frameItems); - } + AppendFirstLineFrames(state, containingBlock->GetContent(), + containingBlock, frameItems); } // We might have captions; put them into the caption list of the @@ -8216,32 +8095,35 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, #endif #ifdef ACCESSIBILITY - nsAccessibilityService* accService = nsIPresShell::AccService(); - if (accService) { + if (nsAccessibilityService* accService = nsIPresShell::AccService()) { accService->ContentRangeInserted(mPresShell, aContainer, aStartChild, aEndChild); } #endif - return NS_OK; + return; } -nsresult -nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, - nsIContent* aChild, - nsIContent* aOldNextSibling, - RemoveFlags aFlags, - bool* aDidReconstruct, - nsIContent** aDestroyedFramesFor) -{ +void +nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, + nsIContent* aChild, + nsIContent* aOldNextSibling, + RemoveFlags aFlags, + bool* aDidReconstruct) +{ + MOZ_ASSERT(aChild); + MOZ_ASSERT(aDidReconstruct); AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC); NS_PRECONDITION(mUpdateCount != 0, "Should be in an update while destroying frames"); *aDidReconstruct = false; - if (aDestroyedFramesFor) { - *aDestroyedFramesFor = aChild; - } + + // Only recreate sync if we're already in frame construction, that is, + // recreate async for XBL DOM changes, and normal content removals. + const InsertionKind insertionKind = (aFlags == REMOVE_FOR_RECONSTRUCTION) + ? InsertionKind::Sync + : InsertionKind::Async; nsPresContext* presContext = mPresShell->GetPresContext(); MOZ_ASSERT(presContext, "Our presShell should have a valid presContext"); @@ -8274,7 +8156,6 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, } #endif - nsresult rv = NS_OK; nsIFrame* childFrame = aChild->GetPrimaryFrame(); if (!childFrame || childFrame->GetContent() != aChild) { // XXXbz the GetContent() != aChild check is needed due to bug 135040. @@ -8284,37 +8165,33 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, MOZ_ASSERT(!childFrame || !GetDisplayContentsStyleFor(aChild), "display:contents nodes shouldn't have a frame"); if (!childFrame && GetDisplayContentsStyleFor(aChild)) { - nsIFrame* ancestorFrame = nullptr; - nsIContent* ancestor = aContainer; - for (; ancestor; ancestor = ancestor->GetParent()) { - ancestorFrame = ancestor->GetPrimaryFrame(); - if (ancestorFrame) { - break; - } + nsIContent* ancestor = aChild->GetFlattenedTreeParent(); + MOZ_ASSERT(ancestor, "display: contents on the root?"); + while (!ancestor->GetPrimaryFrame()) { + ancestor = ancestor->GetFlattenedTreeParent(); + MOZ_ASSERT(ancestor, "we can't have a display: contents subtree root!"); } - if (ancestorFrame) { - nsIFrame* contentInsertion = ancestorFrame->GetContentInsertionFrame(); - if (ancestorFrame->GetGenConPseudos() || - (contentInsertion && contentInsertion->GetGenConPseudos())) { - *aDidReconstruct = true; - LAYOUT_PHASE_TEMP_EXIT(); - // XXXmats Can we recreate frames only for the ::after/::before content? - // XXX Perhaps even only those that belong to the aChild sub-tree? - RecreateFramesForContent(ancestor, false, aFlags, aDestroyedFramesFor); - LAYOUT_PHASE_TEMP_REENTER(); - return NS_OK; - } + + nsIFrame* ancestorFrame = ancestor->GetPrimaryFrame(); + if (ancestorFrame->GetProperty(nsIFrame::GenConProperty())) { + *aDidReconstruct = true; + + // XXXmats Can we recreate frames only for the ::after/::before content? + // XXX Perhaps even only those that belong to the aChild sub-tree? + LAYOUT_PHASE_TEMP_EXIT(); + RecreateFramesForContent(ancestor, insertionKind, aFlags); + LAYOUT_PHASE_TEMP_REENTER(); + return; } FlattenedChildIterator iter(aChild); for (nsIContent* c = iter.GetNextChild(); c; c = iter.GetNextChild()) { if (c->GetPrimaryFrame() || GetDisplayContentsStyleFor(c)) { LAYOUT_PHASE_TEMP_EXIT(); - rv = ContentRemoved(aChild, c, nullptr, aFlags, aDidReconstruct, aDestroyedFramesFor); + ContentRemoved(aChild, c, nullptr, aFlags, aDidReconstruct); LAYOUT_PHASE_TEMP_REENTER(); - NS_ENSURE_SUCCESS(rv, rv); if (aFlags != REMOVE_DESTROY_FRAMES && *aDidReconstruct) { - return rv; + return; } } } @@ -8327,7 +8204,7 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, if (aFlags == REMOVE_DESTROY_FRAMES) { CaptureStateForFramesOf(aChild, mTempFrameTreeState); } - return NS_OK; + return; } #endif // MOZ_XUL @@ -8365,10 +8242,9 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, nsIContent* bindingParent = aContainer->GetBindingParent(); *aDidReconstruct = true; LAYOUT_PHASE_TEMP_EXIT(); - nsresult rv = RecreateFramesForContent(bindingParent, false, - aFlags, aDestroyedFramesFor); + RecreateFramesForContent(bindingParent, insertionKind, aFlags); LAYOUT_PHASE_TEMP_REENTER(); - return rv; + return; } if (aFlags == REMOVE_DESTROY_FRAMES) { @@ -8380,15 +8256,11 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, // See whether we need to remove more than just childFrame LAYOUT_PHASE_TEMP_EXIT(); - nsIContent* container; - if (MaybeRecreateContainerForFrameRemoval(childFrame, aFlags, &rv, &container)) { - LAYOUT_PHASE_TEMP_REENTER(); - MOZ_ASSERT(container); + if (MaybeRecreateContainerForFrameRemoval( + childFrame, insertionKind, aFlags)) { *aDidReconstruct = true; - if (aDestroyedFramesFor) { - *aDestroyedFramesFor = container; - } - return rv; + LAYOUT_PHASE_TEMP_REENTER(); + return; } LAYOUT_PHASE_TEMP_REENTER(); @@ -8401,10 +8273,10 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, // Just reframe the parent, since framesets are weird like that. *aDidReconstruct = true; LAYOUT_PHASE_TEMP_EXIT(); - nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), false, - aFlags, aDestroyedFramesFor); + RecreateFramesForContent(parentFrame->GetContent(), insertionKind, + aFlags); LAYOUT_PHASE_TEMP_REENTER(); - return rv; + return; } // If we're a child of MathML, then we should reframe the MathML content. @@ -8415,10 +8287,9 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, if (possibleMathMLAncestor->IsFrameOfType(nsIFrame::eMathML)) { *aDidReconstruct = true; LAYOUT_PHASE_TEMP_EXIT(); - nsresult rv = RecreateFramesForContent(possibleMathMLAncestor->GetContent(), - false, aFlags, aDestroyedFramesFor); + RecreateFramesForContent(parentFrame->GetContent(), insertionKind, aFlags); LAYOUT_PHASE_TEMP_REENTER(); - return rv; + return; } // Undo XUL wrapping if it's no longer needed. @@ -8432,15 +8303,14 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, !AnyKidsNeedBlockParent(childFrame->GetNextSibling())) { *aDidReconstruct = true; LAYOUT_PHASE_TEMP_EXIT(); - nsresult rv = RecreateFramesForContent(grandparentFrame->GetContent(), true, - aFlags, aDestroyedFramesFor); + RecreateFramesForContent(grandparentFrame->GetContent(), insertionKind, + aFlags); LAYOUT_PHASE_TEMP_REENTER(); - return rv; + return; } #ifdef ACCESSIBILITY - nsAccessibilityService* accService = nsIPresShell::AccService(); - if (accService) { + if (nsAccessibilityService* accService = nsIPresShell::AccService()) { accService->ContentRemoved(mPresShell, aChild); } #endif @@ -8449,7 +8319,7 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, // :first-letter style applies. nsIFrame* inflowChild = childFrame; if (childFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { - inflowChild = GetPlaceholderFrameFor(childFrame); + inflowChild = childFrame->GetPlaceholderFrame(); NS_ASSERTION(inflowChild, "No placeholder for out-of-flow?"); } nsContainerFrame* containingBlock = @@ -8479,7 +8349,7 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, // XXXbz the GetContent() != aChild check is needed due to bug 135040. // Remove it once that's fixed. ClearUndisplayedContentIn(aChild, aContainer); - return NS_OK; + return; } parentFrame = childFrame->GetParent(); parentType = parentFrame->GetType(); @@ -8505,7 +8375,7 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, // Notify the parent frame that it should delete the frame if (childFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { - childFrame = GetPlaceholderFrameFor(childFrame); + childFrame = childFrame->GetPlaceholderFrame(); NS_ASSERTION(childFrame, "Missing placeholder frame for out of flow."); parentFrame = childFrame->GetParent(); } @@ -8543,6 +8413,9 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, // and hence it's adjacent to a block end. // If aOldNextSibling is null, then the text node before the node being // removed is the last node, and we don't need to worry about it. + // + // FIXME(emilio): This should probably use the lazy frame construction + // bits if possible instead of reframing it in place. if (aOldNextSibling) { nsIContent* prevSibling = aOldNextSibling->GetPreviousSibling(); if (prevSibling && prevSibling->GetPreviousSibling()) { @@ -8569,7 +8442,7 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, #endif } - return rv; + return; } /** @@ -8631,12 +8504,11 @@ nsCSSFrameConstructor::EnsureFrameForTextNode(nsGenericDOMDataNode* aContent) return aContent->GetPrimaryFrame(); } -nsresult +void nsCSSFrameConstructor::CharacterDataChanged(nsIContent* aContent, CharacterDataChangeInfo* aInfo) { AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC); - nsresult rv = NS_OK; if ((aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) && !aContent->TextIsOnlyWhitespace()) || @@ -8648,10 +8520,10 @@ nsCSSFrameConstructor::CharacterDataChanged(nsIContent* aContent, "Bit should never be set on generated content"); #endif LAYOUT_PHASE_TEMP_EXIT(); - nsresult rv = RecreateFramesForContent(aContent, false, - REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(aContent, InsertionKind::Sync, + REMOVE_FOR_RECONSTRUCTION); LAYOUT_PHASE_TEMP_REENTER(); - return rv; + return; } // Find the child frame @@ -8699,8 +8571,6 @@ nsCSSFrameConstructor::CharacterDataChanged(nsIContent* aContent, RecoverLetterFrames(block); } } - - return rv; } void @@ -9149,7 +9019,7 @@ nsCSSFrameConstructor::ReplicateFixedFrames(nsPageContentFrame* aParentFrame) // are within fixed frames, because that would cause duplicates on the new // page - bug 389619) for (nsIFrame* fixed = firstFixed; fixed; fixed = fixed->GetNextSibling()) { - nsIFrame* prevPlaceholder = GetPlaceholderFrameFor(fixed); + nsIFrame* prevPlaceholder = fixed->GetPlaceholderFrame(); if (prevPlaceholder && nsLayoutUtils::IsProperAncestorFrame(prevCanvasFrame, prevPlaceholder)) { // We want to use the same style as the primary style frame for @@ -9314,7 +9184,8 @@ nsCSSFrameConstructor::MaybeRecreateFramesForElement(Element* aElement) } } - RecreateFramesForContent(aElement, false, REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(aElement, InsertionKind::Sync, + REMOVE_FOR_RECONSTRUCTION); return nullptr; } @@ -9357,19 +9228,16 @@ FindPreviousNonWhitespaceSibling(nsIFrame* aFrame) } bool -nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame, - RemoveFlags aFlags, - nsresult* aResult, - nsIContent** aDestroyedFramesFor) +nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval( + nsIFrame* aFrame, + InsertionKind aInsertionKind, + RemoveFlags aFlags) { NS_PRECONDITION(aFrame, "Must have a frame"); NS_PRECONDITION(aFrame->GetParent(), "Frame shouldn't be root"); - NS_PRECONDITION(aResult, "Null out param?"); NS_PRECONDITION(aFrame == aFrame->FirstContinuation(), "aFrame not the result of GetPrimaryFrame()?"); - *aDestroyedFramesFor = nullptr; - if (IsFramePartOfIBSplit(aFrame)) { // The removal functions can't handle removal of an {ib} split directly; we // need to rebuild the containing block. @@ -9382,44 +9250,44 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame, } #endif - *aResult = ReframeContainingBlock(aFrame, aFlags, aDestroyedFramesFor); + ReframeContainingBlock(aFrame, aInsertionKind, aFlags); return true; } nsContainerFrame* insertionFrame = aFrame->GetContentInsertionFrame(); if (insertionFrame && insertionFrame->GetType() == nsGkAtoms::legendFrame && aFrame->GetParent()->GetType() == nsGkAtoms::fieldSetFrame) { - // When we remove the legend for a fieldset, we should reframe - // the fieldset to ensure another legend is used, if there is one - *aResult = RecreateFramesForContent(aFrame->GetParent()->GetContent(), false, - aFlags, aDestroyedFramesFor); + RecreateFramesForContent(aFrame->GetParent()->GetContent(), aInsertionKind, + aFlags); return true; } - if (insertionFrame && - aFrame->GetParent()->GetType() == nsGkAtoms::detailsFrame) { + nsIFrame* inFlowFrame = + (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) ? + aFrame->GetPlaceholderFrame() : aFrame; + MOZ_ASSERT(inFlowFrame, "How did that happen?"); + MOZ_ASSERT(inFlowFrame == inFlowFrame->FirstContinuation(), + "placeholder for primary frame has previous continuations?"); + nsIFrame* parent = inFlowFrame->GetParent(); + + if (parent && parent->GetType() == nsGkAtoms::detailsFrame) { HTMLSummaryElement* summary = - HTMLSummaryElement::FromContent(insertionFrame->GetContent()); + HTMLSummaryElement::FromContent(aFrame->GetContent()); + DetailsFrame* detailsFrame = static_cast<DetailsFrame*>(parent); - if (summary && summary->IsMainSummary()) { + // Unlike adding summary element cases, we need to check children of the + // parent details frame since at this moment the summary element has been + // already removed from the parent details element's child list. + if (summary && detailsFrame->HasMainSummaryFrame(aFrame)) { // When removing a summary, we should reframe the parent details frame to // ensure that another summary is used or the default summary is // generated. - *aResult = RecreateFramesForContent(aFrame->GetParent()->GetContent(), - false, REMOVE_FOR_RECONSTRUCTION, - aDestroyedFramesFor); + RecreateFramesForContent(parent->GetContent(), aInsertionKind, aFlags); return true; } } // Now check for possibly needing to reconstruct due to a pseudo parent - nsIFrame* inFlowFrame = - (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) ? - GetPlaceholderFrameFor(aFrame) : aFrame; - MOZ_ASSERT(inFlowFrame, "How did that happen?"); - MOZ_ASSERT(inFlowFrame == inFlowFrame->FirstContinuation(), - "placeholder for primary frame has previous continuations?"); - nsIFrame* parent = inFlowFrame->GetParent(); // For the case of ruby pseudo parent, effectively, only pseudo rb/rt frame // need to be checked here, since all other types of parent will be catched // by "Check ruby containers" section below. @@ -9437,10 +9305,7 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame, // Similar if we're a table-caption. (inFlowFrame->IsTableCaption() && parent->GetChildList(nsIFrame::kCaptionList).FirstChild() == inFlowFrame)) { - // We're the first or last frame in the pseudo. Need to reframe. - // Good enough to recreate frames for |parent|'s content - *aResult = RecreateFramesForContent(parent->GetContent(), true, aFlags, - aDestroyedFramesFor); + RecreateFramesForContent(parent->GetContent(), aInsertionKind, aFlags); return true; } } @@ -9469,8 +9334,7 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame, #endif // Good enough to recreate frames for aFrame's parent's content; even if // aFrame's parent is a pseudo, that'll be the right content node. - *aResult = RecreateFramesForContent(parent->GetContent(), true, aFlags, - aDestroyedFramesFor); + RecreateFramesForContent(parent->GetContent(), aInsertionKind, aFlags); return true; } } @@ -9486,8 +9350,7 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame, // frames may be constructed or destroyed accordingly. // 2. The type of the first child of a ruby frame determines // whether a pseudo ruby base container should exist. - *aResult = RecreateFramesForContent(parent->GetContent(), true, aFlags, - aDestroyedFramesFor); + RecreateFramesForContent(parent->GetContent(), aInsertionKind, aFlags); return true; } @@ -9510,8 +9373,7 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame, } #endif // DEBUG // Recreate frames for the flex container (the removed frame's parent) - *aResult = RecreateFramesForContent(parent->GetContent(), true, aFlags, - aDestroyedFramesFor); + RecreateFramesForContent(parent->GetContent(), aInsertionKind, aFlags); return true; } @@ -9529,8 +9391,8 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame, } #endif // DEBUG // Recreate frames for the flex container (the removed frame's grandparent) - *aResult = RecreateFramesForContent(parent->GetParent()->GetContent(), true, - aFlags, aDestroyedFramesFor); + RecreateFramesForContent(parent->GetParent()->GetContent(), aInsertionKind, + aFlags); return true; } @@ -9538,7 +9400,7 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame, if (aFrame->GetType() == nsGkAtoms::popupSetFrame) { nsIRootBox* rootBox = nsIRootBox::GetRootBox(mPresShell); if (rootBox && rootBox->GetPopupSetFrame() == aFrame) { - *aResult = ReconstructDocElementHierarchy(); + ReconstructDocElementHierarchy(); return true; } } @@ -9550,8 +9412,7 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame, !inFlowFrame->GetNextSibling() && ((parent->GetPrevContinuation() && !parent->GetPrevInFlow()) || (parent->GetNextContinuation() && !parent->GetNextInFlow()))) { - *aResult = RecreateFramesForContent(parent->GetContent(), true, aFlags, - aDestroyedFramesFor); + RecreateFramesForContent(parent->GetContent(), aInsertionKind, aFlags); return true; } @@ -9587,24 +9448,26 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame, } #endif - *aResult = ReframeContainingBlock(parent, aFlags, aDestroyedFramesFor); + ReframeContainingBlock(parent, aInsertionKind, aFlags); return true; } -nsresult -nsCSSFrameConstructor::RecreateFramesForContent(nsIContent* aContent, - bool aAsyncInsert, - RemoveFlags aFlags, - nsIContent** aDestroyedFramesFor) +void +nsCSSFrameConstructor::RecreateFramesForContent(nsIContent* aContent, + InsertionKind aInsertionKind, + RemoveFlags aFlags) { - NS_PRECONDITION(!aAsyncInsert || aContent->IsElement(), - "Can only insert elements async"); + MOZ_ASSERT(aInsertionKind != InsertionKind::Async || aContent->IsElement()); + MOZ_ASSERT(aContent); + // If there is no document, we don't want to recreate frames for it. (You // shouldn't generally be giving this method content without a document // anyway). // Rebuilding the frame tree can have bad effects, especially if it's the // frame tree for chrome (see bug 157322). - NS_ENSURE_TRUE(aContent->GetComposedDoc(), NS_ERROR_FAILURE); + if (NS_WARN_IF(!aContent->GetComposedDoc())) { + return; + } // Is the frame ib-split? If so, we need to reframe the containing // block *here*, rather than trying to remove and re-insert the @@ -9630,8 +9493,8 @@ nsCSSFrameConstructor::RecreateFramesForContent(nsIContent* aContent, if (frame) { nsIFrame* nonGeneratedAncestor = nsLayoutUtils::GetNonGeneratedAncestor(frame); if (nonGeneratedAncestor->GetContent() != aContent) { - return RecreateFramesForContent(nonGeneratedAncestor->GetContent(), aAsyncInsert, - aFlags, aDestroyedFramesFor); + return RecreateFramesForContent(nonGeneratedAncestor->GetContent(), + aInsertionKind, aFlags); } if (frame->GetStateBits() & NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT) { @@ -9651,8 +9514,8 @@ nsCSSFrameConstructor::RecreateFramesForContent(nsIContent* aContent, if (ancestor->GetType() != nsGkAtoms::svgUseFrame) { NS_ASSERTION(aContent->IsInNativeAnonymousSubtree(), "Why is NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT set?"); - return RecreateFramesForContent(ancestor->GetContent(), aAsyncInsert, - aFlags, aDestroyedFramesFor); + return RecreateFramesForContent(ancestor->GetContent(), aInsertionKind, + aFlags); } } @@ -9663,20 +9526,13 @@ nsCSSFrameConstructor::RecreateFramesForContent(nsIContent* aContent, // with native anonymous content from the editor. if (parent && parent->IsLeaf() && parentContent && parentContent != aContent) { - return RecreateFramesForContent(parentContent, aAsyncInsert, aFlags, - aDestroyedFramesFor); + return RecreateFramesForContent(parentContent, aInsertionKind, aFlags); } } - nsresult rv = NS_OK; - nsIContent* container; - if (frame && MaybeRecreateContainerForFrameRemoval(frame, aFlags, &rv, - &container)) { - MOZ_ASSERT(container); - if (aDestroyedFramesFor) { - *aDestroyedFramesFor = container; - } - return rv; + if (frame && + MaybeRecreateContainerForFrameRemoval(frame, aInsertionKind, aFlags)) { + return; } nsINode* containerNode = aContent->GetParentNode(); @@ -9688,47 +9544,44 @@ nsCSSFrameConstructor::RecreateFramesForContent(nsIContent* aContent, // Need the nsIContent parent, which might be null here, since we need to // pass it to ContentInserted and ContentRemoved. + // + // FIXME(emilio): Do we need a strong ref here? nsCOMPtr<nsIContent> container = aContent->GetParent(); // Remove the frames associated with the content object. bool didReconstruct; nsIContent* nextSibling = aContent->IsRootOfAnonymousSubtree() ? nullptr : aContent->GetNextSibling(); - const bool reconstruct = aFlags != REMOVE_DESTROY_FRAMES; - RemoveFlags flags = reconstruct ? REMOVE_FOR_RECONSTRUCTION : aFlags; - rv = ContentRemoved(container, aContent, nextSibling, flags, - &didReconstruct, aDestroyedFramesFor); - if (NS_FAILED(rv)) { - return rv; - } - if (reconstruct && !didReconstruct) { - // Now, recreate the frames associated with this content object. If - // ContentRemoved triggered reconstruction, then we don't need to do this - // because the frames will already have been built. - if (aAsyncInsert) { + ContentRemoved(container, aContent, nextSibling, aFlags, &didReconstruct); + if (!didReconstruct) { + if (aInsertionKind == InsertionKind::Async) { // XXXmats doesn't frame state need to be restored in this case too? - RestyleManager()->PostRestyleEvent( - aContent->AsElement(), nsRestyleHint(0), nsChangeHint_ReconstructFrame); + RestyleManager()->PostRestyleEvent(aContent->AsElement(), + nsRestyleHint(0), + nsChangeHint_ReconstructFrame); } else { - rv = ContentInserted(container, aContent, mTempFrameTreeState, false); + // Now, recreate the frames associated with this content object. If + // ContentRemoved triggered reconstruction, then we don't need to do this + // because the frames will already have been built. + ContentInserted(container, aContent, mTempFrameTreeState, false); } } } - - return rv; } void -nsCSSFrameConstructor::DestroyFramesFor(nsIContent* aContent, - nsIContent** aDestroyedFramesFor) +nsCSSFrameConstructor::DestroyFramesFor(nsIContent* aContent, + bool* aDidReconstruct) { MOZ_ASSERT(aContent && aContent->GetParentNode()); - bool didReconstruct; nsIContent* nextSibling = aContent->IsRootOfAnonymousSubtree() ? nullptr : aContent->GetNextSibling(); - ContentRemoved(aContent->GetParent(), aContent, nextSibling, - REMOVE_DESTROY_FRAMES, &didReconstruct, aDestroyedFramesFor); + ContentRemoved(aContent->GetParent(), + aContent, + nextSibling, + REMOVE_DESTROY_FRAMES, + aDidReconstruct); } ////////////////////////////////////////////////////////////////////// @@ -10660,13 +10513,6 @@ nsCSSFrameConstructor::AddFCItemsForAnonymousContent( { for (uint32_t i = 0; i < aAnonymousItems.Length(); ++i) { nsIContent* content = aAnonymousItems[i].mContent; -#ifdef DEBUG - nsIAnonymousContentCreator* creator = do_QueryFrame(aFrame); - NS_ASSERTION(!creator || !creator->CreateFrameFor(content), - "If you need to use CreateFrameFor, you need to call " - "CreateAnonymousFrames manually and not follow the standard " - "ProcessChildren() codepath for this frame"); -#endif // Gecko-styled nodes should have no pending restyle flags. MOZ_ASSERT_IF(!content->IsStyledByServo(), !content->IsElement() || @@ -10684,24 +10530,111 @@ nsCSSFrameConstructor::AddFCItemsForAnonymousContent( RefPtr<nsStyleContext> styleContext; TreeMatchContext::AutoParentDisplayBasedStyleFixupSkipper parentDisplayBasedStyleFixupSkipper(aState.mTreeMatchContext); - if (aAnonymousItems[i].mStyleContext) { - // If we have an explicit style context, that means that the anonymous - // content creator had its own plan for the style, and doesn't need the - // computed style obtained by cascading this content as a normal node. - // This happens when a native anonymous node is used to implement a - // pseudo-element. Allowing Servo to traverse these nodes would be wasted - // work, so assert that we didn't do that. - MOZ_ASSERT_IF(content->IsStyledByServo(), !content->HasServoData()); - styleContext = aAnonymousItems[i].mStyleContext.forget(); - } else { - // If we don't have an explicit style context, that means we need the - // ordinary computed values. Make sure we eagerly cascaded them when the - // anonymous nodes were created. - MOZ_ASSERT_IF(content->IsStyledByServo() && content->IsElement(), - content->HasServoData()); - styleContext = ResolveStyleContext(aFrame, content, &aState); + + // Make sure we eagerly performed the servo cascade when the anonymous + // nodes were created. + MOZ_ASSERT_IF(content->IsStyledByServo() && content->IsElement(), + content->AsElement()->HasServoData()); + + // Determine whether this NAC is pseudo-implementing. + nsIAtom* pseudo = nullptr; + if (content->IsElement()) { + auto pseudoType = content->AsElement()->GetPseudoElementType(); + if (pseudoType != CSSPseudoElementType::NotPseudo) { + pseudo = nsCSSPseudoElements::GetPseudoAtom(pseudoType); + } + } + + // Determine the appropriate parent style for this NAC, and if the NAC + // implements a pseudo-element, the appropriate originating element + // (that is to say, the element to the left of the ::pseudo-element in + // the selector). This is all rather tricky, and merits some discussion. + // + // First, it's important to note that author stylesheets generally do not + // apply to elements in native-anonymous subtrees. The exceptions to + // this are web-exposed pseudo-elements, where authors can style the + // pseudo-implementing NAC if the originating element is not itself in a NAC + // subtree. + // + // For this reason, it's very important that we avoid using a style parent + // that is inside a NAC subtree together with an originating element that + // is not inside a NAC subtree, since that would allow authors to + // explicitly inherit styles from internal elements, potentially making + // the NAC hierarchy observable. To ensure this, and generally simplify + // things, we always set the originating element to the style parent. + // + // As a consequence of the above, all web-exposed pseudo-elements (which, + // by definition, must have a content-accessible originating element) must + // also inherit style from that same content-accessible element. To avoid + // unintuitive behavior differences between NAC elements that do and don't + // correspond to web-exposed pseudo-elements, we follow this protocol for + // all NAC, pseudo-implementing or not. + // + // However, things get tricky with the <video> element, where we have a + // bunch of XBL-generated anonymous content descending from a native- + // anonymous XULElement. The XBL elements inherit style from their + // flattened tree parent, because that's how XBL works. But then we need + // to figure out what to do when one of those anonymous XBL elements + // (like an <input> element) generates its own (possibly pseudo-element- + // implementing) NAC. + // + // In this case, we inherit style from the XBL-generated NAC-creating + // element, rather than the <video> element. There are a number of good + // reasons for this. First, inheriting from the great-grandparent while + // the parent inherits from the grandparent would be bizarre at best. + // Second, exposing pseudo-elements from elements within our particular + // XBL implementation would allow content styles to (un)intentionally + // alter the video controls, which would be very bad. Third, our UA + // stylesheets have selectors like: + // + // input[type=range][orient=horizontal]::-moz-range-track + // + // and we need to make sure that the originating element is the <input>, + // not the <video>, because that's where the |orient| attribute lives. + // + // The upshot of all of this is that, to find the style parent (and + // originating element, if applicable), we walk up our parent chain to the + // first element that is not itself NAC (distinct from whether it happens + // to be in a NAC subtree). + // + // The one exception to all of this is scrollbar content, which we parent + // directly to the scrollframe. This is because the special-snowflake + // construction of scroll frames doesn't result in the placeholder frame + // being constructed until later, which means that GetInFlowParent() doesn't + // work right in the case of out-of-flow scrollframes. + // + // To implement all this, we need to pass the correct parent style context + // here because SetPrimaryFrame() may not have been called on the content + // yet and thus ResolveStyleContext can't find it otherwise. + // + // We don't need to worry about display:contents here, because such + // elements don't get a frame and thus can't generate NAC. But we do need + // to worry about anonymous boxes, which CorrectStyleParentFrame handles + // for us. + nsIFrame* inheritFrame = aFrame; + if (!content->IsNativeScrollbarContent()) { + while (inheritFrame->GetContent()->IsNativeAnonymous()) { + inheritFrame = inheritFrame->GetInFlowParent(); + } } + nsIFrame* styleParentFrame = + nsFrame::CorrectStyleParentFrame(inheritFrame, pseudo); + // The only way we can not have a style parent now is if inheritFrame is the + // canvas frame and we're the NAC parent for all the things added via + // nsIDocument::InsertAnonymousContent. + MOZ_ASSERT_IF(!styleParentFrame, + inheritFrame->GetType() == nsGkAtoms::canvasFrame); + // And that anonymous div has no pseudo. + MOZ_ASSERT_IF(!styleParentFrame, !pseudo); + + Element* originating = + pseudo ? styleParentFrame->GetContent()->AsElement() : nullptr; + nsStyleContext* parentStyle = + styleParentFrame ? styleParentFrame->StyleContext() : nullptr; + styleContext = + ResolveStyleContext(parentStyle, content, &aState, originating); + nsTArray<nsIAnonymousContentCreator::ContentInfo>* anonChildren = nullptr; if (!aAnonymousItems[i].mChildren.IsEmpty()) { anonChildren = &aAnonymousItems[i].mChildren; @@ -11028,151 +10961,6 @@ nsCSSFrameConstructor::AppendFirstLineFrames( lineFrame, aFrameItems); } -// Special routine to handle inserting a new frame into a block -// frame's child list. Takes care of placing the new frame into the -// right place when first-line style is present. -nsresult -nsCSSFrameConstructor::InsertFirstLineFrames( - nsFrameConstructorState& aState, - nsIContent* aContent, - nsIFrame* aBlockFrame, - nsContainerFrame** aParentFrame, - nsIFrame* aPrevSibling, - nsFrameItems& aFrameItems) -{ - nsresult rv = NS_OK; - // XXXbz If you make this method actually do something, check to - // make sure that the caller is passing what you expect. In - // particular, which content is aContent? And audit the rest of - // this code too; it makes bogus assumptions and may not build. -#if 0 - nsIFrame* parentFrame = *aParentFrame; - nsIFrame* newFrame = aFrameItems.childList; - bool isInline = IsInlineOutside(newFrame); - - if (!aPrevSibling) { - // Insertion will become the first frame. Two cases: we either - // already have a first-line frame or we don't. - nsIFrame* firstBlockKid = aBlockFrame->PrincipalChildList().FirstChild(); - if (firstBlockKid->GetType() == nsGkAtoms::lineFrame) { - // We already have a first-line frame - nsIFrame* lineFrame = firstBlockKid; - - if (isInline) { - // Easy case: the new inline frame will go into the lineFrame. - ReparentFrame(this, lineFrame, newFrame); - InsertFrames(lineFrame, kPrincipalList, nullptr, newFrame); - - // Since the frame is going into the lineFrame, don't let it - // go into the block too. - aFrameItems.childList = nullptr; - aFrameItems.lastChild = nullptr; - } - else { - // Harder case: We are about to insert a block level element - // before the first-line frame. - // XXX need a method to steal away frames from the line-frame - } - } - else { - // We do not have a first-line frame - if (isInline) { - // We now need a first-line frame to contain the inline frame. - nsIFrame* lineFrame = NS_NewFirstLineFrame(firstLineStyle); - - if (NS_SUCCEEDED(rv)) { - // Lookup first-line style context - nsStyleContext* parentStyle = - nsFrame::CorrectStyleParentFrame(aBlockFrame, - nsCSSPseudoElements::firstLine)-> - StyleContext(); - RefPtr<nsStyleContext> firstLineStyle = - GetFirstLineStyle(aContent, parentStyle); - - // Initialize the line frame - InitAndRestoreFrame(aState, aContent, aBlockFrame, lineFrame); - - // Make sure the caller inserts the lineFrame into the - // blocks list of children. - aFrameItems.childList = lineFrame; - aFrameItems.lastChild = lineFrame; - - // Give the inline frames to the lineFrame <b>after</b> - // reparenting them - NS_ASSERTION(lineFrame->StyleContext() == firstLineStyle, - "Bogus style context on line frame"); - ReparentFrame(aPresContext, lineFrame, newFrame); - lineFrame->SetInitialChildList(kPrincipalList, newFrame); - } - } - else { - // Easy case: the regular insertion logic can insert the new - // frame because it's a block frame. - } - } - } - else { - // Insertion will not be the first frame. - nsIFrame* prevSiblingParent = aPrevSibling->GetParent(); - if (prevSiblingParent == aBlockFrame) { - // Easy case: The prev-siblings parent is the block - // frame. Therefore the prev-sibling is not currently in a - // line-frame. Therefore the new frame which is going after it, - // regardless of type, is not going into a line-frame. - } - else { - // If the prevSiblingParent is not the block-frame then it must - // be a line-frame (if it were a letter-frame, that logic would - // already have adjusted the prev-sibling to be the - // letter-frame). - if (isInline) { - // Easy case: the insertion can go where the caller thinks it - // should go (which is into prevSiblingParent). - } - else { - // Block elements don't end up in line-frames, therefore - // change the insertion point to aBlockFrame. However, there - // might be more inline elements following aPrevSibling that - // need to be pulled out of the line-frame and become children - // of the block. - nsIFrame* nextSibling = aPrevSibling->GetNextSibling(); - nsIFrame* nextLineFrame = prevSiblingParent->GetNextInFlow(); - if (nextSibling || nextLineFrame) { - // Oy. We have work to do. Create a list of the new frames - // that are going into the block by stripping them away from - // the line-frame(s). - if (nextSibling) { - nsLineFrame* lineFrame = (nsLineFrame*) prevSiblingParent; - nsFrameList tail = lineFrame->StealFramesAfter(aPrevSibling); - // XXX do something with 'tail' - } - - nsLineFrame* nextLineFrame = (nsLineFrame*) lineFrame; - for (;;) { - nextLineFrame = nextLineFrame->GetNextInFlow(); - if (!nextLineFrame) { - break; - } - nsIFrame* kids = nextLineFrame->PrincipalChildList().FirstChild(); - } - } - else { - // We got lucky: aPrevSibling was the last inline frame in - // the line-frame. - ReparentFrame(this, aBlockFrame, newFrame); - InsertFrames(aBlockFrame, kPrincipalList, - prevSiblingParent, newFrame); - aFrameItems.childList = nullptr; - aFrameItems.lastChild = nullptr; - } - } - } - } - -#endif - return rv; -} - //---------------------------------------------------------------------- // First-letter support @@ -11494,7 +11282,7 @@ FindFirstLetterFrame(nsIFrame* aFrame, nsIFrame::ChildListID aListID) return nullptr; } -nsresult +void nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames( nsIPresShell* aPresShell, nsIFrame* aBlockFrame) @@ -11506,7 +11294,7 @@ nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames( floatFrame = ::FindFirstLetterFrame(aBlockFrame, nsIFrame::kPushedFloatsList); if (!floatFrame) { - return NS_OK; + return; } } @@ -11514,19 +11302,19 @@ nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames( // destroyed when we destroy the letter frame). nsIFrame* textFrame = floatFrame->PrincipalChildList().FirstChild(); if (!textFrame) { - return NS_OK; + return; } // Discover the placeholder frame for the letter frame - nsPlaceholderFrame* placeholderFrame = GetPlaceholderFrameFor(floatFrame); + nsPlaceholderFrame* placeholderFrame = floatFrame->GetPlaceholderFrame(); if (!placeholderFrame) { // Somethings really wrong - return NS_OK; + return; } nsContainerFrame* parentFrame = placeholderFrame->GetParent(); if (!parentFrame) { // Somethings really wrong - return NS_OK; + return; } // Create a new text frame with the right style context that maps @@ -11535,7 +11323,7 @@ nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames( nsStyleContext* parentSC = parentFrame->StyleContext(); nsIContent* textContent = textFrame->GetContent(); if (!textContent) { - return NS_OK; + return; } RefPtr<nsStyleContext> newSC = aPresShell->StyleSet()-> ResolveStyleForText(textContent, parentSC); @@ -11580,11 +11368,9 @@ nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames( if (offsetsNeedFixing) { prevSibling->RemoveStateBits(TEXT_OFFSETS_NEED_FIXING); } - - return NS_OK; } -nsresult +void nsCSSFrameConstructor::RemoveFirstLetterFrames(nsIPresShell* aPresShell, nsContainerFrame* aFrame, nsContainerFrame* aBlockFrame, @@ -11657,11 +11443,9 @@ nsCSSFrameConstructor::RemoveFirstLetterFrames(nsIPresShell* aPresShell, prevSibling = kid; kid = kid->GetNextSibling(); } - - return NS_OK; } -nsresult +void nsCSSFrameConstructor::RemoveLetterFrames(nsIPresShell* aPresShell, nsContainerFrame* aBlockFrame) { @@ -11670,20 +11454,16 @@ nsCSSFrameConstructor::RemoveLetterFrames(nsIPresShell* aPresShell, nsContainerFrame* continuation = aBlockFrame; bool stopLooking = false; - nsresult rv; do { - rv = RemoveFloatingFirstLetterFrames(aPresShell, continuation); - if (NS_SUCCEEDED(rv)) { - rv = RemoveFirstLetterFrames(aPresShell, - continuation, aBlockFrame, &stopLooking); - } + RemoveFloatingFirstLetterFrames(aPresShell, continuation); + RemoveFirstLetterFrames(aPresShell, continuation, aBlockFrame, + &stopLooking); if (stopLooking) { break; } continuation = static_cast<nsContainerFrame*>(continuation->GetNextContinuation()); } while (continuation); - return rv; } // Fixup the letter frame situation for the given block @@ -11726,7 +11506,7 @@ nsCSSFrameConstructor::RecoverLetterFrames(nsContainerFrame* aBlockFrame) // listbox Widget Routines -nsresult +void nsCSSFrameConstructor::CreateListBoxContent(nsContainerFrame* aParentFrame, nsIFrame* aPrevFrame, nsIContent* aChild, @@ -11734,8 +11514,6 @@ nsCSSFrameConstructor::CreateListBoxContent(nsContainerFrame* aParentFrame, bool aIsAppend) { #ifdef MOZ_XUL - nsresult rv = NS_OK; - // Construct a new frame if (nullptr != aParentFrame) { nsFrameItems frameItems; @@ -11756,7 +11534,7 @@ nsCSSFrameConstructor::CreateListBoxContent(nsContainerFrame* aParentFrame, if (StyleDisplay::None == display->mDisplay) { *aNewFrame = nullptr; - return NS_OK; + return; } BeginUpdate(); @@ -11775,9 +11553,9 @@ nsCSSFrameConstructor::CreateListBoxContent(nsContainerFrame* aParentFrame, if (newFrame) { // Notify the parent frame if (aIsAppend) - rv = ((nsListBoxBodyFrame*)aParentFrame)->ListBoxAppendFrames(frameItems); + ((nsListBoxBodyFrame*)aParentFrame)->ListBoxAppendFrames(frameItems); else - rv = ((nsListBoxBodyFrame*)aParentFrame)->ListBoxInsertFrames(aPrevFrame, frameItems); + ((nsListBoxBodyFrame*)aParentFrame)->ListBoxInsertFrames(aPrevFrame, frameItems); } EndUpdate(); @@ -11792,10 +11570,6 @@ nsCSSFrameConstructor::CreateListBoxContent(nsContainerFrame* aParentFrame, } #endif } - - return rv; -#else - return NS_ERROR_FAILURE; #endif } @@ -12218,8 +11992,8 @@ nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState, if (aFrame->IsXULBoxFrame() && !(aFrame->GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK) && aItems.AnyItemsNeedBlockParent()) { - RecreateFramesForContent(aFrame->GetContent(), true, - REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async, + REMOVE_FOR_RECONSTRUCTION); return true; } @@ -12238,8 +12012,8 @@ nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState, const bool isWebkitBox = IsFlexContainerForLegacyBox(aFrame, frameType); if (aPrevSibling && IsAnonymousFlexOrGridItem(aPrevSibling) && iter.item().NeedsAnonFlexOrGridItem(aState, isWebkitBox)) { - RecreateFramesForContent(aFrame->GetContent(), true, - REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async, + REMOVE_FOR_RECONSTRUCTION); return true; } @@ -12250,8 +12024,9 @@ nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState, iter.SetToEnd(); iter.Prev(); if (iter.item().NeedsAnonFlexOrGridItem(aState, isWebkitBox)) { - RecreateFramesForContent(aFrame->GetContent(), true, - REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(aFrame->GetContent(), + InsertionKind::Async, + REMOVE_FOR_RECONSTRUCTION); return true; } } @@ -12281,8 +12056,9 @@ nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState, isWebkitBox)) { // We hit something that _doesn't_ need an anonymous flex item! // Rebuild the flex container to bust it out. - RecreateFramesForContent(containerFrame->GetContent(), true, - REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(containerFrame->GetContent(), + InsertionKind::Async, + REMOVE_FOR_RECONSTRUCTION); return true; } @@ -12306,8 +12082,8 @@ nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState, // We want to optimize it better, and avoid reframing as much as // possible. But given the cases above, and the fact that a ruby // usually won't be very large, it should be fine to reframe it. - RecreateFramesForContent(aFrame->GetContent(), true, - REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async, + REMOVE_FOR_RECONSTRUCTION); return true; } @@ -12473,8 +12249,8 @@ nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState, if (!aItems.AllWantParentType(parentType)) { // Reframing aFrame->GetContent() is good enough, since the content of // table pseudo-frames is the ancestor content. - RecreateFramesForContent(aFrame->GetContent(), true, - REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async, + REMOVE_FOR_RECONSTRUCTION); return true; } } @@ -12562,15 +12338,15 @@ nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState, static_cast<void*>(blockContent)); } #endif - RecreateFramesForContent(blockContent, true, REMOVE_FOR_RECONSTRUCTION, - nullptr); + RecreateFramesForContent(blockContent, InsertionKind::Async, + REMOVE_FOR_RECONSTRUCTION); return true; } -nsresult -nsCSSFrameConstructor::ReframeContainingBlock(nsIFrame* aFrame, - RemoveFlags aFlags, - nsIContent** aDestroyedFramesFor) +void +nsCSSFrameConstructor::ReframeContainingBlock(nsIFrame* aFrame, + InsertionKind aInsertionKind, + RemoveFlags aFlags) { #ifdef DEBUG @@ -12590,7 +12366,7 @@ nsCSSFrameConstructor::ReframeContainingBlock(nsIFrame* aFrame, // don't ReframeContainingBlock, this will result in a crash // if we remove a tree that's in reflow - see bug 121368 for testcase NS_ERROR("Atemptted to nsCSSFrameConstructor::ReframeContainingBlock during a Reflow!!!"); - return NS_OK; + return; } // Get the first "normal" ancestor of the target frame. @@ -12606,23 +12382,25 @@ nsCSSFrameConstructor::ReframeContainingBlock(nsIFrame* aFrame, // so GetFloatContainingBlock(aFrame) has been removed // And get the containingBlock's content - nsCOMPtr<nsIContent> blockContent = containingBlock->GetContent(); - if (blockContent) { + if (nsIContent* blockContent = containingBlock->GetContent()) { #ifdef DEBUG if (gNoisyContentUpdates) { printf(" ==> blockContent=%p\n", static_cast<void*>(blockContent)); } #endif - return RecreateFramesForContent(blockContent, true, aFlags, aDestroyedFramesFor); + RecreateFramesForContent(blockContent->AsElement(), aInsertionKind, + REMOVE_FOR_RECONSTRUCTION); + return; } } // If we get here, we're screwed! - return RecreateFramesForContent(mPresShell->GetDocument()->GetRootElement(), - true, aFlags, nullptr); + RecreateFramesForContent(mPresShell->GetDocument()->GetRootElement(), + aInsertionKind, + REMOVE_FOR_RECONSTRUCTION); } -nsresult +void nsCSSFrameConstructor::GenerateChildFrames(nsContainerFrame* aFrame) { { @@ -12656,8 +12434,6 @@ nsCSSFrameConstructor::GenerateChildFrames(nsContainerFrame* aFrame) // call XBL constructors after the frames are created mPresShell->GetDocument()->BindingManager()->ProcessAttachedQueue(); - - return NS_OK; } ////////////////////////////////////////////////////////// diff --git a/layout/base/nsCSSFrameConstructor.h b/layout/base/nsCSSFrameConstructor.h index 7d1b8d42ff..0de00a90ca 100644 --- a/layout/base/nsCSSFrameConstructor.h +++ b/layout/base/nsCSSFrameConstructor.h @@ -48,7 +48,7 @@ class FlattenedChildIterator; } // namespace dom } // namespace mozilla -class nsCSSFrameConstructor : public nsFrameManager +class nsCSSFrameConstructor final : public nsFrameManager { public: typedef mozilla::CSSPseudoElementType CSSPseudoElementType; @@ -60,7 +60,7 @@ public: nsCSSFrameConstructor(nsIDocument* aDocument, nsIPresShell* aPresShell); ~nsCSSFrameConstructor(void) { - NS_ASSERTION(mUpdateCount == 0, "Dying in the middle of our own update?"); + MOZ_ASSERT(mUpdateCount == 0, "Dying in the middle of our own update?"); } // get the alternate text for a content node @@ -78,7 +78,7 @@ public: nsIFrame* ConstructRootFrame(); - nsresult ReconstructDocElementHierarchy(); + void ReconstructDocElementHierarchy(); // Create frames for content nodes that are marked as needing frames. This // should be called before ProcessPendingRestyles. @@ -97,8 +97,8 @@ private: // aChild is the child being inserted for inserts, and the first // child being appended for appends. bool MaybeConstructLazily(Operation aOperation, - nsIContent* aContainer, - nsIContent* aChild); + nsIContent* aContainer, + nsIContent* aChild); // Issues a single ContentInserted for each child of aContainer in the range // [aStartChild, aEndChild). @@ -152,8 +152,8 @@ private: // Returns true if parent was recreated due to frameset child, false otherwise. bool MaybeRecreateForFrameset(nsIFrame* aParentFrame, - nsIContent* aStartChild, - nsIContent* aEndChild); + nsIContent* aStartChild, + nsIContent* aEndChild); public: /** @@ -202,16 +202,16 @@ public: // If aAllowLazyConstruction is true then frame construction of the new // children can be done lazily. - nsresult ContentAppended(nsIContent* aContainer, - nsIContent* aFirstNewContent, - bool aAllowLazyConstruction); + void ContentAppended(nsIContent* aContainer, + nsIContent* aFirstNewContent, + bool aAllowLazyConstruction); // If aAllowLazyConstruction is true then frame construction of the new child // can be done lazily. - nsresult ContentInserted(nsIContent* aContainer, - nsIContent* aChild, - nsILayoutHistoryState* aFrameState, - bool aAllowLazyConstruction); + void ContentInserted(nsIContent* aContainer, + nsIContent* aChild, + nsILayoutHistoryState* aFrameState, + bool aAllowLazyConstruction); // Like ContentInserted but handles inserting the children of aContainer in // the range [aStartChild, aEndChild). aStartChild must be non-null. @@ -219,12 +219,17 @@ public: // aStartChild. If aAllowLazyConstruction is true then frame construction of // the new children can be done lazily. It is only allowed to be true when // inserting a single node. - nsresult ContentRangeInserted(nsIContent* aContainer, - nsIContent* aStartChild, - nsIContent* aEndChild, - nsILayoutHistoryState* aFrameState, - bool aAllowLazyConstruction); + void ContentRangeInserted(nsIContent* aContainer, + nsIContent* aStartChild, + nsIContent* aEndChild, + nsILayoutHistoryState* aFrameState, + bool aAllowLazyConstruction); +public: + // FIXME(emilio): How important is it to keep the frame tree state around for + // REMOVE_DESTROY_FRAMES? + // + // Seems like otherwise we could just remove it. enum RemoveFlags { REMOVE_CONTENT, REMOVE_FOR_RECONSTRUCTION, REMOVE_DESTROY_FRAMES }; /** @@ -243,15 +248,14 @@ public: * only when aFlags == REMOVE_DESTROY_FRAMES, otherwise it will only be * captured if we reconstructed frames for an ancestor. */ - nsresult ContentRemoved(nsIContent* aContainer, - nsIContent* aChild, - nsIContent* aOldNextSibling, - RemoveFlags aFlags, - bool* aDidReconstruct, - nsIContent** aDestroyedFramesFor = nullptr); + void ContentRemoved(nsIContent* aContainer, + nsIContent* aChild, + nsIContent* aOldNextSibling, + RemoveFlags aFlags, + bool* aDidReconstruct); - nsresult CharacterDataChanged(nsIContent* aContent, - CharacterDataChangeInfo* aInfo); + void CharacterDataChanged(nsIContent* aContent, + CharacterDataChangeInfo* aInfo); // If aContent is a text node that has been optimized away due to being // whitespace next to a block boundary (or for some other reason), stop @@ -260,8 +264,8 @@ public: // Returns the frame for aContent if there is one. nsIFrame* EnsureFrameForTextNode(nsGenericDOMDataNode* aContent); - // generate the child frames and process bindings - nsresult GenerateChildFrames(nsContainerFrame* aFrame); + // Generate the child frames and process bindings + void GenerateChildFrames(nsContainerFrame* aFrame); // Should be called when a frame is going to be destroyed and // WillDestroyFrameTree hasn't been called yet. @@ -280,14 +284,10 @@ public: /** * Destroy the frames for aContent. Note that this may destroy frames - * for an ancestor instead - aDestroyedFramesFor contains the content node - * where frames were actually destroyed (which should be used in the - * ContentInserted call to recreate frames). The frame tree state - * is captured before the frames are destroyed and can be retrieved using - * GetLastCapturedLayoutHistoryState(). + * for an ancestor instead - aDidReconstruct contains whether a reconstruct + * was posted for any ancestor. */ - void DestroyFramesFor(nsIContent* aContent, - nsIContent** aDestroyedFramesFor); + void DestroyFramesFor(nsIContent* aContent, bool* aDidReconstruct); // Request to create a continuing frame. This method never returns null. nsIFrame* CreateContinuingFrame(nsPresContext* aPresContext, @@ -303,11 +303,11 @@ public: */ InsertionPoint GetInsertionPoint(nsIContent* aContainer, nsIContent* aChild); - nsresult CreateListBoxContent(nsContainerFrame* aParentFrame, - nsIFrame* aPrevFrame, - nsIContent* aChild, - nsIFrame** aResult, - bool aIsAppend); + void CreateListBoxContent(nsContainerFrame* aParentFrame, + nsIFrame* aPrevFrame, + nsIContent* aChild, + nsIFrame** aResult, + bool aIsAppend); // GetInitialContainingBlock() is deprecated in favor of GetRootElementFrame(); // nsIFrame* GetInitialContainingBlock() { return mRootElementFrame; } @@ -322,15 +322,6 @@ public: nsContainerFrame* GetDocElementContainingBlock() { return mDocElementContainingBlock; } - /** - * Return the layout history state that was captured in the last - * ContentRemoved / RecreateFramesForContent call. - */ - nsILayoutHistoryState* GetLastCapturedLayoutHistoryState() - { - return mTempFrameTreeState; - } - private: struct FrameConstructionItem; class FrameConstructionItemList; @@ -364,7 +355,8 @@ private: already_AddRefed<nsStyleContext> ResolveStyleContext(nsStyleContext* aParentStyleContext, nsIContent* aContent, - nsFrameConstructorState* aState); + nsFrameConstructorState* aState, + Element* aOriginatingElementOrNull = nullptr); // Add the frame construction items for the given aContent and aParentFrame // to the list. This might add more than one item in some rare cases. @@ -418,14 +410,14 @@ private: * @param [out] aNewContent the content node we create * @param [out] aNewFrame the new frame we create */ - nsresult CreateAttributeContent(nsIContent* aParentContent, - nsIFrame* aParentFrame, - int32_t aAttrNamespace, - nsIAtom* aAttrName, - nsStyleContext* aStyleContext, - nsCOMArray<nsIContent>& aGeneratedContent, - nsIContent** aNewContent, - nsIFrame** aNewFrame); + void CreateAttributeContent(nsIContent* aParentContent, + nsIFrame* aParentFrame, + int32_t aAttrNamespace, + nsIAtom* aAttrName, + nsStyleContext* aStyleContext, + nsCOMArray<nsIContent>& aGeneratedContent, + nsIContent** aNewContent, + nsIFrame** aNewFrame); /** * Create a text node containing the given string. If aText is non-null @@ -463,11 +455,11 @@ private: // aParentFrame. aPrevSibling must be the frame after which aFrameList is to // be placed on aParentFrame's principal child list. It may be null if // aFrameList is being added at the beginning of the child list. - nsresult AppendFramesToParent(nsFrameConstructorState& aState, - nsContainerFrame* aParentFrame, - nsFrameItems& aFrameList, - nsIFrame* aPrevSibling, - bool aIsRecursiveCall = false); + void AppendFramesToParent(nsFrameConstructorState& aState, + nsContainerFrame* aParentFrame, + nsFrameItems& aFrameList, + nsIFrame* aPrevSibling, + bool aIsRecursiveCall = false); // BEGIN TABLE SECTION /** @@ -1440,12 +1432,6 @@ private: nsFrameItems& aFrameItems); static bool AtLineBoundary(FCItemIterator& aIter); - nsresult CreateAnonymousFrames(nsFrameConstructorState& aState, - nsIContent* aParent, - nsContainerFrame* aParentFrame, - PendingBinding* aPendingBinding, - nsFrameItems& aChildItems); - nsresult GetAnonymousContent(nsIContent* aParent, nsIFrame* aParentFrame, nsTArray<nsIAnonymousContentCreator::ContentInfo>& aAnonContent); @@ -1697,7 +1683,7 @@ private: // InitializeSelectFrame puts scrollFrame in aFrameItems if aBuildCombobox is false // aBuildCombobox indicates if we are building a combobox that has a dropdown // popup widget or not. - nsresult + void InitializeSelectFrame(nsFrameConstructorState& aState, nsContainerFrame* aScrollFrame, nsContainerFrame* aScrolledFrame, @@ -1718,34 +1704,36 @@ private: nsStyleContext* MaybeRecreateFramesForElement(Element* aElement); /** + * Whether insertion should be done synchronously or asynchronously. + * + * Generally, insertion is synchronous if we're reconstructing something from + * frame construction/reconstruction, and async if we're removing stuff, like + * from ContentRemoved. + */ + enum class InsertionKind + { + Sync, + Async, + }; + + /** * Recreate frames for aContent. * @param aContent the content to recreate frames for - * @param aAsyncInsert if true then a restyle event will be posted to handle - * the required ContentInserted call instead of doing it immediately. * @param aFlags normally you want to pass REMOVE_FOR_RECONSTRUCTION here - * @param aDestroyedFramesFor if non-null, it will contain the content that - * was actually reframed - it may be different than aContent. */ - nsresult - RecreateFramesForContent(nsIContent* aContent, - bool aAsyncInsert, - RemoveFlags aFlags, - nsIContent** aDestroyedFramesFor); + void RecreateFramesForContent(nsIContent* aContent, + InsertionKind aInsertionKind, + RemoveFlags aFlags); // If removal of aFrame from the frame tree requires reconstruction of some // containing block (either of aFrame or of its parent) due to {ib} splits or // table pseudo-frames, recreate the relevant frame subtree. The return value - // indicates whether this happened. If this method returns true, *aResult is - // the return value of ReframeContainingBlock or RecreateFramesForContent. If - // this method returns false, the value of *aResult is not affected. aFrame - // and aResult must not be null. aFrame must be the result of a + // indicates whether this happened. aFrame must be the result of a // GetPrimaryFrame() call on a content node (which means its parent is also - // not null). If this method returns true, aDestroyedFramesFor contains the - // content that was reframed. - bool MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame, - RemoveFlags aFlags, - nsresult* aResult, - nsIContent** aDestroyedFramesFor); + // not null). + bool MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame, + InsertionKind aInsertionKind, + RemoveFlags aFlags); nsIFrame* CreateContinuingOuterTableFrame(nsIPresShell* aPresShell, nsPresContext* aPresContext, @@ -1869,9 +1857,9 @@ private: bool aIsAppend, nsIFrame* aPrevSibling); - nsresult ReframeContainingBlock(nsIFrame* aFrame, - RemoveFlags aFlags, - nsIContent** aReframeContent); + void ReframeContainingBlock(nsIFrame* aFrame, + InsertionKind aInsertionKind, + RemoveFlags aFlags); //---------------------------------------- @@ -1925,18 +1913,18 @@ private: void RecoverLetterFrames(nsContainerFrame* aBlockFrame); // - nsresult RemoveLetterFrames(nsIPresShell* aPresShell, - nsContainerFrame* aBlockFrame); + void RemoveLetterFrames(nsIPresShell* aPresShell, + nsContainerFrame* aBlockFrame); // Recursive helper for RemoveLetterFrames - nsresult RemoveFirstLetterFrames(nsIPresShell* aPresShell, - nsContainerFrame* aFrame, - nsContainerFrame* aBlockFrame, - bool* aStopLooking); + void RemoveFirstLetterFrames(nsIPresShell* aPresShell, + nsContainerFrame* aFrame, + nsContainerFrame* aBlockFrame, + bool* aStopLooking); // Special remove method for those pesky floating first-letter frames - nsresult RemoveFloatingFirstLetterFrames(nsIPresShell* aPresShell, - nsIFrame* aBlockFrame); + void RemoveFloatingFirstLetterFrames(nsIPresShell* aPresShell, + nsIFrame* aBlockFrame); // Capture state for the frame tree rooted at the frame associated with the // content object, aContent @@ -1967,71 +1955,53 @@ private: nsContainerFrame* aBlockFrame, nsFrameItems& aFrameItems); - nsresult InsertFirstLineFrames(nsFrameConstructorState& aState, - nsIContent* aContent, - nsIFrame* aBlockFrame, - nsContainerFrame** aParentFrame, - nsIFrame* aPrevSibling, - nsFrameItems& aFrameItems); + // The direction in which we should look for siblings. + enum class SiblingDirection + { + Forward, + Backward, + }; /** - * Find the right frame to use for aContent when looking for sibling - * frames for aTargetContent. If aPrevSibling is true, this - * will look for last continuations, etc, as necessary. This calls - * IsValidSibling as needed; if that returns false it returns null. + * Find the frame for the content immediately next to the one aIter points to, + * in the direction SiblingDirection indicates, following continuations if + * necessary. * - * @param aContent the content to search for frames - * @param aTargetContent the content we're finding a sibling frame for - * @param aTargetContentDisplay the CSS display enum for aTargetContent if - * already known, UNSET_DISPLAY otherwise. It will be filled in - * if needed. - * @param aParentFrame the nearest ancestor frame, used internally for - * finding ::after / ::before frames - * @param aPrevSibling true if we're searching in reverse DOM order - */ - nsIFrame* FindFrameForContentSibling(nsIContent* aContent, - nsIContent* aTargetContent, - mozilla::StyleDisplay& aTargetContentDisplay, - nsContainerFrame* aParentFrame, - bool aPrevSibling); - - /** - * Find the frame for the content immediately preceding the one aIter - * points to, following continuations if necessary. aIter is passed by - * value on purpose, so as not to modify the caller's iterator. + * aIter is passed by const reference on purpose, so as not to modify the + * caller's iterator. * * @param aIter should be positioned such that aIter.GetPreviousChild() * is the first content to search for frames - * @param aTargetContent the content we're finding a sibling frame for - * @param aTargetContentDisplay the CSS display enum for aTargetContent if - * already known, UNSET_DISPLAY otherwise. It will be filled in - * if needed. - * @param aParentFrame the nearest ancestor frame, used inernally for - * finding ::after / ::before frames - */ - nsIFrame* FindPreviousSibling(mozilla::dom::FlattenedChildIterator aIter, - nsIContent* aTargetContent, - mozilla::StyleDisplay& aTargetContentDisplay, - nsContainerFrame* aParentFrame); - - /** - * Find the frame for the content node immediately following the one aIter - * points to, following continuations if necessary. aIter is passed by value - * on purpose, so as not to modify the caller's iterator. - * - * @param aIter should be positioned such that aIter.GetNextChild() - * is the first content to search for frames - * @param aTargetContent the content we're finding a sibling frame for - * @param aTargetContentDisplay the CSS display enum for aTargetContent if - * already known, UNSET_DISPLAY otherwise. It will be filled in - * if needed. - * @param aParentFrame the nearest ancestor frame, used inernally for - * finding ::after / ::before frames + * @param aTargetContentDisplay the CSS display enum for the content aIter + * points to if already known, UNSET_DISPLAY otherwise. It will be + * filled in if needed. */ - nsIFrame* FindNextSibling(mozilla::dom::FlattenedChildIterator aIter, - nsIContent* aTargetContent, - mozilla::StyleDisplay& aTargetContentDisplay, - nsContainerFrame* aParentFrame); + template<SiblingDirection> + nsIFrame* FindSibling(const mozilla::dom::FlattenedChildIterator& aIter, + mozilla::StyleDisplay& aTargetContentDisplay); + + // Helper for the implementation of FindSibling. + template<SiblingDirection> + nsIFrame* FindSiblingInternal( + mozilla::dom::FlattenedChildIterator, + nsIContent* aTargetContent, + mozilla::StyleDisplay& aTargetContentDisplay); + + // An alias of FindSibling<SiblingDirection::Forward>. + nsIFrame* FindNextSibling(const mozilla::dom::FlattenedChildIterator& aIter, + mozilla::StyleDisplay& aTargetContentDisplay); + // An alias of FindSibling<SiblingDirection::Backwards>. + nsIFrame* FindPreviousSibling(const mozilla::dom::FlattenedChildIterator& aIter, + mozilla::StyleDisplay& aTargetContentDisplay); + + // Given a potential first-continuation sibling frame for aTargetContent, + // verify that it is an actual valid sibling for it, and return the + // appropriate continuation the new frame for aTargetContent should be + // inserted next to. + nsIFrame* AdjustSiblingFrame(nsIFrame* aSibling, + nsIContent* aTargetContent, + mozilla::StyleDisplay& aTargetContentDisplay, + SiblingDirection aDirection); // Find the right previous sibling for an insertion. This also updates the // parent frame to point to the correct continuation of the parent frame to diff --git a/layout/base/nsFrameManager.cpp b/layout/base/nsFrameManager.cpp index 97b022da8b..be8cd7147d 100644 --- a/layout/base/nsFrameManager.cpp +++ b/layout/base/nsFrameManager.cpp @@ -38,47 +38,17 @@ #include "nsIStatefulFrame.h" #include "nsContainerFrame.h" - #ifdef DEBUG - //#define DEBUG_UNDISPLAYED_MAP - //#define DEBUG_DISPLAY_CONTENTS_MAP - #else - #undef DEBUG_UNDISPLAYED_MAP - #undef DEBUG_DISPLAY_CONTENTS_MAP - #endif +// #define DEBUG_UNDISPLAYED_MAP +// #define DEBUG_DISPLAY_CONTENTS_MAP using namespace mozilla; using namespace mozilla::dom; //---------------------------------------------------------------------- -struct PlaceholderMapEntry : public PLDHashEntryHdr { - // key (the out of flow frame) can be obtained through placeholder frame - nsPlaceholderFrame *placeholderFrame; -}; - -static bool -PlaceholderMapMatchEntry(const PLDHashEntryHdr *hdr, const void *key) -{ - const PlaceholderMapEntry *entry = - static_cast<const PlaceholderMapEntry*>(hdr); - NS_ASSERTION(entry->placeholderFrame->GetOutOfFlowFrame() != - (void*)0xdddddddd, - "Dead placeholder in placeholder map"); - return entry->placeholderFrame->GetOutOfFlowFrame() == key; -} - -static const PLDHashTableOps PlaceholderMapOps = { - PLDHashTable::HashVoidPtrKeyStub, - PlaceholderMapMatchEntry, - PLDHashTable::MoveEntryStub, - PLDHashTable::ClearEntryStub, - nullptr -}; - nsFrameManagerBase::nsFrameManagerBase() : mPresShell(nullptr) , mRootFrame(nullptr) - , mPlaceholderMap(&PlaceholderMapOps, sizeof(PlaceholderMapEntry)) , mUndisplayedMap(nullptr) , mDisplayContentsMap(nullptr) , mIsDestroyingFrames(false) @@ -87,40 +57,49 @@ nsFrameManagerBase::nsFrameManagerBase() //---------------------------------------------------------------------- -// XXXldb This seems too complicated for what I think it's doing, and it -// should also be using PLDHashTable rather than plhash to use less memory. +/** + * The undisplayed map is a class that maps a parent content node to the + * undisplayed content children, and their style contexts. + * + * The linked list of nodes holds strong references to the style contexts and + * the content. + */ +class nsFrameManagerBase::UndisplayedMap : + private nsClassHashtable<nsPtrHashKey<nsIContent>, + LinkedList<UndisplayedNode>> +{ + typedef nsClassHashtable<nsPtrHashKey<nsIContent>, LinkedList<UndisplayedNode>> base_type; -class nsFrameManagerBase::UndisplayedMap { public: - explicit UndisplayedMap(uint32_t aNumBuckets = 16); - ~UndisplayedMap(void); + UndisplayedMap(); + ~UndisplayedMap(); UndisplayedNode* GetFirstNode(nsIContent* aParentContent); - nsresult AddNodeFor(nsIContent* aParentContent, - nsIContent* aChild, nsStyleContext* aStyle); + void AddNodeFor(nsIContent* aParentContent, + nsIContent* aChild, + nsStyleContext* aStyle); - void RemoveNodeFor(nsIContent* aParentContent, - UndisplayedNode* aNode); + void RemoveNodeFor(nsIContent* aParentContent, UndisplayedNode* aNode); void RemoveNodesFor(nsIContent* aParentContent); - UndisplayedNode* UnlinkNodesFor(nsIContent* aParentContent); + + nsAutoPtr<LinkedList<UndisplayedNode>> + UnlinkNodesFor(nsIContent* aParentContent); // Removes all entries from the hash table - void Clear(void); + void Clear(); protected: + LinkedList<UndisplayedNode>* GetListFor(nsIContent* aParentContent); + LinkedList<UndisplayedNode>* GetOrCreateListFor(nsIContent* aParentContent); + void AppendNodeFor(UndisplayedNode* aNode, nsIContent* aParentContent); /** - * Gets the entry for the provided parent content. If the content - * is a <xbl:children> element, |**aParentContent| is set to - * the parent of the children element. + * Get the applicable parent for the map lookup. This is almost always the + * provided argument, except if it's a <xbl:children> element, in which case + * it's the parent of the children element. */ - PLHashEntry** GetEntryFor(nsIContent** aParentContent); - void AppendNodeFor(UndisplayedNode* aNode, - nsIContent* aParentContent); - - PLHashTable* mTable; - PLHashEntry** mLastLookup; + nsIContent* GetApplicableParent(nsIContent* aParent); }; //---------------------------------------------------------------------- @@ -138,14 +117,10 @@ nsFrameManager::Destroy() // Destroy the frame hierarchy. mPresShell->SetIgnoreFrameDestruction(true); - // Unregister all placeholders before tearing down the frame tree - nsFrameManager::ClearPlaceholderFrameMap(); - if (mRootFrame) { mRootFrame->Destroy(); mRootFrame = nullptr; } - delete mUndisplayedMap; mUndisplayedMap = nullptr; delete mDisplayContentsMap; @@ -156,58 +131,9 @@ nsFrameManager::Destroy() //---------------------------------------------------------------------- -// Placeholder frame functions -nsPlaceholderFrame* -nsFrameManager::GetPlaceholderFrameFor(const nsIFrame* aFrame) -{ - NS_PRECONDITION(aFrame, "null param unexpected"); - - auto entry = static_cast<PlaceholderMapEntry*> - (const_cast<PLDHashTable*>(&mPlaceholderMap)->Search(aFrame)); - if (entry) { - return entry->placeholderFrame; - } - - return nullptr; -} - -void -nsFrameManager::RegisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame) -{ - MOZ_ASSERT(aPlaceholderFrame, "null param unexpected"); - MOZ_ASSERT(nsGkAtoms::placeholderFrame == aPlaceholderFrame->GetType(), - "unexpected frame type"); - auto entry = static_cast<PlaceholderMapEntry*> - (mPlaceholderMap.Add(aPlaceholderFrame->GetOutOfFlowFrame())); - MOZ_ASSERT(!entry->placeholderFrame, - "Registering a placeholder for a frame that already has a placeholder!"); - entry->placeholderFrame = aPlaceholderFrame; -} - -void -nsFrameManager::UnregisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame) -{ - NS_PRECONDITION(aPlaceholderFrame, "null param unexpected"); - NS_PRECONDITION(nsGkAtoms::placeholderFrame == aPlaceholderFrame->GetType(), - "unexpected frame type"); - - mPlaceholderMap.Remove(aPlaceholderFrame->GetOutOfFlowFrame()); -} - -void -nsFrameManager::ClearPlaceholderFrameMap() -{ - for (auto iter = mPlaceholderMap.Iter(); !iter.Done(); iter.Next()) { - auto entry = static_cast<PlaceholderMapEntry*>(iter.Get()); - entry->placeholderFrame->SetOutOfFlowFrame(nullptr); - } - mPlaceholderMap.Clear(); -} - -//---------------------------------------------------------------------- - /* static */ nsStyleContext* -nsFrameManager::GetStyleContextInMap(UndisplayedMap* aMap, nsIContent* aContent) +nsFrameManager::GetStyleContextInMap(UndisplayedMap* aMap, + const nsIContent* aContent) { if (!aContent) { return nullptr; @@ -215,7 +141,7 @@ nsFrameManager::GetStyleContextInMap(UndisplayedMap* aMap, nsIContent* aContent) nsIContent* parent = aContent->GetParentElementCrossingShadowRoot(); MOZ_ASSERT(parent || !aContent->GetParent(), "no non-elements"); for (UndisplayedNode* node = aMap->GetFirstNode(parent); - node; node = node->mNext) { + node; node = node->getNext()) { if (node->mContent == aContent) return node->mStyle; } @@ -241,16 +167,16 @@ nsFrameManager::SetStyleContextInMap(UndisplayedMap* aMap, nsIContent* aContent, nsStyleContext* aStyleContext) { - NS_PRECONDITION(!aStyleContext->GetPseudo(), - "Should only have actual elements here"); + MOZ_ASSERT(!aStyleContext->GetPseudo(), + "Should only have actual elements here"); #if defined(DEBUG_UNDISPLAYED_MAP) || defined(DEBUG_DISPLAY_BOX_CONTENTS_MAP) static int i = 0; printf("SetStyleContextInMap(%d): p=%p \n", i++, (void *)aContent); #endif - NS_ASSERTION(!GetStyleContextInMap(aMap, aContent), - "Already have an entry for aContent"); + MOZ_ASSERT(!GetStyleContextInMap(aMap, aContent), + "Already have an entry for aContent"); nsIContent* parent = aContent->GetParentElementCrossingShadowRoot(); MOZ_ASSERT(parent || !aContent->GetParent(), "no non-elements"); @@ -287,7 +213,7 @@ nsFrameManager::ChangeStyleContextInMap(UndisplayedMap* aMap, #endif for (UndisplayedNode* node = aMap->GetFirstNode(aContent->GetParent()); - node; node = node->mNext) { + node; node = node->getNext()) { if (node->mContent == aContent) { node->mStyle = aStyleContext; return; @@ -306,25 +232,25 @@ nsFrameManager::ClearUndisplayedContentIn(nsIContent* aContent, printf("ClearUndisplayedContent(%d): content=%p parent=%p --> ", i++, (void *)aContent, (void*)aParentContent); #endif - if (mUndisplayedMap) { - UndisplayedNode* node = mUndisplayedMap->GetFirstNode(aParentContent); - while (node) { - if (node->mContent == aContent) { - mUndisplayedMap->RemoveNodeFor(aParentContent, node); + if (!mUndisplayedMap) { + return; + } + + for (UndisplayedNode* node = mUndisplayedMap->GetFirstNode(aParentContent); + node; node = node->getNext()) { + if (node->mContent == aContent) { + mUndisplayedMap->RemoveNodeFor(aParentContent, node); #ifdef DEBUG_UNDISPLAYED_MAP - printf( "REMOVED!\n"); + printf( "REMOVED!\n"); #endif -#ifdef DEBUG - // make sure that there are no more entries for the same content - nsStyleContext *context = GetUndisplayedContent(aContent); - NS_ASSERTION(context == nullptr, "Found more undisplayed content data after removal"); -#endif - return; - } - node = node->mNext; + // make sure that there are no more entries for the same content + MOZ_ASSERT(!GetUndisplayedContent(aContent), + "Found more undisplayed content data after removal"); + return; } } + #ifdef DEBUG_UNDISPLAYED_MAP printf( "not found.\n"); #endif @@ -380,26 +306,25 @@ nsFrameManager::ClearDisplayContentsIn(nsIContent* aContent, static int i = 0; printf("ClearDisplayContents(%d): content=%p parent=%p --> ", i++, (void *)aContent, (void*)aParentContent); #endif - - if (mDisplayContentsMap) { - UndisplayedNode* node = mDisplayContentsMap->GetFirstNode(aParentContent); - while (node) { - if (node->mContent == aContent) { - mDisplayContentsMap->RemoveNodeFor(aParentContent, node); + + if (!mDisplayContentsMap) { + return; + } + + for (UndisplayedNode* node = mDisplayContentsMap->GetFirstNode(aParentContent); + node; node = node->getNext()) { + if (node->mContent == aContent) { + mDisplayContentsMap->RemoveNodeFor(aParentContent, node); #ifdef DEBUG_DISPLAY_CONTENTS_MAP - printf( "REMOVED!\n"); -#endif -#ifdef DEBUG - // make sure that there are no more entries for the same content - nsStyleContext* context = GetDisplayContentsStyleFor(aContent); - NS_ASSERTION(context == nullptr, "Found more entries for aContent after removal"); + printf( "REMOVED!\n"); #endif - ClearAllDisplayContentsIn(aContent); - ClearAllUndisplayedContentIn(aContent); - return; - } - node = node->mNext; + // make sure that there are no more entries for the same content + MOZ_ASSERT(!GetDisplayContentsStyleFor(aContent), + "Found more entries for aContent after removal"); + ClearAllDisplayContentsIn(aContent); + ClearAllUndisplayedContentIn(aContent); + return; } } #ifdef DEBUG_DISPLAY_CONTENTS_MAP @@ -416,14 +341,14 @@ nsFrameManager::ClearAllDisplayContentsIn(nsIContent* aParentContent) #endif if (mDisplayContentsMap) { - UndisplayedNode* cur = mDisplayContentsMap->UnlinkNodesFor(aParentContent); - while (cur) { - UndisplayedNode* next = cur->mNext; - cur->mNext = nullptr; - ClearAllDisplayContentsIn(cur->mContent); - ClearAllUndisplayedContentIn(cur->mContent); - delete cur; - cur = next; + nsAutoPtr<LinkedList<UndisplayedNode>> list = + mDisplayContentsMap->UnlinkNodesFor(aParentContent); + if (list) { + while (UndisplayedNode* node = list->popFirst()) { + ClearAllDisplayContentsIn(node->mContent); + ClearAllUndisplayedContentIn(node->mContent); + delete node; + } } } @@ -495,7 +420,7 @@ nsFrameManager::RemoveFrame(ChildListID aListID, aOldFrame->GetType() == nsGkAtoms::textFrame, "Must remove first continuation."); NS_ASSERTION(!(aOldFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW && - GetPlaceholderFrameFor(aOldFrame)), + aOldFrame->GetPlaceholderFrame()), "Must call RemoveFrame on placeholder for out-of-flows."); nsContainerFrame* parentFrame = aOldFrame->GetParent(); if (parentFrame->IsAbsoluteContainer() && @@ -654,182 +579,132 @@ nsFrameManager::RestoreFrameState(nsIFrame* aFrame, //---------------------------------------------------------------------- -static PLHashNumber -HashKey(void* key) -{ - return NS_PTR_TO_INT32(key); -} - -static int -CompareKeys(void* key1, void* key2) -{ - return key1 == key2; -} - -//---------------------------------------------------------------------- - -nsFrameManagerBase::UndisplayedMap::UndisplayedMap(uint32_t aNumBuckets) +nsFrameManagerBase::UndisplayedMap::UndisplayedMap() { MOZ_COUNT_CTOR(nsFrameManagerBase::UndisplayedMap); - mTable = PL_NewHashTable(aNumBuckets, (PLHashFunction)HashKey, - (PLHashComparator)CompareKeys, - (PLHashComparator)nullptr, - nullptr, nullptr); - mLastLookup = nullptr; } nsFrameManagerBase::UndisplayedMap::~UndisplayedMap(void) { MOZ_COUNT_DTOR(nsFrameManagerBase::UndisplayedMap); Clear(); - PL_HashTableDestroy(mTable); } -PLHashEntry** -nsFrameManagerBase::UndisplayedMap::GetEntryFor(nsIContent** aParentContent) +void +nsFrameManagerBase::UndisplayedMap::Clear() { - nsIContent* parentContent = *aParentContent; - - if (mLastLookup && (parentContent == (*mLastLookup)->key)) { - return mLastLookup; + for (auto iter = Iter(); !iter.Done(); iter.Next()) { + auto* list = iter.UserData(); + while (auto* node = list->popFirst()) { + delete node; + } + iter.Remove(); } +} +nsIContent* +nsFrameManagerBase::UndisplayedMap::GetApplicableParent(nsIContent* aParent) +{ // In the case of XBL default content, <xbl:children> elements do not get a // frame causing a mismatch between the content tree and the frame tree. // |GetEntryFor| is sometimes called with the content tree parent (which may // be a <xbl:children> element) but the parent in the frame tree would be the // insertion parent (parent of the <xbl:children> element). Here the children // elements are normalized to the insertion parent to correct for the mismatch. - if (parentContent && nsContentUtils::IsContentInsertionPoint(parentContent)) { - parentContent = parentContent->GetParent(); - // Change the caller's pointer for the parent content to be the insertion parent. - *aParentContent = parentContent; + if (aParent && aParent->IsActiveChildrenElement()) { + return aParent->GetParent(); } - PLHashNumber hashCode = NS_PTR_TO_INT32(parentContent); - PLHashEntry** entry = PL_HashTableRawLookup(mTable, hashCode, parentContent); - if (*entry) { - mLastLookup = entry; - } - return entry; + return aParent; } -UndisplayedNode* -nsFrameManagerBase::UndisplayedMap::GetFirstNode(nsIContent* aParentContent) +LinkedList<UndisplayedNode>* +nsFrameManagerBase::UndisplayedMap::GetListFor(nsIContent* aParent) { - PLHashEntry** entry = GetEntryFor(&aParentContent); - if (*entry) { - return (UndisplayedNode*)((*entry)->value); + aParent = GetApplicableParent(aParent); + + LinkedList<UndisplayedNode>* list; + if (Get(aParent, &list)) { + return list; } + return nullptr; } +LinkedList<UndisplayedNode>* +nsFrameManagerBase::UndisplayedMap::GetOrCreateListFor(nsIContent* aParent) +{ + aParent = GetApplicableParent(aParent); + return LookupOrAdd(aParent); +} + + +UndisplayedNode* +nsFrameManagerBase::UndisplayedMap::GetFirstNode(nsIContent* aParentContent) +{ + auto* list = GetListFor(aParentContent); + return list ? list->getFirst() : nullptr; +} + void nsFrameManagerBase::UndisplayedMap::AppendNodeFor(UndisplayedNode* aNode, nsIContent* aParentContent) { - PLHashEntry** entry = GetEntryFor(&aParentContent); - if (*entry) { - UndisplayedNode* node = (UndisplayedNode*)((*entry)->value); - while (node->mNext) { - if (node->mContent == aNode->mContent) { - // We actually need to check this in optimized builds because - // there are some callers that do this. See bug 118014, bug - // 136704, etc. - NS_NOTREACHED("node in map twice"); - delete aNode; - return; - } - node = node->mNext; - } - node->mNext = aNode; - } - else { - PLHashNumber hashCode = NS_PTR_TO_INT32(aParentContent); - PL_HashTableRawAdd(mTable, entry, hashCode, aParentContent, aNode); - mLastLookup = nullptr; // hashtable may have shifted bucket out from under us + LinkedList<UndisplayedNode>* list = GetOrCreateListFor(aParentContent); + +#ifdef DEBUG + for (UndisplayedNode* node = list->getFirst(); node; node = node->getNext()) { + // NOTE: In the original code there was a work around for this case, I want + // to check it still happens before hacking around it the same way. + MOZ_ASSERT(node->mContent != aNode->mContent, + "Duplicated content in undisplayed list!"); } +#endif + + list->insertBack(aNode); } -nsresult +void nsFrameManagerBase::UndisplayedMap::AddNodeFor(nsIContent* aParentContent, - nsIContent* aChild, + nsIContent* aChild, nsStyleContext* aStyle) { UndisplayedNode* node = new UndisplayedNode(aChild, aStyle); - AppendNodeFor(node, aParentContent); - return NS_OK; } void nsFrameManagerBase::UndisplayedMap::RemoveNodeFor(nsIContent* aParentContent, UndisplayedNode* aNode) { - PLHashEntry** entry = GetEntryFor(&aParentContent); - NS_ASSERTION(*entry, "content not in map"); - if (*entry) { - if ((UndisplayedNode*)((*entry)->value) == aNode) { // first node - if (aNode->mNext) { - (*entry)->value = aNode->mNext; - aNode->mNext = nullptr; - } - else { - PL_HashTableRawRemove(mTable, entry, *entry); - mLastLookup = nullptr; // hashtable may have shifted bucket out from under us - } - } - else { - UndisplayedNode* node = (UndisplayedNode*)((*entry)->value); - while (node->mNext) { - if (node->mNext == aNode) { - node->mNext = aNode->mNext; - aNode->mNext = nullptr; - break; - } - node = node->mNext; - } - } - } +#ifdef DEBUG + auto list = GetListFor(aParentContent); + MOZ_ASSERT(list, "content not in map"); + aNode->removeFrom(*list); +#else + aNode->remove(); +#endif delete aNode; } -UndisplayedNode* +nsAutoPtr<LinkedList<UndisplayedNode>> nsFrameManagerBase::UndisplayedMap::UnlinkNodesFor(nsIContent* aParentContent) { - PLHashEntry** entry = GetEntryFor(&aParentContent); - NS_ASSERTION(entry, "content not in map"); - if (*entry) { - UndisplayedNode* node = (UndisplayedNode*)((*entry)->value); - NS_ASSERTION(node, "null node for non-null entry in UndisplayedMap"); - PL_HashTableRawRemove(mTable, entry, *entry); - mLastLookup = nullptr; // hashtable may have shifted bucket out from under us - return node; - } - return nullptr; + nsAutoPtr<LinkedList<UndisplayedNode>> list; + RemoveAndForget(GetApplicableParent(aParentContent), list); + return list; } void nsFrameManagerBase::UndisplayedMap::RemoveNodesFor(nsIContent* aParentContent) { - delete UnlinkNodesFor(aParentContent); -} - -static int -RemoveUndisplayedEntry(PLHashEntry* he, int i, void* arg) -{ - UndisplayedNode* node = (UndisplayedNode*)(he->value); - delete node; - // Remove and free this entry and continue enumerating - return HT_ENUMERATE_REMOVE | HT_ENUMERATE_NEXT; -} - -void -nsFrameManagerBase::UndisplayedMap::Clear(void) -{ - mLastLookup = nullptr; - PL_HashTableEnumerateEntries(mTable, RemoveUndisplayedEntry, 0); + nsAutoPtr<LinkedList<UndisplayedNode>> list = UnlinkNodesFor(aParentContent); + if (list) { + while (auto* node = list->popFirst()) { + delete node; + } + } } uint32_t nsFrameManagerBase::sGlobalGenerationNumber; diff --git a/layout/base/nsFrameManager.h b/layout/base/nsFrameManager.h index 1b9148314f..ae7477d3d3 100644 --- a/layout/base/nsFrameManager.h +++ b/layout/base/nsFrameManager.h @@ -33,40 +33,25 @@ namespace mozilla { * Node in a linked list, containing the style for an element that * does not have a frame but whose parent does have a frame. */ -struct UndisplayedNode { +struct UndisplayedNode : public LinkedListElement<UndisplayedNode> +{ UndisplayedNode(nsIContent* aContent, nsStyleContext* aStyle) - : mContent(aContent), - mStyle(aStyle), - mNext(nullptr) + : mContent(aContent) + , mStyle(aStyle) { MOZ_COUNT_CTOR(mozilla::UndisplayedNode); } - ~UndisplayedNode() - { - MOZ_COUNT_DTOR(mozilla::UndisplayedNode); - - // Delete mNext iteratively to avoid blowing up the stack (bug 460461). - UndisplayedNode* cur = mNext; - while (cur) { - UndisplayedNode* next = cur->mNext; - cur->mNext = nullptr; - delete cur; - cur = next; - } - } + ~UndisplayedNode() { MOZ_COUNT_DTOR(mozilla::UndisplayedNode); } - nsCOMPtr<nsIContent> mContent; - RefPtr<nsStyleContext> mStyle; - UndisplayedNode* mNext; + nsCOMPtr<nsIContent> mContent; + RefPtr<nsStyleContext> mStyle; }; } // namespace mozilla /** - * Frame manager interface. The frame manager serves two purposes: - * <li>provides a service for mapping from content to frame and from - * out-of-flow frame to placeholder frame. + * Frame manager interface. The frame manager serves one purpose: * <li>handles structural modifications to the frame model. If the frame model * lock can be acquired, then the changes are processed immediately; otherwise, * they're queued and processed later. @@ -94,15 +79,8 @@ public: */ void Destroy(); - // Placeholder frame functions - nsPlaceholderFrame* GetPlaceholderFrameFor(const nsIFrame* aFrame); - void RegisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame); - void UnregisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame); - - void ClearPlaceholderFrameMap(); - // Mapping undisplayed content - nsStyleContext* GetUndisplayedContent(nsIContent* aContent) + nsStyleContext* GetUndisplayedContent(const nsIContent* aContent) { if (!mUndisplayedMap) { return nullptr; @@ -127,7 +105,7 @@ public: /** * Return the registered display:contents style context for aContent, if any. */ - nsStyleContext* GetDisplayContentsStyleFor(nsIContent* aContent) + nsStyleContext* GetDisplayContentsStyleFor(const nsIContent* aContent) { if (!mDisplayContentsMap) { return nullptr; @@ -207,7 +185,7 @@ public: nsILayoutHistoryState* aState); protected: static nsStyleContext* GetStyleContextInMap(UndisplayedMap* aMap, - nsIContent* aContent); + const nsIContent* aContent); static mozilla::UndisplayedNode* GetAllUndisplayedNodesInMapFor(UndisplayedMap* aMap, nsIContent* aParentContent); diff --git a/layout/base/nsFrameManagerBase.h b/layout/base/nsFrameManagerBase.h index 757dce7e5c..bb192b64f4 100644 --- a/layout/base/nsFrameManagerBase.h +++ b/layout/base/nsFrameManagerBase.h @@ -53,7 +53,6 @@ protected: // weak link, because the pres shell owns us nsIPresShell* MOZ_NON_OWNING_REF mPresShell; nsIFrame* mRootFrame; - PLDHashTable mPlaceholderMap; UndisplayedMap* mUndisplayedMap; UndisplayedMap* mDisplayContentsMap; bool mIsDestroyingFrames; // The frame manager is destroying some frame(s). diff --git a/layout/base/nsFrameTraversal.cpp b/layout/base/nsFrameTraversal.cpp index 40fb5ff30c..76dd40af1a 100644 --- a/layout/base/nsFrameTraversal.cpp +++ b/layout/base/nsFrameTraversal.cpp @@ -82,6 +82,10 @@ protected: virtual nsIFrame* GetNextSiblingInner(nsIFrame* aFrame); virtual nsIFrame* GetPrevSiblingInner(nsIFrame* aFrame); + /** + * Return the placeholder frame for aFrame if it has one, otherwise return + * aFrame itself. + */ nsIFrame* GetPlaceholderFrame(nsIFrame* aFrame); bool IsPopupFrame(nsIFrame* aFrame); @@ -486,18 +490,11 @@ nsFrameIterator::GetPrevSiblingInner(nsIFrame* aFrame) { nsIFrame* nsFrameIterator::GetPlaceholderFrame(nsIFrame* aFrame) { - nsIFrame* result = aFrame; - nsIPresShell *presShell = mPresContext->GetPresShell(); - if (presShell) { - nsIFrame* placeholder = presShell->GetPlaceholderFrameFor(aFrame); - if (placeholder) - result = placeholder; + if (MOZ_LIKELY(!aFrame || !aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW))) { + return aFrame; } - - if (result != aFrame) - result = GetPlaceholderFrame(result); - - return result; + nsIFrame* placeholder = aFrame->GetPlaceholderFrame(); + return placeholder ? placeholder : aFrame; } bool diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h index 865f5534c6..5f83b9c153 100644 --- a/layout/base/nsIPresShell.h +++ b/layout/base/nsIPresShell.h @@ -486,12 +486,6 @@ public: virtual nsCanvasFrame* GetCanvasFrame() const = 0; /** - * Gets the placeholder frame associated with the specified frame. This is - * a helper frame that forwards the request to the frame manager. - */ - virtual nsIFrame* GetPlaceholderFrameFor(nsIFrame* aFrame) const = 0; - - /** * Tell the pres shell that a frame needs to be marked dirty and needs * Reflow. It's OK if this is an ancestor of the frame needing reflow as * long as the ancestor chain between them doesn't cross a reflow root. @@ -545,20 +539,12 @@ public: virtual void NotifyCounterStylesAreDirty() = 0; /** - * Destroy the frames for aContent. Note that this may destroy frames - * for an ancestor instead - aDestroyedFramesFor contains the content node - * where frames were actually destroyed (which should be used in the - * CreateFramesFor call). The frame tree state will be captured before - * the frames are destroyed in the frame constructor. - */ - virtual void DestroyFramesFor(nsIContent* aContent, - nsIContent** aDestroyedFramesFor) = 0; - /** - * Create new frames for aContent. It will use the last captured layout - * history state captured in the frame constructor to restore the state - * in the new frame tree. + * Destroy the frames for aElement, and reconstruct them asynchronously if + * needed. + * + * Note that this may destroy frames for an ancestor instead. */ - virtual void CreateFramesFor(nsIContent* aContent) = 0; + virtual void DestroyFramesForAndRestyle(mozilla::dom::Element* aElement) = 0; /** * Recreates the frames for a node @@ -977,7 +963,7 @@ public: /** * Reconstruct frames for all elements in the document */ - virtual nsresult ReconstructFrames() = 0; + virtual void ReconstructFrames() = 0; /** * Notify that a content node's state has changed diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 21d20c69fa..7496a49463 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -1486,90 +1486,38 @@ nsLayoutUtils::GetChildListNameFor(nsIFrame* aChildFrame) return id; } -/*static*/ nsIFrame* -nsLayoutUtils::GetBeforeFrameForContent(nsIFrame* aFrame, - const nsIContent* aContent) -{ - // We need to call GetGenConPseudos() on the first continuation/ib-split. - // Find it, for symmetry with GetAfterFrameForContent. - nsContainerFrame* genConParentFrame = - FirstContinuationOrIBSplitSibling(aFrame)->GetContentInsertionFrame(); - if (!genConParentFrame) { - return nullptr; - } - nsTArray<nsIContent*>* prop = genConParentFrame->GetGenConPseudos(); - if (prop) { - const nsTArray<nsIContent*>& pseudos(*prop); - for (uint32_t i = 0; i < pseudos.Length(); ++i) { - if (pseudos[i]->GetParent() == aContent && - pseudos[i]->NodeInfo()->NameAtom() == nsGkAtoms::mozgeneratedcontentbefore) { - return pseudos[i]->GetPrimaryFrame(); - } - } - } - // If the first child frame is a pseudo-frame, then try that. - // Note that the frame we create for the generated content is also a - // pseudo-frame and so don't drill down in that case. - nsIFrame* childFrame = genConParentFrame->PrincipalChildList().FirstChild(); - if (childFrame && - childFrame->IsPseudoFrame(aContent) && - !childFrame->IsGeneratedContentFrame()) { - return GetBeforeFrameForContent(childFrame, aContent); - } - return nullptr; +static Element* +GetPseudo(const nsIContent* aContent, nsIAtom* aPseudoProperty) +{ + MOZ_ASSERT(aPseudoProperty == nsGkAtoms::beforePseudoProperty || + aPseudoProperty == nsGkAtoms::afterPseudoProperty); + return static_cast<Element*>(aContent->GetProperty(aPseudoProperty)); } -/*static*/ nsIFrame* -nsLayoutUtils::GetBeforeFrame(nsIFrame* aFrame) +/*static*/ Element* +nsLayoutUtils::GetBeforePseudo(const nsIContent* aContent) { - return GetBeforeFrameForContent(aFrame, aFrame->GetContent()); + return GetPseudo(aContent, nsGkAtoms::beforePseudoProperty); } /*static*/ nsIFrame* -nsLayoutUtils::GetAfterFrameForContent(nsIFrame* aFrame, - const nsIContent* aContent) -{ - // We need to call GetGenConPseudos() on the first continuation, - // but callers are likely to pass the last. - nsContainerFrame* genConParentFrame = - FirstContinuationOrIBSplitSibling(aFrame)->GetContentInsertionFrame(); - if (!genConParentFrame) { - return nullptr; - } - nsTArray<nsIContent*>* prop = genConParentFrame->GetGenConPseudos(); - if (prop) { - const nsTArray<nsIContent*>& pseudos(*prop); - for (uint32_t i = 0; i < pseudos.Length(); ++i) { - if (pseudos[i]->GetParent() == aContent && - pseudos[i]->NodeInfo()->NameAtom() == nsGkAtoms::mozgeneratedcontentafter) { - return pseudos[i]->GetPrimaryFrame(); - } - } - } - // If the last child frame is a pseudo-frame, then try that. - // Note that the frame we create for the generated content is also a - // pseudo-frame and so don't drill down in that case. - genConParentFrame = aFrame->GetContentInsertionFrame(); - if (!genConParentFrame) { - return nullptr; - } - nsIFrame* lastParentContinuation = - LastContinuationWithChild(static_cast<nsContainerFrame*>( - LastContinuationOrIBSplitSibling(genConParentFrame))); - nsIFrame* childFrame = - lastParentContinuation->GetChildList(nsIFrame::kPrincipalList).LastChild(); - if (childFrame && - childFrame->IsPseudoFrame(aContent) && - !childFrame->IsGeneratedContentFrame()) { - return GetAfterFrameForContent(childFrame->FirstContinuation(), aContent); - } - return nullptr; +nsLayoutUtils::GetBeforeFrame(const nsIContent* aContent) +{ + Element* pseudo = GetBeforePseudo(aContent); + return pseudo ? pseudo->GetPrimaryFrame() : nullptr; +} + +/*static*/ Element* +nsLayoutUtils::GetAfterPseudo(const nsIContent* aContent) +{ + return GetPseudo(aContent, nsGkAtoms::afterPseudoProperty); } /*static*/ nsIFrame* -nsLayoutUtils::GetAfterFrame(nsIFrame* aFrame) +nsLayoutUtils::GetAfterFrame(const nsIContent* aContent) { - return GetAfterFrameForContent(aFrame, aFrame->GetContent()); + Element* pseudo = GetAfterPseudo(aContent); + return pseudo ? pseudo->GetPrimaryFrame() : nullptr; } // static @@ -1640,33 +1588,6 @@ nsLayoutUtils::GetFloatFromPlaceholder(nsIFrame* aFrame) { } // static -bool -nsLayoutUtils::IsGeneratedContentFor(nsIContent* aContent, - nsIFrame* aFrame, - nsIAtom* aPseudoElement) -{ - NS_PRECONDITION(aFrame, "Must have a frame"); - NS_PRECONDITION(aPseudoElement, "Must have a pseudo name"); - - if (!aFrame->IsGeneratedContentFrame()) { - return false; - } - nsIFrame* parent = aFrame->GetParent(); - NS_ASSERTION(parent, "Generated content can't be root frame"); - if (parent->IsGeneratedContentFrame()) { - // Not the root of the generated content - return false; - } - - if (aContent && parent->GetContent() != aContent) { - return false; - } - - return (aFrame->GetContent()->NodeInfo()->NameAtom() == nsGkAtoms::mozgeneratedcontentbefore) == - (aPseudoElement == nsCSSPseudoElements::before); -} - -// static nsIFrame* nsLayoutUtils::GetCrossDocParentFrame(const nsIFrame* aFrame, nsPoint* aExtraOffset) @@ -4400,8 +4321,7 @@ nsLayoutUtils::GetParentOrPlaceholderFor(nsIFrame* aFrame) { if ((aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) && !aFrame->GetPrevInFlow()) { - return aFrame->PresContext()->PresShell()->FrameManager()-> - GetPlaceholderFrameFor(aFrame); + return aFrame->GetProperty(nsIFrame::PlaceholderFrameProperty()); } return aFrame->GetParent(); } diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index bba1f32652..36a43e46ba 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -276,50 +276,26 @@ public: static mozilla::layout::FrameChildListID GetChildListNameFor(nsIFrame* aChildFrame); /** - * GetBeforeFrameForContent returns the ::before frame for aContent, if - * one exists. This is typically O(1). The frame passed in must be - * the first-in-flow. - * - * @param aGenConParentFrame an ancestor of the ::before frame - * @param aContent the content whose ::before is wanted - * @return the ::before frame or nullptr if there isn't one + * Returns the ::before pseudo-element for aContent, if any. */ - static nsIFrame* GetBeforeFrameForContent(nsIFrame* aGenConParentFrame, - const nsIContent* aContent); + static mozilla::dom::Element* GetBeforePseudo(const nsIContent* aContent); /** - * GetBeforeFrame returns the outermost ::before frame of the given frame, if - * one exists. This is typically O(1). The frame passed in must be - * the first-in-flow. - * - * @param aFrame the frame whose ::before is wanted - * @return the :before frame or nullptr if there isn't one + * Returns the frame corresponding to the ::before pseudo-element for + * aContent, if any. */ - static nsIFrame* GetBeforeFrame(nsIFrame* aFrame); + static nsIFrame* GetBeforeFrame(const nsIContent* aContent); /** - * GetAfterFrameForContent returns the ::after frame for aContent, if one - * exists. This will walk the in-flow chain of aGenConParentFrame to the - * last-in-flow if needed. This function is typically O(N) in the number - * of child frames, following in-flows, etc. - * - * @param aGenConParentFrame an ancestor of the ::after frame - * @param aContent the content whose ::after is wanted - * @return the ::after frame or nullptr if there isn't one + * Returns the ::after pseudo-element for aContent, if any. */ - static nsIFrame* GetAfterFrameForContent(nsIFrame* aGenConParentFrame, - const nsIContent* aContent); + static mozilla::dom::Element* GetAfterPseudo(const nsIContent* aContent); /** - * GetAfterFrame returns the outermost ::after frame of the given frame, if one - * exists. This will walk the in-flow chain to the last-in-flow if - * needed. This function is typically O(N) in the number of child - * frames, following in-flows, etc. - * - * @param aFrame the frame whose ::after is wanted - * @return the :after frame or nullptr if there isn't one + * Returns the frame corresponding to the ::after pseudo-element for aContent, + * if any. */ - static nsIFrame* GetAfterFrame(nsIFrame* aFrame); + static nsIFrame* GetAfterFrame(const nsIContent* aContent); /** * Given a frame, search up the frame tree until we find an @@ -377,23 +353,6 @@ public: */ static nsIFrame* GetRealPrimaryFrameFor(const nsIContent* aContent); - /** - * IsGeneratedContentFor returns true if aFrame is the outermost - * frame for generated content of type aPseudoElement for aContent. - * aFrame *might not* have the aPseudoElement pseudo-style! For example - * it might be a table wrapper frame and the inner table frame might - * have the pseudo-style. - * - * @param aContent the content node we're looking at. If this is - * null, then we just assume that aFrame has the right content - * pointer. - * @param aFrame the frame we're looking at - * @param aPseudoElement the pseudo type we're interested in - * @return whether aFrame is the generated aPseudoElement frame for aContent - */ - static bool IsGeneratedContentFor(nsIContent* aContent, nsIFrame* aFrame, - nsIAtom* aPseudoElement); - #ifdef DEBUG // TODO: remove, see bug 598468. static bool gPreventAssertInCompareTreePosition; diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 264b52b18d..bd5125637f 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -2828,10 +2828,9 @@ PresShell::CancelAllPendingReflows() } void -PresShell::DestroyFramesFor(nsIContent* aContent, - nsIContent** aDestroyedFramesFor) +PresShell::DestroyFramesForAndRestyle(Element* aElement) { - MOZ_ASSERT(aContent); + MOZ_ASSERT(aElement); NS_ENSURE_TRUE_VOID(mPresContext); if (!mDidInitialize) { return; @@ -2843,43 +2842,20 @@ PresShell::DestroyFramesFor(nsIContent* aContent, ++mChangeNestCount; nsCSSFrameConstructor* fc = FrameConstructor(); + bool didReconstruct; fc->BeginUpdate(); - fc->DestroyFramesFor(aContent, aDestroyedFramesFor); + fc->DestroyFramesFor(aElement, &didReconstruct); fc->EndUpdate(); - --mChangeNestCount; -} + auto changeHint = didReconstruct + ? nsChangeHint(0) + : nsChangeHint_ReconstructFrame; -void -PresShell::CreateFramesFor(nsIContent* aContent) -{ - NS_ENSURE_TRUE_VOID(mPresContext); - if (!mDidInitialize) { - // Nothing to do here. In fact, if we proceed and aContent is the - // root we will crash. - return; - } - - // Don't call RecreateFramesForContent since that is not exported and we want - // to keep the number of entrypoints down. - - NS_ASSERTION(mViewManager, "Should have view manager"); - MOZ_ASSERT(aContent); - - // Have to make sure that the content notifications are flushed before we - // start messing with the frame model; otherwise we can get content doubling. - mDocument->FlushPendingNotifications(Flush_ContentAndNotify); - - nsAutoScriptBlocker scriptBlocker; - - // Mark ourselves as not safe to flush while we're doing frame construction. - ++mChangeNestCount; - - nsCSSFrameConstructor* fc = FrameConstructor(); - nsILayoutHistoryState* layoutState = fc->GetLastCapturedLayoutHistoryState(); - fc->BeginUpdate(); - fc->ContentInserted(aContent->GetParent(), aContent, layoutState, false); - fc->EndUpdate(); + // NOTE(emilio): eRestyle_Subtree is needed to force also a full subtree + // restyle for the content (in Stylo, where the existence of frames != the + // existence of styles). + mPresContext->RestyleManager()->PostRestyleEvent( + aElement, eRestyle_Subtree, changeHint); --mChangeNestCount; } @@ -4444,14 +4420,14 @@ PresShell::NotifyCounterStylesAreDirty() mFrameConstructor->EndUpdate(); } -nsresult -PresShell::ReconstructFrames(void) +void +PresShell::ReconstructFrames() { NS_PRECONDITION(!mFrameConstructor->GetRootFrame() || mDidInitialize, "Must not have root frame before initial reflow"); if (!mDidInitialize || mIsDestroying) { // Nothing to do here - return NS_OK; + return; } nsCOMPtr<nsIPresShell> kungFuDeathGrip(this); @@ -4461,16 +4437,14 @@ PresShell::ReconstructFrames(void) mDocument->FlushPendingNotifications(Flush_ContentAndNotify); if (mIsDestroying) { - return NS_OK; + return; } nsAutoCauseReflowNotifier crNotifier(this); mFrameConstructor->BeginUpdate(); - nsresult rv = mFrameConstructor->ReconstructDocElementHierarchy(); + mFrameConstructor->ReconstructDocElementHierarchy(); VERIFY_STYLE_TREE; mFrameConstructor->EndUpdate(); - - return rv; } void @@ -4598,12 +4572,6 @@ PresShell::StyleRuleRemoved(StyleSheet* aStyleSheet) RecordStyleSheetChange(aStyleSheet); } -nsIFrame* -PresShell::GetPlaceholderFrameFor(nsIFrame* aFrame) const -{ - return mFrameConstructor->GetPlaceholderFrameFor(aFrame); -} - nsresult PresShell::RenderDocument(const nsRect& aRect, uint32_t aFlags, nscolor aBackgroundColor, @@ -4755,9 +4723,11 @@ PresShell::ClipListToRange(nsDisplayListBuilder *aBuilder, frame->GetOffsets(frameStartOffset, frameEndOffset); int32_t hilightStart = - atStart ? std::max(aRange->StartOffset(), frameStartOffset) : frameStartOffset; + atStart ? std::max(static_cast<int32_t>(aRange->StartOffset()), + frameStartOffset) : frameStartOffset; int32_t hilightEnd = - atEnd ? std::min(aRange->EndOffset(), frameEndOffset) : frameEndOffset; + atEnd ? std::min(static_cast<int32_t>(aRange->EndOffset()), + frameEndOffset) : frameEndOffset; if (hilightStart < hilightEnd) { // determine the location of the start and end edges of the range. nsPoint startPoint, endPoint; diff --git a/layout/base/nsPresShell.h b/layout/base/nsPresShell.h index f20370d73a..628e613c88 100644 --- a/layout/base/nsPresShell.h +++ b/layout/base/nsPresShell.h @@ -130,7 +130,6 @@ public: virtual nsIPageSequenceFrame* GetPageSequenceFrame() const override; virtual nsCanvasFrame* GetCanvasFrame() const override; - virtual nsIFrame* GetPlaceholderFrameFor(nsIFrame* aFrame) const override; virtual void FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty, nsFrameState aBitToAdd, ReflowRootHandling aRootHandling = @@ -140,9 +139,7 @@ public: virtual bool IsSafeToFlush() const override; virtual void FlushPendingNotifications(mozFlushType aType) override; virtual void FlushPendingNotifications(mozilla::ChangesToFlush aType) override; - virtual void DestroyFramesFor(nsIContent* aContent, - nsIContent** aDestroyedFramesFor) override; - virtual void CreateFramesFor(nsIContent* aContent) override; + virtual void DestroyFramesForAndRestyle(mozilla::dom::Element* aElement) override; /** * Recreates the frames for a node @@ -200,7 +197,7 @@ public: virtual void NotifyCounterStylesAreDirty() override; - virtual nsresult ReconstructFrames(void) override; + virtual void ReconstructFrames(void) override; virtual void Freeze() override; virtual void Thaw() override; virtual void FireOrClearDelayedEvents(bool aFireEvents) override; diff --git a/layout/forms/nsColorControlFrame.cpp b/layout/forms/nsColorControlFrame.cpp index e0bae43a98..63aee814aa 100644 --- a/layout/forms/nsColorControlFrame.cpp +++ b/layout/forms/nsColorControlFrame.cpp @@ -67,6 +67,7 @@ nsColorControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) { nsCOMPtr<nsIDocument> doc = mContent->GetComposedDoc(); mColorContent = doc->CreateHTMLElement(nsGkAtoms::div); + mColorContent->SetPseudoElementType(CSSPseudoElementType::mozColorSwatch); // Mark the element to be native anonymous before setting any attributes. mColorContent->SetIsNativeAnonymousRoot(); @@ -74,11 +75,7 @@ nsColorControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) nsresult rv = UpdateColor(); NS_ENSURE_SUCCESS(rv, rv); - CSSPseudoElementType pseudoType = CSSPseudoElementType::mozColorSwatch; - RefPtr<nsStyleContext> newStyleContext = PresContext()->StyleSet()-> - ResolvePseudoElementStyle(mContent->AsElement(), pseudoType, - StyleContext(), mColorContent->AsElement()); - if (!aElements.AppendElement(ContentInfo(mColorContent, newStyleContext))) { + if (!aElements.AppendElement(mColorContent)) { return NS_ERROR_OUT_OF_MEMORY; } diff --git a/layout/forms/nsComboboxControlFrame.cpp b/layout/forms/nsComboboxControlFrame.cpp index 78185616ff..98a91b9f5d 100644 --- a/layout/forms/nsComboboxControlFrame.cpp +++ b/layout/forms/nsComboboxControlFrame.cpp @@ -115,7 +115,7 @@ NS_IMPL_ISUPPORTS(nsComboButtonListener, // static class data member for Bug 32920 nsComboboxControlFrame* nsComboboxControlFrame::sFocused = nullptr; -nsContainerFrame* +nsComboboxControlFrame* NS_NewComboboxControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, nsFrameState aStateFlags) { nsComboboxControlFrame* it = new (aPresShell) nsComboboxControlFrame(aContext); @@ -249,6 +249,7 @@ nsComboboxControlFrame::~nsComboboxControlFrame() //-------------------------------------------------------------- NS_QUERYFRAME_HEAD(nsComboboxControlFrame) + NS_QUERYFRAME_ENTRY(nsComboboxControlFrame) NS_QUERYFRAME_ENTRY(nsIComboboxControlFrame) NS_QUERYFRAME_ENTRY(nsIFormControlFrame) NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator) @@ -1350,16 +1351,9 @@ nsComboboxDisplayFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, } nsIFrame* -nsComboboxControlFrame::CreateFrameFor(nsIContent* aContent) +nsComboboxControlFrame::CreateFrameForDisplayNode() { - NS_PRECONDITION(nullptr != aContent, "null ptr"); - - NS_ASSERTION(mDisplayContent, "mDisplayContent can't be null!"); - - if (mDisplayContent != aContent) { - // We only handle the frames for mDisplayContent here - return nullptr; - } + MOZ_ASSERT(mDisplayContent); // Get PresShell nsIPresShell *shell = PresContext()->PresShell(); @@ -1384,7 +1378,7 @@ nsComboboxControlFrame::CreateFrameFor(nsIContent* aContent) nsIFrame* textFrame = NS_NewTextFrame(shell, textStyleContext); // initialize the text frame - textFrame->Init(aContent, mDisplayFrame, nullptr); + textFrame->Init(mDisplayContent, mDisplayFrame, nullptr); mDisplayContent->SetPrimaryFrame(textFrame); nsFrameList textList(textFrame, textFrame); diff --git a/layout/forms/nsComboboxControlFrame.h b/layout/forms/nsComboboxControlFrame.h index 22849e8d14..de713576f3 100644 --- a/layout/forms/nsComboboxControlFrame.h +++ b/layout/forms/nsComboboxControlFrame.h @@ -54,9 +54,10 @@ class nsComboboxControlFrame final : public nsBlockFrame, typedef mozilla::gfx::DrawTarget DrawTarget; public: - friend nsContainerFrame* NS_NewComboboxControlFrame(nsIPresShell* aPresShell, - nsStyleContext* aContext, - nsFrameState aFlags); + NS_DECL_QUERYFRAME_TARGET(nsComboboxControlFrame) + friend nsComboboxControlFrame* NS_NewComboboxControlFrame(nsIPresShell* aPresShell, + nsStyleContext* aContext, + nsFrameState aFlags); friend class nsComboboxDisplayFrame; explicit nsComboboxControlFrame(nsStyleContext* aContext); @@ -69,7 +70,9 @@ public: virtual nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) override; virtual void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements, uint32_t aFilter) override; - virtual nsIFrame* CreateFrameFor(nsIContent* aContent) override; + + nsIContent* GetDisplayNode() { return mDisplayContent; } + nsIFrame* CreateFrameForDisplayNode(); #ifdef ACCESSIBILITY virtual mozilla::a11y::AccType AccessibleType() override; diff --git a/layout/forms/nsGfxButtonControlFrame.cpp b/layout/forms/nsGfxButtonControlFrame.cpp index 393145e0b0..df729c1aa9 100644 --- a/layout/forms/nsGfxButtonControlFrame.cpp +++ b/layout/forms/nsGfxButtonControlFrame.cpp @@ -77,30 +77,6 @@ nsGfxButtonControlFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElemen } } -// Create the text content used as label for the button. -// The frame will be generated by the frame constructor. -nsIFrame* -nsGfxButtonControlFrame::CreateFrameFor(nsIContent* aContent) -{ - nsIFrame * newFrame = nullptr; - - if (aContent == mTextContent) { - nsContainerFrame* parentFrame = do_QueryFrame(mFrames.FirstChild()); - - nsPresContext* presContext = PresContext(); - RefPtr<nsStyleContext> textStyleContext; - textStyleContext = presContext->StyleSet()-> - ResolveStyleForText(mTextContent, mStyleContext); - - newFrame = NS_NewTextFrame(presContext->PresShell(), textStyleContext); - // initialize the text frame - newFrame->Init(mTextContent, parentFrame, nullptr); - mTextContent->SetPrimaryFrame(newFrame); - } - - return newFrame; -} - NS_QUERYFRAME_HEAD(nsGfxButtonControlFrame) NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator) NS_QUERYFRAME_TAIL_INHERITING(nsHTMLButtonControlFrame) diff --git a/layout/forms/nsGfxButtonControlFrame.h b/layout/forms/nsGfxButtonControlFrame.h index d91fe3695d..07072fdb56 100644 --- a/layout/forms/nsGfxButtonControlFrame.h +++ b/layout/forms/nsGfxButtonControlFrame.h @@ -42,7 +42,6 @@ public: virtual nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) override; virtual void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements, uint32_t aFilter) override; - virtual nsIFrame* CreateFrameFor(nsIContent* aContent) override; virtual nsresult AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, diff --git a/layout/forms/nsMeterFrame.cpp b/layout/forms/nsMeterFrame.cpp index 1f3bd8022a..6ef362820f 100644 --- a/layout/forms/nsMeterFrame.cpp +++ b/layout/forms/nsMeterFrame.cpp @@ -75,14 +75,9 @@ nsMeterFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) mBarDiv = doc->CreateHTMLElement(nsGkAtoms::div); // Associate ::-moz-meter-bar pseudo-element to the anonymous child. - CSSPseudoElementType pseudoType = CSSPseudoElementType::mozMeterBar; - RefPtr<nsStyleContext> newStyleContext = PresContext()->StyleSet()-> - ResolvePseudoElementStyle(mContent->AsElement(), pseudoType, - StyleContext(), mBarDiv->AsElement()); + mBarDiv->SetPseudoElementType(CSSPseudoElementType::mozMeterBar); - if (!aElements.AppendElement(ContentInfo(mBarDiv, newStyleContext))) { - return NS_ERROR_OUT_OF_MEMORY; - } + aElements.AppendElement(mBarDiv); return NS_OK; } diff --git a/layout/forms/nsNumberControlFrame.cpp b/layout/forms/nsNumberControlFrame.cpp index 9724109cb1..64e3df0fe3 100644 --- a/layout/forms/nsNumberControlFrame.cpp +++ b/layout/forms/nsNumberControlFrame.cpp @@ -325,27 +325,15 @@ nsresult nsNumberControlFrame::MakeAnonymousElement(Element** aResult, nsTArray<ContentInfo>& aElements, nsIAtom* aTagName, - CSSPseudoElementType aPseudoType, - nsStyleContext* aParentContext) + CSSPseudoElementType aPseudoType) { // Get the NodeInfoManager and tag necessary to create the anonymous divs. nsCOMPtr<nsIDocument> doc = mContent->GetComposedDoc(); RefPtr<Element> resultElement = doc->CreateHTMLElement(aTagName); + resultElement->SetPseudoElementType(aPseudoType); - // If we legitimately fail this assertion and need to allow - // non-pseudo-element anonymous children, then we'll need to add a branch - // that calls ResolveStyleFor((*aResult)->AsElement(), aParentContext)") to - // set newStyleContext. - NS_ASSERTION(aPseudoType != CSSPseudoElementType::NotPseudo, - "Expecting anonymous children to all be pseudo-elements"); // Associate the pseudo-element with the anonymous child - RefPtr<nsStyleContext> newStyleContext = - PresContext()->StyleSet()->ResolvePseudoElementStyle(mContent->AsElement(), - aPseudoType, - aParentContext, - resultElement); - - if (!aElements.AppendElement(ContentInfo(resultElement, newStyleContext))) { + if (!aElements.AppendElement(resultElement)) { return NS_ERROR_OUT_OF_MEMORY; } @@ -382,8 +370,7 @@ nsNumberControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) rv = MakeAnonymousElement(getter_AddRefs(mOuterWrapper), aElements, nsGkAtoms::div, - CSSPseudoElementType::mozNumberWrapper, - mStyleContext); + CSSPseudoElementType::mozNumberWrapper); NS_ENSURE_SUCCESS(rv, rv); ContentInfo& outerWrapperCI = aElements.LastElement(); @@ -392,8 +379,7 @@ nsNumberControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) rv = MakeAnonymousElement(getter_AddRefs(mTextField), outerWrapperCI.mChildren, nsGkAtoms::input, - CSSPseudoElementType::mozNumberText, - outerWrapperCI.mStyleContext); + CSSPseudoElementType::mozNumberText); NS_ENSURE_SUCCESS(rv, rv); mTextField->SetAttr(kNameSpaceID_None, nsGkAtoms::type, @@ -442,8 +428,7 @@ nsNumberControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) rv = MakeAnonymousElement(getter_AddRefs(mSpinBox), outerWrapperCI.mChildren, nsGkAtoms::div, - CSSPseudoElementType::mozNumberSpinBox, - outerWrapperCI.mStyleContext); + CSSPseudoElementType::mozNumberSpinBox); NS_ENSURE_SUCCESS(rv, rv); ContentInfo& spinBoxCI = outerWrapperCI.mChildren.LastElement(); @@ -452,16 +437,14 @@ nsNumberControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) rv = MakeAnonymousElement(getter_AddRefs(mSpinUp), spinBoxCI.mChildren, nsGkAtoms::div, - CSSPseudoElementType::mozNumberSpinUp, - spinBoxCI.mStyleContext); + CSSPseudoElementType::mozNumberSpinUp); NS_ENSURE_SUCCESS(rv, rv); // Create the ::-moz-number-spin-down pseudo-element: rv = MakeAnonymousElement(getter_AddRefs(mSpinDown), spinBoxCI.mChildren, nsGkAtoms::div, - CSSPseudoElementType::mozNumberSpinDown, - spinBoxCI.mStyleContext); + CSSPseudoElementType::mozNumberSpinDown); return rv; } diff --git a/layout/forms/nsNumberControlFrame.h b/layout/forms/nsNumberControlFrame.h index 47e32ad65a..5396e71703 100644 --- a/layout/forms/nsNumberControlFrame.h +++ b/layout/forms/nsNumberControlFrame.h @@ -170,8 +170,7 @@ private: nsresult MakeAnonymousElement(Element** aResult, nsTArray<ContentInfo>& aElements, nsIAtom* aTagName, - CSSPseudoElementType aPseudoType, - nsStyleContext* aParentContext); + CSSPseudoElementType aPseudoType); class SyncDisabledStateEvent; friend class SyncDisabledStateEvent; diff --git a/layout/forms/nsProgressFrame.cpp b/layout/forms/nsProgressFrame.cpp index 2445defd35..6d6db7a1fd 100644 --- a/layout/forms/nsProgressFrame.cpp +++ b/layout/forms/nsProgressFrame.cpp @@ -72,12 +72,9 @@ nsProgressFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) mBarDiv = doc->CreateHTMLElement(nsGkAtoms::div); // Associate ::-moz-progress-bar pseudo-element to the anonymous child. - CSSPseudoElementType pseudoType = CSSPseudoElementType::mozProgressBar; - RefPtr<nsStyleContext> newStyleContext = PresContext()->StyleSet()-> - ResolvePseudoElementStyle(mContent->AsElement(), pseudoType, - StyleContext(), mBarDiv->AsElement()); + mBarDiv->SetPseudoElementType(CSSPseudoElementType::mozProgressBar); - if (!aElements.AppendElement(ContentInfo(mBarDiv, newStyleContext))) { + if (!aElements.AppendElement(mBarDiv)) { return NS_ERROR_OUT_OF_MEMORY; } diff --git a/layout/forms/nsRangeFrame.cpp b/layout/forms/nsRangeFrame.cpp index 7590da066e..99faa6043b 100644 --- a/layout/forms/nsRangeFrame.cpp +++ b/layout/forms/nsRangeFrame.cpp @@ -119,13 +119,9 @@ nsRangeFrame::MakeAnonymousDiv(Element** aResult, RefPtr<Element> resultElement = doc->CreateHTMLElement(nsGkAtoms::div); // Associate the pseudo-element with the anonymous child. - RefPtr<nsStyleContext> newStyleContext = - PresContext()->StyleSet()->ResolvePseudoElementStyle(mContent->AsElement(), - aPseudoType, - StyleContext(), - resultElement); + resultElement->SetPseudoElementType(aPseudoType); - if (!aElements.AppendElement(ContentInfo(resultElement, newStyleContext))) { + if (!aElements.AppendElement(resultElement)) { return NS_ERROR_OUT_OF_MEMORY; } diff --git a/layout/forms/nsTextControlFrame.cpp b/layout/forms/nsTextControlFrame.cpp index f8fdf3420a..b34e132e60 100644 --- a/layout/forms/nsTextControlFrame.cpp +++ b/layout/forms/nsTextControlFrame.cpp @@ -352,32 +352,12 @@ nsTextControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) // Create the placeholder anonymous content if needed. if (mUsePlaceholder) { - nsIContent* placeholderNode = txtCtrl->CreatePlaceholderNode(); + Element* placeholderNode = txtCtrl->CreatePlaceholderNode(); NS_ENSURE_TRUE(placeholderNode, NS_ERROR_OUT_OF_MEMORY); // Associate ::placeholder pseudo-element with the placeholder node. - CSSPseudoElementType pseudoType = CSSPseudoElementType::placeholder; - - // If this is a text input inside a number input then we want to use the - // main number input as the source of style for the placeholder frame. - nsIFrame* mainInputFrame = this; - if (StyleContext()->GetPseudoType() == CSSPseudoElementType::mozNumberText) { - do { - mainInputFrame = mainInputFrame->GetParent(); - } while (mainInputFrame && - mainInputFrame->GetType() != nsGkAtoms::numberControlFrame); - MOZ_ASSERT(mainInputFrame); - } - - RefPtr<nsStyleContext> placeholderStyleContext = - PresContext()->StyleSet()->ResolvePseudoElementStyle( - mainInputFrame->GetContent()->AsElement(), pseudoType, StyleContext(), - placeholderNode->AsElement()); - - if (!aElements.AppendElement(ContentInfo(placeholderNode, - placeholderStyleContext))) { - return NS_ERROR_OUT_OF_MEMORY; - } + placeholderNode->SetPseudoElementType(CSSPseudoElementType::placeholder); + aElements.AppendElement(placeholderNode); if (!IsSingleLineTextControl()) { // For textareas, UpdateValueDisplay doesn't initialize the visibility @@ -778,7 +758,12 @@ nsTextControlFrame::SetSelectionInternal(nsIDOMNode *aStartNode, // we have access to the node. nsCOMPtr<nsINode> start = do_QueryInterface(aStartNode); nsCOMPtr<nsINode> end = do_QueryInterface(aEndNode); - nsresult rv = range->Set(start, aStartOffset, end, aEndOffset); + // XXXbz nsRange::SetStartAndEnd takes int32_t (and ranges generally work on + // int32_t), but we're passing uint32_t. The good news is that at this point + // our endpoints should really be within our length, so not really that big. + // And if they _are_ that big, SetStartAndEnd() will simply error out, which + // is not too bad for a case we don't expect to happen. + nsresult rv = range->SetStartAndEnd(start, aStartOffset, end, aEndOffset); NS_ENSURE_SUCCESS(rv, rv); // Get the selection, clear it and add the new range to it! diff --git a/layout/generic/DetailsFrame.cpp b/layout/generic/DetailsFrame.cpp index 1d28bbe960..389a476cba 100644 --- a/layout/generic/DetailsFrame.cpp +++ b/layout/generic/DetailsFrame.cpp @@ -129,3 +129,12 @@ DetailsFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements, aElements.AppendElement(mDefaultSummary); } } + +bool +DetailsFrame::HasMainSummaryFrame(nsIFrame* aSummaryFrame) +{ + nsIFrame* firstChild = + nsPlaceholderFrame::GetRealFrameFor(mFrames.FirstChild()); + + return aSummaryFrame == firstChild; +} diff --git a/layout/generic/DetailsFrame.h b/layout/generic/DetailsFrame.h index 1eb4ea87be..4ae177f16f 100644 --- a/layout/generic/DetailsFrame.h +++ b/layout/generic/DetailsFrame.h @@ -54,6 +54,12 @@ public: void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements, uint32_t aFilter) override; + // Returns true if |aSummaryFrame| is the main summary (i.e. the first child + // of this details frame). + // This function is used when the summary element is removed from the parent + // details element since at that moment the summary element has been already + // removed from the details element children. + bool HasMainSummaryFrame(nsIFrame* aSummaryFrame); private: nsCOMPtr<nsIContent> mDefaultSummary; diff --git a/layout/generic/ReflowInput.cpp b/layout/generic/ReflowInput.cpp index 78eca8c6cb..9f7b4368c3 100644 --- a/layout/generic/ReflowInput.cpp +++ b/layout/generic/ReflowInput.cpp @@ -1564,8 +1564,7 @@ ReflowInput::InitAbsoluteConstraints(nsPresContext* aPresContext, // have been if it had been in the flow nsHypotheticalPosition hypotheticalPos; if ((iStartIsAuto && iEndIsAuto) || (bStartIsAuto && bEndIsAuto)) { - nsIFrame* placeholderFrame = - aPresContext->PresShell()->GetPlaceholderFrameFor(mFrame); + nsPlaceholderFrame* placeholderFrame = mFrame->GetPlaceholderFrame(); NS_ASSERTION(placeholderFrame, "no placeholder frame"); if (placeholderFrame->HasAnyStateBits( diff --git a/layout/generic/Selection.h b/layout/generic/Selection.h index 5414d15c12..cc696a7c78 100644 --- a/layout/generic/Selection.h +++ b/layout/generic/Selection.h @@ -31,6 +31,9 @@ class nsHTMLCopyEncoder; namespace mozilla { class ErrorResult; struct AutoPrepareFocusRange; +namespace dom { +class DocGroup; +} // namespace dom } // namespace mozilla struct RangeData @@ -73,6 +76,7 @@ public: nsresult EndBatchChangesInternal(int16_t aReason = nsISelectionListener::NO_REASON); nsIDocument* GetParentObject() const; + DocGroup* GetDocGroup() const; // utility methods for scrolling the selection into view nsPresContext* GetPresContext() const; diff --git a/layout/generic/crashtests/1381134-2.html b/layout/generic/crashtests/1381134-2.html new file mode 100644 index 0000000000..d3ac73507a --- /dev/null +++ b/layout/generic/crashtests/1381134-2.html @@ -0,0 +1,45 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="UTF-8"> +<script> +addEventListener("DOMContentLoaded", () => { + [d1, d2] = document.getElementsByTagName("div"); + [s1, s2] = document.getElementsByTagName("span") + d3 = document.createElement("div") + d4 = document.createElement("div") + d4.setAttribute("class", "grid") + d3.appendChild(d4) + d1.appendChild(document.createElement("span")) + setTimeout(() => { + d2.removeChild(s2) + setTimeout(() => { + d2.insertBefore(d3, s1) + }, 100) + }, 100) +}) +</script> +<style> +.columns { + columns: 3; +} +.grid { + border:5px solid; + counter-reset: item; +} +.grid * { display:block; } +span { display:contents; } +span::before { content: counter(item) ":before"; } +span::after { content: counter(item) ":after"; } +</style> +</head> +<body> +<div class=columns> +<div class=grid> +<c></c> +<span><c></c></span> +<span><c></c></span> +</div> +</div> +</body> +</html> diff --git a/layout/generic/crashtests/1381134.html b/layout/generic/crashtests/1381134.html new file mode 100644 index 0000000000..a45fa04ecb --- /dev/null +++ b/layout/generic/crashtests/1381134.html @@ -0,0 +1,45 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="UTF-8"> +<script> +addEventListener("DOMContentLoaded", () => { + [d1, d2] = document.getElementsByTagName("div"); + [s1, s2] = document.getElementsByTagName("span") + d3 = document.createElement("div") + d4 = document.createElement("div") + d4.setAttribute("class", "grid") + d3.appendChild(d4) + d1.appendChild(document.createElement("span")) + setTimeout(() => { + d2.removeChild(s2) + setTimeout(() => { + d2.insertBefore(d3, s1) + }, 100) + }, 100) +}) +</script> +<style> +.columns { + columns: 3; +} +.grid { + display: grid; + border:5px solid; + counter-reset: item; +} +span { display:contents; } +span::before { content: counter(item) ":before"; } +span::after { content: counter(item) ":after"; } +</style> +</head> +<body> +<div class=columns> +<div class=grid> +<c></c> +<span><c></c></span> +<span><c></c></span> +</div> +</div> +</body> +</html> diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list index a3c0d62c68..1a597e51c0 100644 --- a/layout/generic/crashtests/crashtests.list +++ b/layout/generic/crashtests/crashtests.list @@ -642,3 +642,5 @@ load 1278461-1.html load 1278461-2.html load 1304441.html load 1316649.html +load 1381134.html +load 1381134-2.html diff --git a/layout/generic/nsAbsoluteContainingBlock.cpp b/layout/generic/nsAbsoluteContainingBlock.cpp index e3c847d01b..a92a2062d5 100644 --- a/layout/generic/nsAbsoluteContainingBlock.cpp +++ b/layout/generic/nsAbsoluteContainingBlock.cpp @@ -338,17 +338,9 @@ nsAbsoluteContainingBlock::DoMarkFramesDirty(bool aMarkAllDirty) // Given an out-of-flow frame, this method returns the parent frame of // its placeholder frame, if that parent is a nsContainerFrame. static nsContainerFrame* -GetPlaceholderContainer(nsPresContext* aPresContext, - nsIFrame* aPositionedFrame) +GetPlaceholderContainer(nsIFrame* aPositionedFrame) { - MOZ_ASSERT(aPositionedFrame, "need non-null frame"); - MOZ_ASSERT(aPositionedFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW), - "expecting abspos frame"); - MOZ_ASSERT(aPresContext && aPresContext == aPositionedFrame->PresContext(), - "need non-null pres context which matches our frame"); - - nsIFrame* placeholder = - aPresContext->PresShell()->GetPlaceholderFrameFor(aPositionedFrame); + nsIFrame* placeholder = aPositionedFrame->GetPlaceholderFrame(); if (!placeholder) { return nullptr; @@ -557,8 +549,7 @@ nsAbsoluteContainingBlock::ResolveSizeDependentOffsets( aOffsets->IEnd(outerWM) - aMargin.IStartEnd(outerWM) - aKidSize.ISize(outerWM); } else if (aKidReflowInput.mFlags.mIOffsetsNeedCSSAlign) { - placeholderContainer = GetPlaceholderContainer(aPresContext, - aKidReflowInput.mFrame); + placeholderContainer = GetPlaceholderContainer(aKidReflowInput.mFrame); nscoord offset = OffsetToAlignedStaticPos(aKidReflowInput, aKidSize, logicalCBSizeOuterWM, placeholderContainer, @@ -579,8 +570,7 @@ nsAbsoluteContainingBlock::ResolveSizeDependentOffsets( aKidSize.BSize(outerWM); } else if (aKidReflowInput.mFlags.mBOffsetsNeedCSSAlign) { if (!placeholderContainer) { - placeholderContainer = GetPlaceholderContainer(aPresContext, - aKidReflowInput.mFrame); + placeholderContainer = GetPlaceholderContainer(aKidReflowInput.mFrame); } nscoord offset = OffsetToAlignedStaticPos(aKidReflowInput, aKidSize, logicalCBSizeOuterWM, @@ -655,8 +645,7 @@ nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegat // due to the multiple coordinate spaces in play, we use a convenience flag // to simply have the child's ReflowInput give it a static position at its // abs.pos. CB origin, and then we'll align & offset it from there. - nsIFrame* placeholder = - aPresContext->PresShell()->GetPlaceholderFrameFor(aKidFrame); + nsIFrame* placeholder = aKidFrame->GetPlaceholderFrame(); if (placeholder && placeholder->GetParent() == aDelegatingFrame) { rsFlags |= ReflowInput::STATIC_POS_IS_CB_ORIGIN; } diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index a37bfc06bd..8fec6cd6a6 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -4336,8 +4336,7 @@ CheckPlaceholderInLine(nsIFrame* aBlock, nsLineBox* aLine, nsFloatCache* aFC) "float in a line should never be a continuation"); NS_ASSERTION(!(aFC->mFloat->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT), "float in a line should never be a pushed float"); - nsIFrame* ph = aBlock->PresContext()->FrameManager()-> - GetPlaceholderFrameFor(aFC->mFloat->FirstInFlow()); + nsIFrame* ph = aFC->mFloat->FirstInFlow()->GetPlaceholderFrame(); for (nsIFrame* f = ph; f; f = f->GetParent()) { if (f->GetParent() == aBlock) return aLine->Contains(f); @@ -4939,9 +4938,8 @@ nsBlockFrame::DrainSelfPushedFloats() if (f->GetPrevContinuation()) { // FIXME } else { - nsPlaceholderFrame *placeholder = - presContext->FrameManager()->GetPlaceholderFrameFor(f); - nsIFrame *floatOriginalParent = presContext->PresShell()-> + nsPlaceholderFrame* placeholder = f->GetPlaceholderFrame(); + nsIFrame* floatOriginalParent = presContext->PresShell()-> FrameConstructor()->GetFloatContainingBlock(placeholder); if (floatOriginalParent != this) { // This is a first continuation that was pushed from one of our @@ -5565,7 +5563,7 @@ FindChildContaining(nsBlockFrame* aFrame, nsIFrame* aFindFrame) return nullptr; if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) break; - aFindFrame = aFrame->PresContext()->FrameManager()->GetPlaceholderFrameFor(child); + aFindFrame = child->GetPlaceholderFrame(); } return child; @@ -6847,9 +6845,8 @@ nsBlockFrame::ChildIsDirty(nsIFrame* aChild) AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES); } else { NS_ASSERTION(aChild->IsFloating(), "should be a float"); - nsIFrame *thisFC = FirstContinuation(); - nsIFrame *placeholderPath = - PresContext()->FrameManager()->GetPlaceholderFrameFor(aChild); + nsIFrame* thisFC = FirstContinuation(); + nsIFrame* placeholderPath = aChild->GetPlaceholderFrame(); // SVG code sometimes sends FrameNeedsReflow notifications during // frame destruction, leading to null placeholders, but we're safe // ignoring those. diff --git a/layout/generic/nsFirstLetterFrame.cpp b/layout/generic/nsFirstLetterFrame.cpp index 980e1e9bed..5f561c7593 100644 --- a/layout/generic/nsFirstLetterFrame.cpp +++ b/layout/generic/nsFirstLetterFrame.cpp @@ -320,8 +320,7 @@ nsFirstLetterFrame::CreateContinuationForFloatingParent(nsPresContext* aPresCont *aContinuation = nullptr; nsIPresShell* presShell = aPresContext->PresShell(); - nsPlaceholderFrame* placeholderFrame = - presShell->FrameManager()->GetPlaceholderFrameFor(this); + nsPlaceholderFrame* placeholderFrame = GetPlaceholderFrame(); nsContainerFrame* parent = placeholderFrame->GetParent(); nsIFrame* continuation = presShell->FrameConstructor()-> diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 0d0c7108cc..ea29c6945e 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -633,8 +633,7 @@ nsFrame::DestroyFrom(nsIFrame* aDestructRoot) nsIPresShell *shell = presContext->GetPresShell(); if (mState & NS_FRAME_OUT_OF_FLOW) { - nsPlaceholderFrame* placeholder = - shell->FrameManager()->GetPlaceholderFrameFor(this); + nsPlaceholderFrame* placeholder = GetPlaceholderFrame(); NS_ASSERTION(!placeholder || (aDestructRoot != this), "Don't call Destroy() on OOFs, call Destroy() on the placeholder."); NS_ASSERTION(!placeholder || @@ -642,7 +641,6 @@ nsFrame::DestroyFrom(nsIFrame* aDestructRoot) "Placeholder relationship should have been torn down already; " "this might mean we have a stray placeholder in the tree."); if (placeholder) { - shell->FrameManager()->UnregisterPlaceholderFrame(placeholder); placeholder->SetOutOfFlowFrame(nullptr); } } @@ -8073,9 +8071,8 @@ int32_t nsFrame::GetLineNumber(nsIFrame *aFrame, bool aLockScroll, nsIFrame** aContainingBlock) { NS_ASSERTION(aFrame, "null aFrame"); - nsFrameManager* frameManager = aFrame->PresContext()->FrameManager(); - nsIFrame *blockFrame = aFrame; - nsIFrame *thisBlock; + nsIFrame* blockFrame = aFrame; + nsIFrame* thisBlock; nsAutoLineIterator it; nsresult result = NS_ERROR_FAILURE; while (NS_FAILED(result) && blockFrame) @@ -8088,7 +8085,7 @@ nsFrame::GetLineNumber(nsIFrame *aFrame, bool aLockScroll, nsIFrame** aContainin // abspos continuations don't have placeholders, get the fif thisBlock = thisBlock->FirstInFlow(); } - thisBlock = frameManager->GetPlaceholderFrameFor(thisBlock); + thisBlock = thisBlock->GetPlaceholderFrame(); if (!thisBlock) return -1; } @@ -8918,6 +8915,8 @@ GetIBSplitSiblingForAnonymousBlock(const nsIFrame* aFrame) * * Also skip anonymous scrolled-content parents; inherit directly from the * outer scroll frame. + * + * Also skip NAC parents if the child frame is NAC. */ static nsIFrame* GetCorrectedParent(const nsIFrame* aFrame) @@ -8943,6 +8942,32 @@ GetCorrectedParent(const nsIFrame* aFrame) if (pseudo == nsCSSAnonBoxes::tableWrapper) { pseudo = aFrame->PrincipalChildList().FirstChild()->StyleContext()->GetPseudo(); } + + // Prevent NAC from inheriting NAC. This partially duplicates the logic + // implemented in nsCSSFrameConstructor::AddFCItemsForAnonymousContent, and is + // necessary so that restyle inherits style contexts in the same way as the + // initial styling performed in frame construction. + // + // It would be nice to put it in CorrectStyleParentFrame and therefore share + // it, but that would lose the information of whether the _child_ is NAC, + // since CorrectStyleParentFrame only knows about the prospective _parent_. + // This duplication and complexity will go away when we fully switch to the + // Servo style system, where all this can be handled much more naturally. + // + // We need to take special care not to disrupt the style inheritance of frames + // whose content is NAC but who implement a pseudo (like an anonymous + // box, or a non-NAC-backed pseudo like ::first-line) that does not match the + // one that the NAC implements, if any. + nsIContent* content = aFrame->GetContent(); + Element* element = + content && content->IsElement() ? content->AsElement() : nullptr; + if (element && element->IsNativeAnonymous() && !element->IsNativeScrollbarContent() && + element->GetPseudoElementType() == aFrame->StyleContext()->GetPseudoType()) { + while (parent->GetContent() && parent->GetContent()->IsNativeAnonymous()) { + parent = parent->GetInFlowParent(); + } + } + return nsFrame::CorrectStyleParentFrame(parent, pseudo); } @@ -9012,7 +9037,9 @@ nsStyleContext* nsFrame::DoGetParentStyleContext(nsIFrame** aProviderFrame) const { *aProviderFrame = nullptr; - nsFrameManager* fm = PresContext()->FrameManager(); + + // Handle display:contents and the root frame, when there's no parent frame + // to inherit from. if (MOZ_LIKELY(mContent)) { nsIContent* parentContent = mContent->GetFlattenedTreeParent(); if (MOZ_LIKELY(parentContent)) { @@ -9026,6 +9053,7 @@ nsFrame::DoGetParentStyleContext(nsIFrame** aProviderFrame) const /* if next is true then it's really a request for the table frame's parent context, see nsTable[Outer]Frame::GetParentStyleContext. */ pseudo == nsCSSAnonBoxes::tableWrapper) { + nsFrameManager* fm = PresContext()->FrameManager(); nsStyleContext* sc = fm->GetDisplayContentsStyleFor(parentContent); if (MOZ_UNLIKELY(sc)) { return sc; @@ -9062,7 +9090,7 @@ nsFrame::DoGetParentStyleContext(nsIFrame** aProviderFrame) const // We're an out-of-flow frame. For out-of-flow frames, we must // resolve underneath the placeholder's parent. The placeholder is // reached from the first-in-flow. - nsIFrame* placeholder = fm->GetPlaceholderFrameFor(FirstInFlow()); + nsIFrame* placeholder = FirstInFlow()->GetPlaceholderFrame(); if (!placeholder) { NS_NOTREACHED("no placeholder frame for out-of-flow frame"); *aProviderFrame = GetCorrectedParent(this); @@ -9977,19 +10005,16 @@ nsIFrame::IsPseudoStackingContextFromStyle() { Element* nsIFrame::GetPseudoElement(CSSPseudoElementType aType) { - nsIFrame* frame = nullptr; + if (!mContent) { + return nullptr; + } if (aType == CSSPseudoElementType::before) { - frame = nsLayoutUtils::GetBeforeFrame(this); - } else if (aType == CSSPseudoElementType::after) { - frame = nsLayoutUtils::GetAfterFrame(this); + return nsLayoutUtils::GetBeforePseudo(mContent); } - if (frame) { - nsIContent* content = frame->GetContent(); - if (content->IsElement()) { - return content->AsElement(); - } + if (aType == CSSPseudoElementType::after) { + return nsLayoutUtils::GetAfterPseudo(mContent); } return nullptr; diff --git a/layout/generic/nsHTMLParts.h b/layout/generic/nsHTMLParts.h index 89a7a6edde..243c432b2f 100644 --- a/layout/generic/nsHTMLParts.h +++ b/layout/generic/nsHTMLParts.h @@ -11,6 +11,7 @@ #include "nscore.h" #include "nsISupports.h" #include "nsIFrame.h" +class nsComboboxControlFrame; class nsIAtom; class nsNodeInfoManager; class nsIContent; @@ -161,7 +162,7 @@ nsIFrame* NS_NewNativeSelectControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); nsContainerFrame* NS_NewListControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); -nsContainerFrame* +nsComboboxControlFrame* NS_NewComboboxControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, nsFrameState aFlags); nsIFrame* NS_NewProgressFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); diff --git a/layout/generic/nsIAnonymousContentCreator.h b/layout/generic/nsIAnonymousContentCreator.h index e7d4399b6b..5167178b83 100644 --- a/layout/generic/nsIAnonymousContentCreator.h +++ b/layout/generic/nsIAnonymousContentCreator.h @@ -34,12 +34,7 @@ public: mContent(aContent) {} - ContentInfo(nsIContent* aContent, nsStyleContext* aStyleContext) : - mContent(aContent), mStyleContext(aStyleContext) - {} - nsIContent* mContent; - RefPtr<nsStyleContext> mStyleContext; nsTArray<ContentInfo> mChildren; }; @@ -66,18 +61,12 @@ public: * Appends "native" anonymous children created by CreateAnonymousContent() * to the given content list depending on the filter. * - * @see nsIContent::GetChildren for set of values used for filter. + * @see nsIContent::GetChildren for set of values used for filter. Currently, + * eSkipPlaceholderContent is the only flag that any implementation of + * this method heeds. */ virtual void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements, uint32_t aFilter) = 0; - - /** - * Implementations can override this method to create special frames for the - * anonymous content returned from CreateAnonymousContent. - * By default this method returns nullptr, which means the default frame - * is created. - */ - virtual nsIFrame* CreateFrameFor(nsIContent* aContent) { return nullptr; } }; #endif diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index 93eb95099a..e22dd690a8 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -82,6 +82,7 @@ class nsLineList_iterator; class nsAbsoluteContainingBlock; class nsIContent; class nsContainerFrame; +class nsPlaceholderFrame; struct nsPeekOffsetStruct; struct nsPoint; @@ -721,6 +722,23 @@ public: * Accessor functions for geometric parent. */ nsContainerFrame* GetParent() const { return mParent; } + + /** + * Gets the parent of a frame, using the parent of the placeholder for + * out-of-flow frames. + */ + inline nsContainerFrame* GetInFlowParent(); + + /** + * Return the placeholder for this frame (which must be out-of-flow). + * @note this will only return non-null if |this| is the first-in-flow + * although we don't assert that here for legacy reasons. + */ + inline nsPlaceholderFrame* GetPlaceholderFrame() const { + MOZ_ASSERT(HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)); + return GetProperty(PlaceholderFrameProperty()); + } + /** * Set this frame's parent to aParent. * If the frame may have moved into or out of a scrollframe's @@ -1040,6 +1058,8 @@ public: NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(BidiDataProperty, mozilla::FrameBidiData) + NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(PlaceholderFrameProperty, nsPlaceholderFrame) + mozilla::FrameBidiData GetBidiData() { return GetProperty(BidiDataProperty()); @@ -1055,10 +1075,6 @@ public: return GetBidiData().embeddingLevel; } - nsTArray<nsIContent*>* GetGenConPseudos() { - return GetProperty(GenConProperty()); - } - /** * Return the distance between the border edge of the frame and the * margin edge of the frame. Like GetRect(), returns the dimensions diff --git a/layout/generic/nsIFrameInlines.h b/layout/generic/nsIFrameInlines.h index eb9a7202a3..a3da7ebca9 100644 --- a/layout/generic/nsIFrameInlines.h +++ b/layout/generic/nsIFrameInlines.h @@ -8,8 +8,10 @@ #define nsIFrameInlines_h___ #include "nsContainerFrame.h" +#include "nsPlaceholderFrame.h" #include "nsStyleStructInlines.h" #include "nsCSSAnonBoxes.h" +#include "nsFrameManager.h" bool nsIFrame::IsFlexItem() const @@ -160,4 +162,15 @@ nsIFrame::BaselineBOffset(mozilla::WritingMode aWM, return SynthesizeBaselineBOffsetFromBorderBox(aWM, aBaselineGroup); } +nsContainerFrame* +nsIFrame::GetInFlowParent() +{ + if (GetStateBits() & NS_FRAME_OUT_OF_FLOW) { + nsIFrame* ph = FirstContinuation()->GetProperty(nsIFrame::PlaceholderFrameProperty()); + return ph->GetParent(); + } + + return GetParent(); +} + #endif diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp index c64520f2ea..ee35ecad81 100644 --- a/layout/generic/nsImageFrame.cpp +++ b/layout/generic/nsImageFrame.cpp @@ -1836,7 +1836,7 @@ nsImageFrame::ShouldDisplaySelection() int32_t thisOffset = parentContent->IndexOf(mContent); nsCOMPtr<nsIDOMNode> parentNode = do_QueryInterface(parentContent); nsCOMPtr<nsIDOMNode> rangeNode; - int32_t rangeOffset; + uint32_t rangeOffset; nsCOMPtr<nsIDOMRange> range; selection->GetRangeAt(0,getter_AddRefs(range)); if (range) @@ -1844,12 +1844,16 @@ nsImageFrame::ShouldDisplaySelection() range->GetStartContainer(getter_AddRefs(rangeNode)); range->GetStartOffset(&rangeOffset); - if (parentNode && rangeNode && (rangeNode == parentNode) && rangeOffset == thisOffset) - { + if (parentNode && rangeNode && rangeNode == parentNode && + static_cast<int32_t>(rangeOffset) == thisOffset) { range->GetEndContainer(getter_AddRefs(rangeNode)); range->GetEndOffset(&rangeOffset); - if ((rangeNode == parentNode) && (rangeOffset == (thisOffset +1))) //+1 since that would mean this whole content is selected only - return false; //do not allow nsFrame do draw any further selection + // +1 since that would mean this whole content is selected only + if (rangeNode == parentNode && + static_cast<int32_t>(rangeOffset) == thisOffset + 1) { + // Do not allow nsFrame do draw any further selection + return false; + } } } } diff --git a/layout/generic/nsPlaceholderFrame.cpp b/layout/generic/nsPlaceholderFrame.cpp index bd380a2d98..62370a06a4 100644 --- a/layout/generic/nsPlaceholderFrame.cpp +++ b/layout/generic/nsPlaceholderFrame.cpp @@ -156,16 +156,15 @@ nsPlaceholderFrame::DestroyFrom(nsIFrame* aDestructRoot) { nsIFrame* oof = mOutOfFlowFrame; if (oof) { - // Unregister out-of-flow frame - nsFrameManager* fm = PresContext()->GetPresShell()->FrameManager(); - fm->UnregisterPlaceholderFrame(this); mOutOfFlowFrame = nullptr; + oof->DeleteProperty(nsIFrame::PlaceholderFrameProperty()); // If aDestructRoot is not an ancestor of the out-of-flow frame, // then call RemoveFrame on it here. // Also destroy it here if it's a popup frame. (Bug 96291) if ((GetStateBits() & PLACEHOLDER_FOR_POPUP) || !nsLayoutUtils::IsProperAncestorFrame(aDestructRoot, oof)) { ChildListID listId = nsLayoutUtils::GetChildListNameFor(oof); + nsFrameManager* fm = PresContext()->GetPresShell()->FrameManager(); fm->RemoveFrame(listId, oof); } // else oof will be destroyed by its parent diff --git a/layout/generic/nsSelection.cpp b/layout/generic/nsSelection.cpp index 5ccb2d8bf8..919ab0815a 100644 --- a/layout/generic/nsSelection.cpp +++ b/layout/generic/nsSelection.cpp @@ -1788,8 +1788,7 @@ nsFrameSelection::TakeFocus(nsIContent* aNewFocus, RefPtr<nsRange> newRange = new nsRange(aNewFocus); - newRange->SetStart(aNewFocus, aContentOffset); - newRange->SetEnd(aNewFocus, aContentOffset); + newRange->CollapseTo(aNewFocus, aContentOffset); mDomSelections[index]->AddRange(newRange); mBatching = batching; mChangesDuringBatching = changes; @@ -3352,10 +3351,11 @@ nsFrameSelection::CreateAndAddRange(nsINode *aParentNode, int32_t aOffset) RefPtr<nsRange> range = new nsRange(aParentNode); // Set range around child at given offset - nsresult result = range->SetStart(aParentNode, aOffset); - if (NS_FAILED(result)) return result; - result = range->SetEnd(aParentNode, aOffset+1); - if (NS_FAILED(result)) return result; + nsresult rv = range->SetStartAndEnd(aParentNode, aOffset, + aParentNode, aOffset + 1); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } int8_t index = GetIndexFromSelectionType(SelectionType::eNormal); if (!mDomSelections[index]) @@ -3521,6 +3521,18 @@ Selection::GetParentObject() const return nullptr; } +DocGroup* +Selection::GetDocGroup() const +{ + nsIPresShell* shell = GetPresShell(); + if (!shell) { + return nullptr; + } + + nsIDocument* doc = shell->GetDocument(); + return doc ? doc->GetDocGroup() : nullptr; +} + NS_IMPL_CYCLE_COLLECTION_CLASS(Selection) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Selection) @@ -3780,13 +3792,12 @@ Selection::SubtractRange(RangeData* aRange, nsRange* aSubtract, // We need to add a new RangeData to the output, running from // the end of aSubtract to the end of range RefPtr<nsRange> postOverlap = new nsRange(aSubtract->GetEndParent()); - - rv = - postOverlap->SetStart(aSubtract->GetEndParent(), aSubtract->EndOffset()); - NS_ENSURE_SUCCESS(rv, rv); - rv = - postOverlap->SetEnd(range->GetEndParent(), range->EndOffset()); - NS_ENSURE_SUCCESS(rv, rv); + rv = postOverlap->SetStartAndEnd( + aSubtract->GetEndParent(), aSubtract->EndOffset(), + range->GetEndParent(), range->EndOffset()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } if (!postOverlap->Collapsed()) { if (!aOutput->InsertElementAt(0, RangeData(postOverlap))) return NS_ERROR_OUT_OF_MEMORY; @@ -3799,12 +3810,12 @@ Selection::SubtractRange(RangeData* aRange, nsRange* aSubtract, // the start of the range to the start of aSubtract RefPtr<nsRange> preOverlap = new nsRange(range->GetStartParent()); - nsresult rv = - preOverlap->SetStart(range->GetStartParent(), range->StartOffset()); - NS_ENSURE_SUCCESS(rv, rv); - rv = - preOverlap->SetEnd(aSubtract->GetStartParent(), aSubtract->StartOffset()); - NS_ENSURE_SUCCESS(rv, rv); + rv = preOverlap->SetStartAndEnd( + range->GetStartParent(), range->StartOffset(), + aSubtract->GetStartParent(), aSubtract->StartOffset()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } if (!preOverlap->Collapsed()) { if (!aOutput->InsertElementAt(0, RangeData(preOverlap))) @@ -4120,13 +4131,15 @@ Selection::GetType(int16_t* aType) static inline bool RangeMatchesBeginPoint(nsRange* aRange, nsINode* aNode, int32_t aOffset) { - return aRange->GetStartParent() == aNode && aRange->StartOffset() == aOffset; + return aRange->GetStartParent() == aNode && + static_cast<int32_t>(aRange->StartOffset()) == aOffset; } static inline bool RangeMatchesEndPoint(nsRange* aRange, nsINode* aNode, int32_t aOffset) { - return aRange->GetEndParent() == aNode && aRange->EndOffset() == aOffset; + return aRange->GetEndParent() == aNode && + static_cast<int32_t>(aRange->EndOffset()) == aOffset; } // Selection::EqualsRangeAtPoint @@ -5186,12 +5199,7 @@ Selection::Collapse(nsINode& aParentNode, uint32_t aOffset, ErrorResult& aRv) } RefPtr<nsRange> range = new nsRange(parentNode); - result = range->SetEnd(parentNode, aOffset); - if (NS_FAILED(result)) { - aRv.Throw(result); - return; - } - result = range->SetStart(parentNode, aOffset); + result = range->CollapseTo(parentNode, aOffset); if (NS_FAILED(result)) { aRv.Throw(result); return; @@ -5579,11 +5587,8 @@ Selection::Extend(nsINode& aParentNode, uint32_t aOffset, ErrorResult& aRv) return; } SetDirection(eDirNext); - res = difRange->SetEnd(range->GetEndParent(), range->EndOffset()); - nsresult tmp = difRange->SetStart(focusNode, focusOffset); - if (NS_FAILED(tmp)) { - res = tmp; - } + res = difRange->SetStartAndEnd(focusNode, focusOffset, + range->GetEndParent(), range->EndOffset()); if (NS_FAILED(res)) { aRv.Throw(res); return; @@ -5611,11 +5616,8 @@ Selection::Extend(nsINode& aParentNode, uint32_t aOffset, ErrorResult& aRv) } else if (result3 <= 0 && result2 >= 0) {//a,2,1 or a2,1 or a,21 or a21 //deselect from 2 to 1 - res = difRange->SetEnd(focusNode, focusOffset); - difRange->SetStart(aParentNode, aOffset, aRv); - if (aRv.Failed()) { - return; - } + res = difRange->SetStartAndEnd(&aParentNode, aOffset, + focusNode, focusOffset); if (NS_FAILED(res)) { aRv.Throw(res); return; @@ -5678,11 +5680,8 @@ Selection::Extend(nsINode& aParentNode, uint32_t aOffset, ErrorResult& aRv) } else if (result2 <= 0 && result3 >= 0) {//1,2,a or 12,a or 1,2a or 12a //deselect from 1 to 2 - difRange->SetEnd(aParentNode, aOffset, aRv); - res = difRange->SetStart(focusNode, focusOffset); - if (aRv.Failed()) { - return; - } + res = difRange->SetStartAndEnd(focusNode, focusOffset, + &aParentNode, aOffset); if (NS_FAILED(res)) { aRv.Throw(res); return; @@ -5713,15 +5712,9 @@ Selection::Extend(nsINode& aParentNode, uint32_t aOffset, ErrorResult& aRv) } //deselect from a to 1 if (focusNode != anchorNode || focusOffset!= anchorOffset) {//if collapsed diff dont do anything - res = difRange->SetStart(anchorNode, anchorOffset); - nsresult tmp = difRange->SetEnd(focusNode, focusOffset); - if (NS_FAILED(tmp)) { - res = tmp; - } - tmp = SetAnchorFocusToRange(range); - if (NS_FAILED(tmp)) { - res = tmp; - } + res = difRange->SetStartAndEnd(anchorNode, anchorOffset, + focusNode, focusOffset); + nsresult tmp = SetAnchorFocusToRange(range); if (NS_FAILED(res)) { aRv.Throw(res); return; @@ -5746,11 +5739,9 @@ Selection::Extend(nsINode& aParentNode, uint32_t aOffset, ErrorResult& aRv) return; } SetDirection(eDirPrevious); - res = difRange->SetEnd(focusNode, focusOffset); - nsresult tmp = difRange->SetStart(range->GetStartParent(), range->StartOffset()); - if (NS_FAILED(tmp)) { - res = tmp; - } + res = difRange->SetStartAndEnd( + range->GetStartParent(), range->StartOffset(), + focusNode, focusOffset); if (NS_FAILED(res)) { aRv.Throw(res); return; diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index 59ef020ce2..6875f33e88 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -1383,8 +1383,7 @@ BuildTextRuns(DrawTarget* aDrawTarget, nsTextFrame* aForFrame, nsIFrame* lineContainerChild = aForFrame; if (!aLineContainer) { if (aForFrame->IsFloatingFirstLetterChild()) { - lineContainerChild = aForFrame->PresContext()->PresShell()-> - GetPlaceholderFrameFor(aForFrame->GetParent()); + lineContainerChild = aForFrame->GetParent()->GetPlaceholderFrame(); } aLineContainer = FindLineContainer(lineContainerChild); } else { diff --git a/layout/inspector/inDOMUtils.cpp b/layout/inspector/inDOMUtils.cpp index e212e20df0..58afc5f760 100644 --- a/layout/inspector/inDOMUtils.cpp +++ b/layout/inspector/inDOMUtils.cpp @@ -48,7 +48,8 @@ #include "nsCSSProps.h" #include "nsCSSValue.h" #include "nsColor.h" -#include "nsStyleSet.h" +#include "mozilla/StyleSetHandle.h" +#include "mozilla/StyleSetHandleInlines.h" #include "nsStyleUtil.h" #include "nsQueryObject.h" @@ -77,7 +78,7 @@ inDOMUtils::GetAllStyleSheets(nsIDOMDocument *aDocument, uint32_t *aLength, { NS_ENSURE_ARG_POINTER(aDocument); - nsTArray<RefPtr<CSSStyleSheet>> sheets; + nsTArray<RefPtr<StyleSheet>> sheets; nsCOMPtr<nsIDocument> document = do_QueryInterface(aDocument); MOZ_ASSERT(document); @@ -85,15 +86,8 @@ inDOMUtils::GetAllStyleSheets(nsIDOMDocument *aDocument, uint32_t *aLength, // Get the agent, then user and finally xbl sheets in the style set. nsIPresShell* presShell = document->GetShell(); - if (presShell && presShell->StyleSet()->IsServo()) { - // XXXheycam ServoStyleSets don't have the ability to expose their - // sheets in a script-accessible way yet. - NS_ERROR("stylo: ServoStyleSets cannot expose their sheets to script yet"); - return NS_ERROR_FAILURE; - } - if (presShell) { - nsStyleSet* styleSet = presShell->StyleSet()->AsGecko(); + StyleSetHandle styleSet = presShell->StyleSet(); SheetType sheetType = SheetType::Agent; for (int32_t i = 0; i < styleSet->SheetCount(sheetType); i++) { sheets.AppendElement(styleSet->StyleSheetAt(sheetType, i)); @@ -102,24 +96,26 @@ inDOMUtils::GetAllStyleSheets(nsIDOMDocument *aDocument, uint32_t *aLength, for (int32_t i = 0; i < styleSet->SheetCount(sheetType); i++) { sheets.AppendElement(styleSet->StyleSheetAt(sheetType, i)); } - AutoTArray<CSSStyleSheet*, 32> xblSheetArray; - styleSet->AppendAllXBLStyleSheets(xblSheetArray); - - // The XBL stylesheet array will quite often be full of duplicates. Cope: - nsTHashtable<nsPtrHashKey<CSSStyleSheet>> sheetSet; - for (CSSStyleSheet* sheet : xblSheetArray) { - if (!sheetSet.Contains(sheet)) { - sheetSet.PutEntry(sheet); - sheets.AppendElement(sheet); + if (styleSet->IsGecko()) { + AutoTArray<CSSStyleSheet*, 32> xblSheetArray; + styleSet->AsGecko()->AppendAllXBLStyleSheets(xblSheetArray); + + // The XBL stylesheet array will quite often be full of duplicates. Cope: + nsTHashtable<nsPtrHashKey<CSSStyleSheet>> sheetSet; + for (CSSStyleSheet* sheet : xblSheetArray) { + if (!sheetSet.Contains(sheet)) { + sheetSet.PutEntry(sheet); + sheets.AppendElement(sheet); + } } + } else { + NS_WARNING("stylo: XBL style sheets not supported yet"); } } // Get the document sheets. - for (int32_t i = 0; i < document->GetNumberOfStyleSheets(); i++) { - // XXXheycam ServoStyleSets don't have the ability to expose their - // sheets in a script-accessible way yet. - sheets.AppendElement(document->GetStyleSheetAt(i)->AsGecko()); + for (size_t i = 0; i < document->SheetCount(); i++) { + sheets.AppendElement(document->SheetAt(i)); } nsISupports** ret = static_cast<nsISupports**>(moz_xmalloc(sheets.Length() * diff --git a/layout/inspector/tests/test_bug522601.xhtml b/layout/inspector/tests/test_bug522601.xhtml index 7c5a9e79c5..c49f2fa83c 100644 --- a/layout/inspector/tests/test_bug522601.xhtml +++ b/layout/inspector/tests/test_bug522601.xhtml @@ -237,6 +237,8 @@ addLoadEvent(function() { testFunc(walkerAnon, "previousNode", $("display"), "step back to root (anon)"); testFunc(walkerAnon, "previousNode", null, "step back past root (anon)"); + //XXXsmaug update this test for Shadow DOM v1! bug 1421539 + /*if (Element.prototype.createShadowRoot) { var shadowdiv = document.querySelector('#test-shadow'); var shadowRoot = shadowdiv.createShadowRoot(); var h = document.createElement("header"); @@ -266,6 +268,7 @@ addLoadEvent(function() { SimpleTest.finish(); }); + }*/ ]]> </script> diff --git a/layout/printing/nsPrintEngine.cpp b/layout/printing/nsPrintEngine.cpp index f2db53250e..0c455f563f 100644 --- a/layout/printing/nsPrintEngine.cpp +++ b/layout/printing/nsPrintEngine.cpp @@ -2449,13 +2449,17 @@ CloneRangeToSelection(nsRange* aRange, nsIDocument* aDoc, NS_ENSURE_TRUE_VOID(newStart && newEnd); nsCOMPtr<nsINode> newStartNode = do_QueryInterface(newStart); - NS_ENSURE_TRUE_VOID(newStartNode); + nsCOMPtr<nsINode> newEndNode = do_QueryInterface(newEnd); + if (NS_WARN_IF(!newStartNode) || NS_WARN_IF(!newEndNode)) { + return; + } RefPtr<nsRange> range = new nsRange(newStartNode); - nsresult rv = range->SetStart(newStartNode, startOffset); - NS_ENSURE_SUCCESS_VOID(rv); - rv = range->SetEnd(newEnd, endOffset); - NS_ENSURE_SUCCESS_VOID(rv); + nsresult rv = + range->SetStartAndEnd(newStartNode, startOffset, newEndNode, endOffset); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } aSelection->AddRange(range); } diff --git a/layout/reftests/bugs/1066554-1.html b/layout/reftests/bugs/1066554-1.html index bb0a97f964..f4df207a1b 100644 --- a/layout/reftests/bugs/1066554-1.html +++ b/layout/reftests/bugs/1066554-1.html @@ -7,15 +7,17 @@ <script> function insertShadowSVG() { var x = document.getElementById("x"); - x.createShadowRoot(); - x.shadowRoot.innerHTML = - '<svg width="50px" height="10px"> \ - <switch> \ - <foreignObject width="50px" height="50px"> \ - <div style="width: 100px; height: 10px; background: red;"></div> \ - </foreignObject> \ - </switch> \ - </svg>'; + if (x.createShadowRoot) { + x.createShadowRoot(); + x.shadowRoot.innerHTML = + '<svg width="50px" height="10px"> \ + <switch> \ + <foreignObject width="50px" height="50px"> \ + <div style="width: 100px; height: 10px; background: red;"></div> \ + </foreignObject> \ + </switch> \ + </svg>'; + } document.documentElement.removeAttribute("class"); } window.addEventListener("MozReftestInvalidate", insertShadowSVG, false); diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index d6f58a9c23..84d7f188f8 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1851,7 +1851,7 @@ test-pref(layout.css.grid.enabled,true) == 1053035-1-grid.html 1053035-1-ref.htm == 1062108-1.html 1062108-1-ref.html == 1062792-1.html 1062792-1-ref.html == 1062963-floatmanager-reflow.html 1062963-floatmanager-reflow-ref.html -test-pref(dom.webcomponents.enabled,true) == 1066554-1.html 1066554-1-ref.html +test-pref(dom.webcomponents.enabled,true) fails-if(stylo||styloVsGecko) == 1066554-1.html 1066554-1-ref.html == 1069716-1.html 1069716-1-ref.html == 1078262-1.html about:blank test-pref(layout.testing.overlay-scrollbars.always-visible,false) == 1081072-1.html 1081072-1-ref.html diff --git a/layout/reftests/css-display/display-contents-shadow-dom-1-ref.html b/layout/reftests/css-display/display-contents-shadow-dom-1-ref.html index f57822901d..6e6dad2331 100644 --- a/layout/reftests/css-display/display-contents-shadow-dom-1-ref.html +++ b/layout/reftests/css-display/display-contents-shadow-dom-1-ref.html @@ -45,8 +45,6 @@ span { color:blue; } <span style="color:green">R</span> <div></div> <b style="color:green">V</b> - <b style="color:green">W</b> - <b style="color:green">X</b> <!-- <b style="color:green">Y</b> --> </body> </html> diff --git a/layout/reftests/css-display/display-contents-shadow-dom-1.html b/layout/reftests/css-display/display-contents-shadow-dom-1.html index f5e49a192a..6c0f297f9d 100644 --- a/layout/reftests/css-display/display-contents-shadow-dom-1.html +++ b/layout/reftests/css-display/display-contents-shadow-dom-1.html @@ -51,8 +51,6 @@ div.after::after {content: " Y";} <div id="hostT" class="c">T</div> <div id="hostU"><span class="c">U</span></div> <div id="hostV" class="c" style="color:red"><b class="c" style="color:inherit">V</b></div> - <div id="hostW" class="c" style="color:red"><b class="c" style="color:inherit">W</b></div> - <span id="hostX" style="color:red"><b class="c" style="color:inherit">X</b></span> <!-- TODO(bug 1021572?) <div id="hostY" class="c" style="color:red"><b>Y</b></div> --> <script> @@ -76,38 +74,40 @@ div.after::after {content: " Y";} return e; } - document.body.offsetHeight; - - shadow("host1").innerHTML = '<content></content> c'; - shadow("host2").innerHTML = 'a <content style="display:contents"></content> c'; - shadow("host3").innerHTML = 'a <content style="display:contents"></content>'; - shadow("host4").innerHTML = '<content style="display:contents"></content>'; - shadow("host5").innerHTML = 'a <content style="display:contents"></content>'; - shadow("host6").innerHTML = '<z style="color:blue; display:contents"><content></content></z> c'; - shadow("host7").innerHTML = 'a <content style="display:contents"></content> c'; - shadow("host8").innerHTML = 'a <z style="color:blue; display:contents"><content style="display:contents"></z></content>'; - shadow("host9").innerHTML = '<content style="display:contents"></content>'; - shadow("hostA").innerHTML = 'a <content style="display:contents"></content>'; - shadow("hostB").innerHTML = 'a <content select=".c"></content> <content select=".b"></content> B'; - shadow("hostC").innerHTML = 'A <content select=".c"></content> <content select=".b"></content> B'; - shadow("hostD").innerHTML = 'A <content select=".c"></content> <content select=".b"></content> B <content select=".b"></content>'; - shadow("hostE").innerHTML = 'A <content select=".c"></content> <content select=".b"></content> B'; - shadow("hostF").innerHTML = '<content select=".c"></content> <content select=".b"></content> B'; - shadow("hostG").innerHTML = '<content select=".b"></content>'; - shadow("hostH").innerHTML = '<content select=".b"></content>'; - shadow("hostI").innerHTML = 'A<content select=".b"></content>'; - shadow("hostJ").innerHTML = 'A<content select=".b"></content>'; - shadow("hostK").innerHTML = '<content select=".b"></content>'; - shadow("hostL").innerHTML = '<content select=".b"></content>'; - shadow("hostM").innerHTML = '<content select="b"></content><content select="i"></content>'; - shadow("hostN").innerHTML = '<content select="b"></content><content select="i"></content>'; - shadow("hostO").innerHTML = '<content select="b"></content><content select="i"></content>'; - shadow("hostP").innerHTML = '<content select="b"></content><content select="i"></content>'; - shadow("hostQ").innerHTML = '<content select="b"></content><content select="i"></content>'; - shadow("hostR").innerHTML = '<content select="span"></content>'; - shadow("hostW").innerHTML = '<z style="color:red"><content select="b"></content></z>'; - shadow("hostX").innerHTML = '<z style="color:red"><content select="b"></content></z>'; - // TODO(bug 1021572?) shadow("hostY").innerHTML = '<content select="b"><style scoped>:scope{color:green}</style></content>'; + function run() { + document.body.offsetHeight; + + shadow("host1").innerHTML = '<content></content> c'; + shadow("host2").innerHTML = 'a <content style="display:contents"></content> c'; + shadow("host3").innerHTML = 'a <content style="display:contents"></content>'; + shadow("host4").innerHTML = '<content style="display:contents"></content>'; + shadow("host5").innerHTML = 'a <content style="display:contents"></content>'; + shadow("host6").innerHTML = '<z style="color:blue; display:contents"><content></content></z> c'; + shadow("host7").innerHTML = 'a <content style="display:contents"></content> c'; + shadow("host8").innerHTML = 'a <z style="color:blue; display:contents"><content style="display:contents"></z></content>'; + shadow("host9").innerHTML = '<content style="display:contents"></content>'; + shadow("hostA").innerHTML = 'a <content style="display:contents"></content>'; + shadow("hostB").innerHTML = 'a <content select=".c"></content> <content select=".b"></content> B'; + shadow("hostC").innerHTML = 'A <content select=".c"></content> <content select=".b"></content> B'; + shadow("hostD").innerHTML = 'A <content select=".c"></content> <content select=".b"></content> B <content select=".b"></content>'; + shadow("hostE").innerHTML = 'A <content select=".c"></content> <content select=".b"></content> B'; + shadow("hostF").innerHTML = '<content select=".c"></content> <content select=".b"></content> B'; + shadow("hostG").innerHTML = '<content select=".b"></content>'; + shadow("hostH").innerHTML = '<content select=".b"></content>'; + shadow("hostI").innerHTML = 'A<content select=".b"></content>'; + shadow("hostJ").innerHTML = 'A<content select=".b"></content>'; + shadow("hostK").innerHTML = '<content select=".b"></content>'; + shadow("hostL").innerHTML = '<content select=".b"></content>'; + shadow("hostM").innerHTML = '<content select="b"></content><content select="i"></content>'; + shadow("hostN").innerHTML = '<content select="b"></content><content select="i"></content>'; + shadow("hostO").innerHTML = '<content select="b"></content><content select="i"></content>'; + shadow("hostP").innerHTML = '<content select="b"></content><content select="i"></content>'; + shadow("hostQ").innerHTML = '<content select="b"></content><content select="i"></content>'; + shadow("hostR").innerHTML = '<content select="span"></content>'; + shadow("hostW").innerHTML = '<z style="color:red"><content select="b"></content></z>'; + shadow("hostX").innerHTML = '<z style="color:red"><content select="b"></content></z>'; + // TODO(bug 1021572?) shadow("hostY").innerHTML = '<content select="b"><style scoped>:scope{color:green}</style></content>'; + } function tweak() { document.body.offsetHeight; @@ -222,15 +222,18 @@ div.after::after {content: " Y";} shadow("hostT"); shadow("hostU"); shadow("hostV").innerHTML = '<z style="color:green"><content select="b"></content></z>'; - shadow("hostW").innerHTML = '<z style="color:green"><content select="b"></content></z>'; - shadow("hostX").innerHTML = '<z style="color:green"><content select="b"></content></z>'; document.body.offsetHeight; document.documentElement.removeAttribute("class"); },0); } - window.addEventListener("MozReftestInvalidate", tweak, false); + if (document.body.createShadowRoot) { + run(); + window.addEventListener("MozReftestInvalidate", tweak, false); + } else { + document.documentElement.removeAttribute("class"); + } </script> </body> </html> diff --git a/layout/reftests/css-grid/reftest.list b/layout/reftests/css-grid/reftest.list index 7c5e6be514..35e3140a85 100644 --- a/layout/reftests/css-grid/reftest.list +++ b/layout/reftests/css-grid/reftest.list @@ -248,10 +248,10 @@ asserts(0-10) == grid-fragmentation-015.html grid-fragmentation-015-ref.html # b == grid-fragmentation-dyn5-019.html grid-fragmentation-019-ref.html == grid-fragmentation-dyn1-020.html grid-fragmentation-020-ref.html == grid-fragmentation-dyn2-020.html grid-fragmentation-020-ref.html -!= grid-fragmentation-dyn1-021.html grid-fragmentation-021-ref.html # bug 1251799 +== grid-fragmentation-dyn1-021.html grid-fragmentation-021-ref.html == grid-fragmentation-dyn2-021.html grid-fragmentation-021-ref.html == grid-fragmentation-dyn3-021.html grid-fragmentation-021-ref.html -asserts(1-10) == grid-fragmentation-dyn4-021.html grid-fragmentation-021-ref.html # assertion related to bug 1251799 ? +== grid-fragmentation-dyn4-021.html grid-fragmentation-021-ref.html == grid-fragmentation-dyn5-021.html grid-fragmentation-021-ref.html == grid-fragmentation-dyn2-022.html grid-fragmentation-007-ref.html == grid-fragmentation-dyn1-023.html grid-fragmentation-023-ref.html diff --git a/layout/reftests/details-summary/reftest.list b/layout/reftests/details-summary/reftest.list index e96581ad45..a972cf4980 100644 --- a/layout/reftests/details-summary/reftest.list +++ b/layout/reftests/details-summary/reftest.list @@ -101,3 +101,7 @@ fuzzy(1,1) == mouse-click-twice-float-details.html float-details.html # Bug 1316 == details-before.html single-summary.html == open-details-after.html open-single-summary.html == open-details-before.html open-single-summary.html + +# Move summary element +== move-float-summary-to-different-details.html move-float-summary-to-different-details-ref.html +== move-position-absolute-summary-to-different-details.html move-position-absolute-summary-to-different-details-ref.html diff --git a/layout/reftests/forms/input/number/number-style-inheritance-ref.html b/layout/reftests/forms/input/number/number-style-inheritance-ref.html new file mode 100644 index 0000000000..00c8dba43a --- /dev/null +++ b/layout/reftests/forms/input/number/number-style-inheritance-ref.html @@ -0,0 +1,6 @@ +<!DOCTYPE html> +<html> + <body> + <input type="text" style="width: 100px; text-decoration: underline;" value="1234"> + </body> +</html> diff --git a/layout/reftests/forms/input/number/number-style-inheritance.html b/layout/reftests/forms/input/number/number-style-inheritance.html new file mode 100644 index 0000000000..b21ff1f1c8 --- /dev/null +++ b/layout/reftests/forms/input/number/number-style-inheritance.html @@ -0,0 +1,6 @@ +<!DOCTYPE html> +<html> + <body> + <input type="number" style="width: 100px; -moz-appearance: textfield; text-decoration: underline;" value="1234"> + </body> +</html> diff --git a/layout/reftests/forms/input/number/reftest.list b/layout/reftests/forms/input/number/reftest.list index ecf05ce155..bd07e274ce 100644 --- a/layout/reftests/forms/input/number/reftest.list +++ b/layout/reftests/forms/input/number/reftest.list @@ -52,3 +52,6 @@ fuzzy-if(skiaContent,2,5) needs-focus == focus-handling.html focus-handling-ref. fuzzy(128,4) == number-reframe-anon-text-field.html number-reframe-anon-text-field-ref.html == pseudo-classes.html about:blank + +# Style inheritance: +== number-style-inheritance.html number-style-inheritance-ref.html diff --git a/layout/reftests/forms/legend/reftest.list b/layout/reftests/forms/legend/reftest.list index 879835a59b..03e25eb201 100644 --- a/layout/reftests/forms/legend/reftest.list +++ b/layout/reftests/forms/legend/reftest.list @@ -1,3 +1,3 @@ == legend.html legend-ref.html -fuzzy-if(skiaContent,1,7) pref(dom.webcomponents.enabled,true) == shadow-dom.html shadow-dom-ref.html +#bug 1418002 fuzzy-if(skiaContent,1,7) pref(dom.webcomponents.enabled,true) == shadow-dom.html shadow-dom-ref.html == 1273433.html 1273433-ref.html diff --git a/layout/reftests/forms/legend/shadow-dom.html b/layout/reftests/forms/legend/shadow-dom.html index ad7babcf7c..0f0a536655 100644 --- a/layout/reftests/forms/legend/shadow-dom.html +++ b/layout/reftests/forms/legend/shadow-dom.html @@ -48,17 +48,19 @@ div.after::after {content: " Y";} return e; } - document.body.offsetHeight; + function run() { + document.body.offsetHeight; - shadow("host1").innerHTML = '<content></content> c'; - shadow("host2").innerHTML = 'a <content></content> c'; - shadow("host3").innerHTML = 'a <content></content>'; - shadow("host4").innerHTML = '<content></content>'; - shadow("host5").innerHTML = 'a <content></content>'; - shadow("host6").innerHTML = '<z style="color:blue; display:contents"><content></content></z> c'; - shadow("host7").innerHTML = 'a <content select=".c"></content> <content select=".b"></content> B'; - shadow("host8").innerHTML = 'A <content select=".c"></content> <content select=".b"></content> B'; - shadow("host9").innerHTML = 'A <content select=".c"></content> <content select=".b"></content> B <content select=".b"></content>'; + shadow("host1").innerHTML = '<content></content> c'; + shadow("host2").innerHTML = 'a <content></content> c'; + shadow("host3").innerHTML = 'a <content></content>'; + shadow("host4").innerHTML = '<content></content>'; + shadow("host5").innerHTML = 'a <content></content>'; + shadow("host6").innerHTML = '<z style="color:blue; display:contents"><content></content></z> c'; + shadow("host7").innerHTML = 'a <content select=".c"></content> <content select=".b"></content> B'; + shadow("host8").innerHTML = 'A <content select=".c"></content> <content select=".b"></content> B'; + shadow("host9").innerHTML = 'A <content select=".c"></content> <content select=".b"></content> B <content select=".b"></content>'; + } function tweak() { document.body.offsetHeight; @@ -105,7 +107,12 @@ div.after::after {content: " Y";} },0); } - window.addEventListener("MozReftestInvalidate", tweak, false); + if (document.body.createShadowRoot) { + run(); + window.addEventListener("MozReftestInvalidate", tweak, false); + } else { + document.documentElement.removeAttribute("class"); + } </script> </body> </html> diff --git a/layout/reftests/mathml/shadow-dom-1.html b/layout/reftests/mathml/shadow-dom-1.html index 547253a3c0..bbf27069fd 100644 --- a/layout/reftests/mathml/shadow-dom-1.html +++ b/layout/reftests/mathml/shadow-dom-1.html @@ -7,9 +7,11 @@ <script> function insertShadowMathML() { var x = document.getElementById("x"); - x.createShadowRoot(); - x.shadowRoot.innerHTML = - '<math><msup><mi>X</mi><mi>X</mi></msup></math>'; + if (x.createShadowRoot) { + x.createShadowRoot(); + x.shadowRoot.innerHTML = + '<math><msup><mi>X</mi><mi>X</mi></msup></math>'; + } document.documentElement.removeAttribute("class"); } window.addEventListener("MozReftestInvalidate", insertShadowMathML, false); diff --git a/layout/reftests/webcomponents/adjacent-insertion-points-1-ref.html b/layout/reftests/webcomponents/adjacent-insertion-points-1-ref.html deleted file mode 100644 index 2c1f4e3419..0000000000 --- a/layout/reftests/webcomponents/adjacent-insertion-points-1-ref.html +++ /dev/null @@ -1,6 +0,0 @@ -<!DOCTYPE HTML> -<html> -<body> -<div><span>Hello</span><span>World</span></div> -</body> -</html> diff --git a/layout/reftests/webcomponents/adjacent-insertion-points-1.html b/layout/reftests/webcomponents/adjacent-insertion-points-1.html deleted file mode 100644 index a8c6f983dc..0000000000 --- a/layout/reftests/webcomponents/adjacent-insertion-points-1.html +++ /dev/null @@ -1,17 +0,0 @@ -<!DOCTYPE HTML> -<html> -<head> - <script> - function tweak() { - var oldShadowRoot = document.getElementById('outer').createShadowRoot(); - oldShadowRoot.innerHTML = 'World'; - - var youngShadowRoot = document.getElementById('outer').createShadowRoot(); - youngShadowRoot.innerHTML = 'Hello<content></content><shadow></shadow>'; - } - </script> -</head> -<body onload="tweak()"> -<div id="outer"></div> -</body> -</html> diff --git a/layout/reftests/webcomponents/adjacent-insertion-points-2-ref.html b/layout/reftests/webcomponents/adjacent-insertion-points-2-ref.html deleted file mode 100644 index 2c1f4e3419..0000000000 --- a/layout/reftests/webcomponents/adjacent-insertion-points-2-ref.html +++ /dev/null @@ -1,6 +0,0 @@ -<!DOCTYPE HTML> -<html> -<body> -<div><span>Hello</span><span>World</span></div> -</body> -</html> diff --git a/layout/reftests/webcomponents/adjacent-insertion-points-2.html b/layout/reftests/webcomponents/adjacent-insertion-points-2.html deleted file mode 100644 index f90cb206bb..0000000000 --- a/layout/reftests/webcomponents/adjacent-insertion-points-2.html +++ /dev/null @@ -1,17 +0,0 @@ -<!DOCTYPE HTML> -<html> -<head> - <script> - function tweak() { - var oldShadowRoot = document.getElementById('outer').createShadowRoot(); - oldShadowRoot.innerHTML = 'Hello'; - - var youngShadowRoot = document.getElementById('outer').createShadowRoot(); - youngShadowRoot.innerHTML = '<shadow></shadow><content></content>World'; - } - </script> -</head> -<body onload="tweak()"> -<div id="outer"></div> -</body> -</html> diff --git a/layout/reftests/webcomponents/basic-insertion-point-1-ref.html b/layout/reftests/webcomponents/basic-insertion-point-1-ref.html deleted file mode 100644 index 16f6afb288..0000000000 --- a/layout/reftests/webcomponents/basic-insertion-point-1-ref.html +++ /dev/null @@ -1,8 +0,0 @@ -<!DOCTYPE HTML> -<html> -<body> -<div> - <div style="border: 10px solid green">Hello</div> -</div> -</body> -</html> diff --git a/layout/reftests/webcomponents/basic-insertion-point-1.html b/layout/reftests/webcomponents/basic-insertion-point-1.html deleted file mode 100644 index 727175ef35..0000000000 --- a/layout/reftests/webcomponents/basic-insertion-point-1.html +++ /dev/null @@ -1,21 +0,0 @@ -<!DOCTYPE HTML> -<html> -<head> - <script> - function tweak() { - // div with style "border: 10px solid green" - var shadowDiv = document.createElement("div"); - shadowDiv.style.border = "10px solid green"; - - var insertionPoint = document.createElement("content"); - shadowDiv.appendChild(insertionPoint); - - var shadowRoot = document.getElementById('outer').createShadowRoot(); - shadowRoot.appendChild(shadowDiv); - } - </script> -</head> -<body onload="tweak()"> -<div id="outer">Hello</div> -</body> -</html> diff --git a/layout/reftests/webcomponents/basic-insertion-point-2-ref.html b/layout/reftests/webcomponents/basic-insertion-point-2-ref.html deleted file mode 100644 index 5e92137752..0000000000 --- a/layout/reftests/webcomponents/basic-insertion-point-2-ref.html +++ /dev/null @@ -1,11 +0,0 @@ -<!DOCTYPE HTML> -<html> -<body> -<div> - <div style="border: 10px solid green"> - <span style="background-color: purple">Hello</span> - <span style="background-color: orange">World</span> - </div> -</div> -</body> -</html> diff --git a/layout/reftests/webcomponents/basic-insertion-point-2.html b/layout/reftests/webcomponents/basic-insertion-point-2.html deleted file mode 100644 index 595edb4712..0000000000 --- a/layout/reftests/webcomponents/basic-insertion-point-2.html +++ /dev/null @@ -1,24 +0,0 @@ -<!DOCTYPE HTML> -<html> -<head> - <script> - function tweak() { - // div with style "border: 10px solid green" - var shadowDiv = document.createElement("div"); - shadowDiv.style.border = "10px solid green"; - - var insertionPoint = document.createElement("content"); - shadowDiv.appendChild(insertionPoint); - - var shadowRoot = document.getElementById('outer').createShadowRoot(); - shadowRoot.appendChild(shadowDiv); - } - </script> -</head> -<body onload="tweak()"> -<div id="outer"> - <span style="background-color: purple">Hello</span> - <span style="background-color: orange">World</span> -</div> -</body> -</html> diff --git a/layout/reftests/webcomponents/basic-shadow-1.html b/layout/reftests/webcomponents/basic-shadow-1.html index 944a594d27..8949dfc6de 100644 --- a/layout/reftests/webcomponents/basic-shadow-1.html +++ b/layout/reftests/webcomponents/basic-shadow-1.html @@ -9,7 +9,7 @@ shadowDiv.style.height = "100px"; shadowDiv.style.backgroundColor = "green"; - var shadowRoot = document.getElementById('outer').createShadowRoot(); + var shadowRoot = document.getElementById('outer').attachShadow({mode: 'open'}); shadowRoot.appendChild(shadowDiv); } </script> diff --git a/layout/reftests/webcomponents/basic-shadow-2.html b/layout/reftests/webcomponents/basic-shadow-2.html index 587c47221a..8e066997dc 100644 --- a/layout/reftests/webcomponents/basic-shadow-2.html +++ b/layout/reftests/webcomponents/basic-shadow-2.html @@ -6,7 +6,7 @@ var shadowDiv = document.createElement("div"); shadowDiv.style.border = "10px solid green"; - var shadowRoot = document.getElementById('outer').createShadowRoot(); + var shadowRoot = document.getElementById('outer').attachShadow({mode: 'open'}); shadowRoot.appendChild(shadowDiv); var orangeDiv = document.createElement("div"); diff --git a/layout/reftests/webcomponents/basic-shadow-3.html b/layout/reftests/webcomponents/basic-shadow-3.html index 2ae53b5a9e..3226c4baa6 100644 --- a/layout/reftests/webcomponents/basic-shadow-3.html +++ b/layout/reftests/webcomponents/basic-shadow-3.html @@ -6,7 +6,7 @@ var shadowDiv = document.createElement("div"); shadowDiv.style.border = "10px solid green"; - var shadowRoot = document.getElementById('outer').createShadowRoot(); + var shadowRoot = document.getElementById('outer').attachShadow({mode: 'open'}); shadowRoot.appendChild(shadowDiv); var orangeDiv = document.createElement("div"); diff --git a/layout/reftests/webcomponents/basic-shadow-4.html b/layout/reftests/webcomponents/basic-shadow-4.html index 70f91773e4..39dc51a9e5 100644 --- a/layout/reftests/webcomponents/basic-shadow-4.html +++ b/layout/reftests/webcomponents/basic-shadow-4.html @@ -6,7 +6,7 @@ var shadowDiv = document.createElement("div"); shadowDiv.style.border = "10px solid green"; - var shadowRoot = document.getElementById('outer').createShadowRoot(); + var shadowRoot = document.getElementById('outer').attachShadow({mode: 'open'}); shadowRoot.appendChild(shadowDiv); var orangeDiv = document.createElement("div"); diff --git a/layout/reftests/webcomponents/basic-shadow-element-1-ref.html b/layout/reftests/webcomponents/basic-shadow-element-1-ref.html deleted file mode 100644 index a47b9362a3..0000000000 --- a/layout/reftests/webcomponents/basic-shadow-element-1-ref.html +++ /dev/null @@ -1,10 +0,0 @@ -<!DOCTYPE HTML> -<html> -<head> -</head> -<body> -<div> - <div style="width:100px; height:100px; background-color:green"></div><div style="width:100px; height:100px; background-color:orange">Hello World</div> -</div> -</body> -</html> diff --git a/layout/reftests/webcomponents/basic-shadow-element-1.html b/layout/reftests/webcomponents/basic-shadow-element-1.html deleted file mode 100644 index e51bd8b722..0000000000 --- a/layout/reftests/webcomponents/basic-shadow-element-1.html +++ /dev/null @@ -1,19 +0,0 @@ -<!DOCTYPE HTML> -<html> -<head> - <script> - function tweak() { - var olderShadow = document.getElementById('outer').createShadowRoot(); - olderShadow.innerHTML = '<div style="width:100px; height:100px; background-color: orange"><content></content></div>'; - - var youngerShadow = document.getElementById('outer').createShadowRoot(); - youngerShadow.innerHTML = '<div style="width:100px; height:100px; background-color: green"></div><shadow>Hello World</shadow>'; - } - </script> -</head> -<body onload="tweak()"> -<div id="outer"> - <div style="width:300px; height:100px; background-color:red;"></div> -</div> -</body> -</html> diff --git a/layout/reftests/webcomponents/basic-slot-1-ref.html b/layout/reftests/webcomponents/basic-slot-1-ref.html new file mode 100644 index 0000000000..4f74184983 --- /dev/null +++ b/layout/reftests/webcomponents/basic-slot-1-ref.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html> + <head> + <style> + body { color: green; } + </style> + </head> + <body> + This text should be green + </body> +</html> diff --git a/layout/reftests/webcomponents/basic-slot-1.html b/layout/reftests/webcomponents/basic-slot-1.html new file mode 100644 index 0000000000..b31f4c1c36 --- /dev/null +++ b/layout/reftests/webcomponents/basic-slot-1.html @@ -0,0 +1,6 @@ +<!DOCTYPE HTML> +<html> + <body> + <slot style="color: green">This text should be green</slot> + </body> +</html> diff --git a/layout/reftests/webcomponents/basic-slot-2-ref.html b/layout/reftests/webcomponents/basic-slot-2-ref.html new file mode 100644 index 0000000000..d4d1b8c066 --- /dev/null +++ b/layout/reftests/webcomponents/basic-slot-2-ref.html @@ -0,0 +1,16 @@ +<!DOCTYPE HTML> +<html> +<head> + <style> + div { + width: 100px; + height: 100px; + background: green; + } + </style> +</head> +<body> + <p>There should be a green box below.</p> + <div></div> +</body> +</html> diff --git a/layout/reftests/webcomponents/basic-slot-2.html b/layout/reftests/webcomponents/basic-slot-2.html new file mode 100644 index 0000000000..3754ace20c --- /dev/null +++ b/layout/reftests/webcomponents/basic-slot-2.html @@ -0,0 +1,7 @@ +<!DOCTYPE HTML> +<html> + <body> + <p>There should be a green box below.</p> + <slot style="display: block; width: 100px; height: 100px; background: green;"></slot> + </body> +</html> diff --git a/layout/reftests/webcomponents/basic-slot-3-ref.html b/layout/reftests/webcomponents/basic-slot-3-ref.html new file mode 100644 index 0000000000..54be54848d --- /dev/null +++ b/layout/reftests/webcomponents/basic-slot-3-ref.html @@ -0,0 +1,8 @@ +<!DOCTYPE HTML> +<html> +<body> +<div> + <div style="color: green">This text should be green</div> +</div> +</body> +</html> diff --git a/layout/reftests/webcomponents/basic-slot-3.html b/layout/reftests/webcomponents/basic-slot-3.html new file mode 100644 index 0000000000..c00483fe2f --- /dev/null +++ b/layout/reftests/webcomponents/basic-slot-3.html @@ -0,0 +1,18 @@ +<!DOCTYPE HTML> +<html> +<head> + <script> + function tweak() { + var slot = document.createElement("slot"); + slot.style.color = "green"; + + var shadowRoot = + document.getElementById('outer').attachShadow({mode: 'open'}); + shadowRoot.appendChild(slot); + } + </script> +</head> +<body onload="tweak()"> +<div id="outer">This text should be green</div> +</body> +</html> diff --git a/layout/reftests/webcomponents/basic-slot-4.html b/layout/reftests/webcomponents/basic-slot-4.html new file mode 100644 index 0000000000..496a926514 --- /dev/null +++ b/layout/reftests/webcomponents/basic-slot-4.html @@ -0,0 +1,20 @@ +<!DOCTYPE HTML> +<html> +<head> + <script> + function tweak() { + var slot = document.createElement("slot"); + // The border shouldn't be visible, due to display: contents. + slot.style.border = "1px solid red"; + slot.style.color = "green"; + + var shadowRoot = + document.getElementById('outer').attachShadow({mode: 'open'}); + shadowRoot.appendChild(slot); + } + </script> +</head> +<body onload="tweak()"> +<div id="outer">This text should be green</div> +</body> +</html> diff --git a/layout/reftests/webcomponents/cross-tree-selection-1.html b/layout/reftests/webcomponents/cross-tree-selection-1.html index 1e4f027479..01e7317f23 100644 --- a/layout/reftests/webcomponents/cross-tree-selection-1.html +++ b/layout/reftests/webcomponents/cross-tree-selection-1.html @@ -3,6 +3,11 @@ <head> <script> function tweak() { + if (!document.body.createShadowRoot) { + document.documentElement.className = ""; + return; + } + var host = document.getElementById("host"); var shadow = host.createShadowRoot(); diff --git a/layout/reftests/webcomponents/dynamic-insertion-point-distribution-1.html b/layout/reftests/webcomponents/dynamic-insertion-point-distribution-1.html index c57f72b379..8919a9c6a0 100644 --- a/layout/reftests/webcomponents/dynamic-insertion-point-distribution-1.html +++ b/layout/reftests/webcomponents/dynamic-insertion-point-distribution-1.html @@ -5,9 +5,13 @@ <body> <div id="host"></div> <script> - var host = document.getElementById("host"); - var root = host.createShadowRoot(); - root.innerHTML = 'a <content></content> c'; + var host, root; + + function run() { + host = document.getElementById("host"); + root = host.createShadowRoot(); + root.innerHTML = 'a <content></content> c'; + } function tweak() { var span = document.createElement("span"); @@ -19,7 +23,12 @@ document.documentElement.removeAttribute("class"); } - window.addEventListener("MozReftestInvalidate", tweak, false); + if (document.body.createShadowRoot) { + run(); + window.addEventListener("MozReftestInvalidate", tweak, false); + } else { + document.documentElement.className = ""; + } </script> </body> </html> diff --git a/layout/reftests/webcomponents/dynamic-insertion-point-distribution-2.html b/layout/reftests/webcomponents/dynamic-insertion-point-distribution-2.html index 29a850e90f..c58b9cfbd7 100644 --- a/layout/reftests/webcomponents/dynamic-insertion-point-distribution-2.html +++ b/layout/reftests/webcomponents/dynamic-insertion-point-distribution-2.html @@ -5,9 +5,13 @@ <body> <div id="host"></div> <script> - var host = document.getElementById("host"); - var root = host.createShadowRoot(); - root.innerHTML = "<span>a</span>"; + var host, root; + + function run() { + host = document.getElementById("host"); + root = host.createShadowRoot(); + root.innerHTML = "<span>a</span>"; + } function tweak() { var span = document.createElement("span"); @@ -20,7 +24,12 @@ document.documentElement.removeAttribute("class"); } - window.addEventListener("MozReftestInvalidate", tweak, false); + if (document.body.createShadowRoot) { + run(); + window.addEventListener("MozReftestInvalidate", tweak); + } else { + document.documentElement.className = ""; + } </script> </body> </html> diff --git a/layout/reftests/webcomponents/dynamic-shadow-element-1-ref.html b/layout/reftests/webcomponents/dynamic-shadow-element-1-ref.html deleted file mode 100644 index a3b5150f65..0000000000 --- a/layout/reftests/webcomponents/dynamic-shadow-element-1-ref.html +++ /dev/null @@ -1,10 +0,0 @@ -<!DOCTYPE HTML> -<html> -<head> -</head> -<body> -<div> - <div style="background-color: green"><span>Hello</span><span> </span><span>World</span></div> -</div> -</body> -</html> diff --git a/layout/reftests/webcomponents/dynamic-shadow-element-1.html b/layout/reftests/webcomponents/dynamic-shadow-element-1.html deleted file mode 100644 index 0142164a1a..0000000000 --- a/layout/reftests/webcomponents/dynamic-shadow-element-1.html +++ /dev/null @@ -1,23 +0,0 @@ -<!DOCTYPE HTML> -<html> -<head> - <script> - function tweak() { - var oldestShadow = document.getElementById('outer').createShadowRoot(); - oldestShadow.innerHTML = 'Hello'; - - var olderShadow = document.getElementById('outer').createShadowRoot(); - - var youngerShadow = document.getElementById('outer').createShadowRoot(); - youngerShadow.innerHTML = '<div style="background-color:green"><shadow></shadow></div>'; - - olderShadow.innerHTML = '<shadow></shadow> World'; - } - </script> -</head> -<body onload="tweak()"> -<div id="outer"> - <div style="width:300px; height:100px; background-color:red;"></div> -</div> -</body> -</html> diff --git a/layout/reftests/webcomponents/fallback-content-1.html b/layout/reftests/webcomponents/fallback-content-1.html index ac3b5d02a4..0bcd2abbd3 100644 --- a/layout/reftests/webcomponents/fallback-content-1.html +++ b/layout/reftests/webcomponents/fallback-content-1.html @@ -8,13 +8,13 @@ shadowDiv.style.border = "10px solid green"; // Insertion point will match nothing and use fallback content. - var insertionPoint = document.createElement("content"); - shadowDiv.appendChild(insertionPoint); + var slot = document.createElement("slot"); + shadowDiv.appendChild(slot); // Append three nodes as children to use as fallback content. - insertionPoint.innerHTML = '<span style="background-color: orange">Hello</span> <span style="background-color: green">World</span>'; + slot.innerHTML = '<span style="background-color: orange">Hello</span> <span style="background-color: green">World</span>'; - var shadowRoot = document.getElementById('outer').createShadowRoot(); + var shadowRoot = document.getElementById('outer').attachShadow({mode: 'open'}); shadowRoot.appendChild(shadowDiv); } </script> diff --git a/layout/reftests/webcomponents/input-transition-1.html b/layout/reftests/webcomponents/input-transition-1.html index ede0fa40bb..a4be5271b5 100644 --- a/layout/reftests/webcomponents/input-transition-1.html +++ b/layout/reftests/webcomponents/input-transition-1.html @@ -5,19 +5,28 @@ <body> <div id="host"></div> <script> - var host = document.getElementById("host"); - var root = host.createShadowRoot(); - root.innerHTML = '<style>input ~ div { background: red; transition: background 100ms; } input:checked ~ div { background: green; }</style><input id="one" type="checkbox"><div style="height: 50px; width: 50px;"></div>'; + var host, root; + + function run() { + host = document.getElementById("host"); + root = host.createShadowRoot(); + root.innerHTML = '<style>input ~ div { background: red; transition: background 100ms; } input:checked ~ div { background: green; }</style><input id="one" type="checkbox"><div style="height: 50px; width: 50px;"></div>'; + } function tweak() { - var el = root.getElementById("one"); - el.checked = true; - el.nextSibling.addEventListener("transitionend", function() { - document.documentElement.removeAttribute("class"); - }, false); + var el = root.getElementById("one"); + el.checked = true; + el.nextSibling.addEventListener("transitionend", function() { + setTimeout(()=>{document.documentElement.removeAttribute("class")}, 1000); // wait for the checkbox SVG image to load on Android + }); } - window.addEventListener("MozReftestInvalidate", tweak, false); + if (document.body.createShadowRoot) { + run(); + window.addEventListener("MozReftestInvalidate", tweak); + } else { + document.documentElement.className = ""; + } </script> </body> </html> diff --git a/layout/reftests/webcomponents/nested-insertion-point-1.html b/layout/reftests/webcomponents/nested-insertion-point-1.html index 66892029ab..3d0d92f0dc 100644 --- a/layout/reftests/webcomponents/nested-insertion-point-1.html +++ b/layout/reftests/webcomponents/nested-insertion-point-1.html @@ -7,19 +7,20 @@ var outerShadow = document.createElement("div"); outerShadow.style.border = "10px solid green"; - var outerInsertionPoint = document.createElement("content"); + var outerInsertionPoint = document.createElement("slot"); outerShadow.appendChild(outerInsertionPoint); // div with style "border: 10px solid orange" var innerShadow = document.createElement("div"); innerShadow.style.border = "10px solid orange"; - var innerInsertionPoint = document.createElement("content"); - innerShadow.appendChild(innerInsertionPoint); + var slot = document.createElement("slot"); + innerShadow.appendChild(slot); - outerShadow.createShadowRoot().appendChild(innerShadow); + outerShadow.attachShadow({mode: 'open'}).appendChild(innerShadow); - var shadowRoot = document.getElementById('outer').createShadowRoot(); + var shadowRoot = + document.getElementById('outer').attachShadow({mode: 'open'}); shadowRoot.appendChild(outerShadow); } </script> diff --git a/layout/reftests/webcomponents/nested-shadow-element-1.html b/layout/reftests/webcomponents/nested-shadow-element-1.html deleted file mode 100644 index cfaad3d490..0000000000 --- a/layout/reftests/webcomponents/nested-shadow-element-1.html +++ /dev/null @@ -1,29 +0,0 @@ -<!DOCTYPE HTML> -<html> -<head> - <script> - function tweak() { - var olderShadow = document.getElementById('outer').createShadowRoot(); - olderShadow.innerHTML = '<content></content><span>World</span>'; - - var youngerShadow = document.getElementById('outer').createShadowRoot(); - youngerShadow.innerHTML = '<div id="shadowparent"><shadow id="youngshadow"><span>Hello</span></shadow></div>'; - - var shadowParent = youngerShadow.getElementById("shadowparent"); - var nestedShadow = shadowParent.createShadowRoot(); - nestedShadow.innerHTML = '<div style="background-color: green"><content></content></div>'; - - // Dynamically append a span to the shadow element in the younger ShadowRoot to make sure - // it is projected into the nested shadow. - var appendedSpan = document.createElement("span"); - appendedSpan.textContent = ' '; - youngerShadow.getElementById("youngshadow").appendChild(appendedSpan); - } - </script> -</head> -<body onload="tweak()"> -<div id="outer"> - <div style="width:300px; height:100px; background-color:red;"></div> -</div> -</body> -</html> diff --git a/layout/reftests/webcomponents/reframe-shadow-child-1.html b/layout/reftests/webcomponents/reframe-shadow-child-1.html new file mode 100644 index 0000000000..d953beb6d7 --- /dev/null +++ b/layout/reftests/webcomponents/reframe-shadow-child-1.html @@ -0,0 +1,16 @@ +<!doctype html> +<template id="tmpl"> + <div style="display: table"> + Some text + <span style="display: table-cell">something</span> + More text + </div> +</template> +<div id="host"></div> +<script> + let shadowRoot = document.getElementById("host").attachShadow({mode: 'open'}); + let tmpl = document.getElementById("tmpl"); + shadowRoot.appendChild(document.importNode(tmpl.content, true)); + document.body.offsetTop; + shadowRoot.firstElementChild.querySelector("span").remove(); +</script> diff --git a/layout/reftests/webcomponents/reframe-shadow-child-2.html b/layout/reftests/webcomponents/reframe-shadow-child-2.html new file mode 100644 index 0000000000..0ebbe7433d --- /dev/null +++ b/layout/reftests/webcomponents/reframe-shadow-child-2.html @@ -0,0 +1,15 @@ +<!doctype html> +<template id="tmpl"> + <div style="display: block"> + Some text + More text + </div> +</template> +<div id="host"></div> +<script> + let shadowRoot = document.getElementById("host").attachShadow({mode: 'open'}); + let tmpl = document.getElementById("tmpl"); + shadowRoot.appendChild(document.importNode(tmpl.content, true)); + document.body.offsetTop; + shadowRoot.firstElementChild.style.display = "table"; +</script> diff --git a/layout/reftests/webcomponents/reftest.list b/layout/reftests/webcomponents/reftest.list index beba21b18c..6afbc27204 100644 --- a/layout/reftests/webcomponents/reftest.list +++ b/layout/reftests/webcomponents/reftest.list @@ -3,17 +3,17 @@ pref(dom.webcomponents.enabled,true) == basic-shadow-1.html basic-shadow-1-ref.h pref(dom.webcomponents.enabled,true) == basic-shadow-2.html basic-shadow-2-ref.html pref(dom.webcomponents.enabled,true) == basic-shadow-3.html basic-shadow-3-ref.html pref(dom.webcomponents.enabled,true) == basic-shadow-4.html basic-shadow-4-ref.html -pref(dom.webcomponents.enabled,true) == basic-insertion-point-1.html basic-insertion-point-1-ref.html -pref(dom.webcomponents.enabled,true) == basic-insertion-point-2.html basic-insertion-point-2-ref.html -pref(dom.webcomponents.enabled,true) == adjacent-insertion-points-1.html adjacent-insertion-points-1-ref.html -pref(dom.webcomponents.enabled,true) == adjacent-insertion-points-2.html adjacent-insertion-points-2-ref.html pref(dom.webcomponents.enabled,true) == fallback-content-1.html fallback-content-1-ref.html pref(dom.webcomponents.enabled,true) == remove-insertion-point-1.html remove-insertion-point-1-ref.html -pref(dom.webcomponents.enabled,true) == nested-insertion-point-1.html nested-insertion-point-1-ref.html -pref(dom.webcomponents.enabled,true) == basic-shadow-element-1.html basic-shadow-element-1-ref.html -pref(dom.webcomponents.enabled,true) == nested-shadow-element-1.html nested-shadow-element-1-ref.html -pref(dom.webcomponents.enabled,true) == update-dist-node-descendants-1.html update-dist-node-descendants-1-ref.html +#bug 1421542 pref(dom.webcomponents.enabled,true) == nested-insertion-point-1.html nested-insertion-point-1-ref.html +#bug 1421542 pref(dom.webcomponents.enabled,true) == update-dist-node-descendants-1.html update-dist-node-descendants-1-ref.html pref(dom.webcomponents.enabled,true) == input-transition-1.html input-transition-1-ref.html -pref(dom.webcomponents.enabled,true) == dynamic-insertion-point-distribution-1.html dynamic-insertion-point-distribution-1-ref.html +#bug 1421542 pref(dom.webcomponents.enabled,true) == dynamic-insertion-point-distribution-1.html dynamic-insertion-point-distribution-1-ref.html pref(dom.webcomponents.enabled,true) == dynamic-insertion-point-distribution-2.html dynamic-insertion-point-distribution-2-ref.html pref(dom.webcomponents.enabled,true) == remove-append-shadow-host-1.html remove-append-shadow-host-1-ref.html +pref(dom.webcomponents.enabled,true) == basic-slot-1.html basic-slot-1-ref.html +pref(dom.webcomponents.enabled,true) == basic-slot-2.html basic-slot-2-ref.html +pref(dom.webcomponents.enabled,true) == basic-slot-3.html basic-slot-3-ref.html +pref(dom.webcomponents.enabled,true) == basic-slot-4.html basic-slot-3-ref.html +pref(dom.webcomponents.enabled,true) == basic-slot-5.html basic-slot-5-ref.html +pref(dom.webcomponents.enabled,true) == basic-slot-6.html basic-slot-6-ref.html diff --git a/layout/reftests/webcomponents/remove-append-shadow-host-1.html b/layout/reftests/webcomponents/remove-append-shadow-host-1.html index 85161565e0..1a752eba6c 100644 --- a/layout/reftests/webcomponents/remove-append-shadow-host-1.html +++ b/layout/reftests/webcomponents/remove-append-shadow-host-1.html @@ -6,7 +6,7 @@ <div id="container"><div id="host"></div></div> <script> var host = document.getElementById("host"); - var root = host.createShadowRoot(); + var root = host.attachShadow({mode: 'open'}); root.innerHTML = 'inside shadow DOM'; var container = document.getElementById("container"); diff --git a/layout/reftests/webcomponents/remove-insertion-point-1.html b/layout/reftests/webcomponents/remove-insertion-point-1.html index 195673ca8d..1b7588daf6 100644 --- a/layout/reftests/webcomponents/remove-insertion-point-1.html +++ b/layout/reftests/webcomponents/remove-insertion-point-1.html @@ -8,15 +8,15 @@ shadowDiv.style.border = "10px solid green"; // Insertion point will match nothing and use fallback content. - var insertionPoint = document.createElement("content"); - shadowDiv.appendChild(insertionPoint); + var slot = document.createElement("slot"); + shadowDiv.appendChild(slot); - var shadowRoot = document.getElementById('outer').createShadowRoot(); + var shadowRoot = document.getElementById('outer').attachShadow({mode: 'open'}); shadowRoot.appendChild(shadowDiv); // Remove the insertion point from the ShadowRoot, "Hello" should no // longer be rendered. - shadowDiv.removeChild(insertionPoint); + shadowDiv.removeChild(slot); } </script> </head> diff --git a/layout/reftests/webcomponents/style-sharing-across-shadow.html b/layout/reftests/webcomponents/style-sharing-across-shadow.html new file mode 100644 index 0000000000..b41cf74796 --- /dev/null +++ b/layout/reftests/webcomponents/style-sharing-across-shadow.html @@ -0,0 +1,22 @@ +<!doctype html> +<style> + div { display: contents } +</style> +<div></div> +<div></div> +<script> + let divs = document.querySelectorAll('div'); + divs[0].attachShadow({mode: 'open'}).innerHTML = ` + <style> + * { color: green; } + </style> + <span>Should be green</span> + `; + divs[1].attachShadow({mode: 'open'}).innerHTML = ` + <style> + * { color: initial; } + [foo] { } + </style> + <span>Should not be green</span> + `; +</script> diff --git a/layout/reftests/webcomponents/style-sharing.html b/layout/reftests/webcomponents/style-sharing.html new file mode 100644 index 0000000000..0a1e3c95c4 --- /dev/null +++ b/layout/reftests/webcomponents/style-sharing.html @@ -0,0 +1,14 @@ +<!doctype html> +<div id="host"></div> +<script> + let root = host.attachShadow({mode: 'open'}); + root.innerHTML = ` + <style> + #test { + color: green; + } + </style> + <span id="test">Should be green</span> + <span id="test2">Should not be green</span> + `; +</script> diff --git a/layout/reftests/webcomponents/update-dist-node-descendants-1.html b/layout/reftests/webcomponents/update-dist-node-descendants-1.html index e2dd4ebb7b..3ba96594bf 100644 --- a/layout/reftests/webcomponents/update-dist-node-descendants-1.html +++ b/layout/reftests/webcomponents/update-dist-node-descendants-1.html @@ -5,17 +5,25 @@ <body> <div id="outer"><span id="distnode">text</span></div> <script> -var shadowRoot = document.getElementById('outer').createShadowRoot(); -shadowRoot.innerHTML = '<div><content></content></div>'; -function tweak() { - var distNode = document.getElementById("distnode"); - distNode.textContent = "Hello World"; - - document.documentElement.removeAttribute("class"); +function run() { + var shadowRoot = document.getElementById('outer').createShadowRoot(); + shadowRoot.innerHTML = '<div><content></content></div>'; } -window.addEventListener("MozReftestInvalidate", tweak); + function tweak() { + var distNode = document.getElementById("distnode"); + distNode.textContent = "Hello World"; + + document.documentElement.removeAttribute("class"); + } + +if (document.body.createShadowRoot) { + run(); + window.addEventListener("MozReftestInvalidate", tweak); +} else { + document.documentElement.className = ""; +} </script> </body> </html> diff --git a/layout/style/CSSStyleSheet.cpp b/layout/style/CSSStyleSheet.cpp index 71ca6e3f2c..43d47100d1 100644 --- a/layout/style/CSSStyleSheet.cpp +++ b/layout/style/CSSStyleSheet.cpp @@ -694,7 +694,7 @@ nsMediaList::GetMediaText(nsAString& aMediaText) // nsCOMPtr<nsIDocument> #define BEGIN_MEDIA_CHANGE(sheet, doc) \ if (sheet) { \ - doc = sheet->GetOwningDocument(); \ + doc = sheet->GetAssociatedDocument(); \ } \ mozAutoDocUpdate updateBatch(doc, UPDATE_STYLE, true); \ if (sheet) { \ @@ -864,7 +864,8 @@ struct ChildSheetListBuilder { void SetParentLinks(CSSStyleSheet* aSheet) { aSheet->mParent = parent; - aSheet->SetOwningDocument(parent->mDocument); + aSheet->SetAssociatedDocument(parent->mDocument, + parent->mDocumentAssociationMode); } static void ReparentChildList(CSSStyleSheet* aPrimarySheet, @@ -872,7 +873,8 @@ struct ChildSheetListBuilder { { for (CSSStyleSheet *child = aFirstChild; child; child = child->mNext) { child->mParent = aPrimarySheet; - child->SetOwningDocument(aPrimarySheet->mDocument); + child->SetAssociatedDocument(aPrimarySheet->mDocument, + aPrimarySheet->mDocumentAssociationMode); } } }; @@ -1359,16 +1361,22 @@ CSSStyleSheet::GetParentSheet() const } void -CSSStyleSheet::SetOwningDocument(nsIDocument* aDocument) -{ // not ref counted +CSSStyleSheet::SetAssociatedDocument(nsIDocument* aDocument, + DocumentAssociationMode aAssociationMode) +{ + MOZ_ASSERT_IF(!aDocument, aAssociationMode == NotOwnedByDocument); + + // not ref counted mDocument = aDocument; + mDocumentAssociationMode = aAssociationMode; + // Now set the same document on all our child sheets.... // XXXbz this is a little bogus; see the XXX comment where we // declare mFirstChild. for (CSSStyleSheet* child = mInner->mFirstChild; child; child = child->mNext) { if (child->mParent == this) { - child->SetOwningDocument(aDocument); + child->SetAssociatedDocument(aDocument, aAssociationMode); } } } diff --git a/layout/style/CSSStyleSheet.h b/layout/style/CSSStyleSheet.h index 74e12291ef..89189d7816 100644 --- a/layout/style/CSSStyleSheet.h +++ b/layout/style/CSSStyleSheet.h @@ -129,7 +129,8 @@ public: // style sheet owner info CSSStyleSheet* GetParentSheet() const; // may be null - void SetOwningDocument(nsIDocument* aDocument); + void SetAssociatedDocument(nsIDocument* aDocument, + DocumentAssociationMode aAssociationMode); // Find the ID of the owner inner window. uint64_t FindOwningWindowInnerID() const; diff --git a/layout/style/DocumentStyleRootIterator.cpp b/layout/style/DocumentStyleRootIterator.cpp new file mode 100644 index 0000000000..af95c5c4ff --- /dev/null +++ b/layout/style/DocumentStyleRootIterator.cpp @@ -0,0 +1,41 @@ +/* -*- 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 "DocumentStyleRootIterator.h" + +#include "nsContentUtils.h" + +namespace mozilla { + +DocumentStyleRootIterator::DocumentStyleRootIterator(nsIDocument* aDocument) + : mPosition(0) +{ + MOZ_COUNT_CTOR(DocumentStyleRootIterator); + if (Element* root = aDocument->GetRootElement()) { + mStyleRoots.AppendElement(root); + } + nsContentUtils::AppendDocumentLevelNativeAnonymousContentTo( + aDocument, mStyleRoots); +} + +Element* +DocumentStyleRootIterator::GetNextStyleRoot() +{ + for (;;) { + if (mPosition >= mStyleRoots.Length()) { + return nullptr; + } + + nsIContent* next = mStyleRoots[mPosition]; + ++mPosition; + + if (next->IsElement()) { + return next->AsElement(); + } + } +} + +} // namespace mozilla diff --git a/layout/style/DocumentStyleRootIterator.h b/layout/style/DocumentStyleRootIterator.h new file mode 100644 index 0000000000..7dcdc7fa1b --- /dev/null +++ b/layout/style/DocumentStyleRootIterator.h @@ -0,0 +1,37 @@ +/* -*- 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 DocumentStyleRootIterator_h +#define DocumentStyleRootIterator_h + +#include "nsTArray.h" + +class nsIContent; +class nsIDocument; + +namespace mozilla { + +/** + * DocumentStyleRootIterator traverses the roots of the document from the + * perspective of the Servo-backed style system. This will first traverse + * the document root, followed by any document level native anonymous content. + */ +class DocumentStyleRootIterator +{ +public: + explicit DocumentStyleRootIterator(nsIDocument* aDocument); + ~DocumentStyleRootIterator() { MOZ_COUNT_DTOR(DocumentStyleRootIterator); } + + Element* GetNextStyleRoot(); + +private: + AutoTArray<nsIContent*, 8> mStyleRoots; + uint32_t mPosition; +}; + +} // namespace mozilla + +#endif // DocumentStyleRootIterator_h diff --git a/layout/style/Loader.cpp b/layout/style/Loader.cpp index 0ce337e29f..9894ce8f45 100644 --- a/layout/style/Loader.cpp +++ b/layout/style/Loader.cpp @@ -1334,7 +1334,7 @@ Loader::InsertSheetInDoc(StyleSheet* aSheet, // XXX Need to cancel pending sheet loads for this element, if any - int32_t sheetCount = aDocument->GetNumberOfStyleSheets(); + int32_t sheetCount = aDocument->SheetCount(); /* * Start the walk at the _end_ of the list, since in the typical @@ -1346,7 +1346,7 @@ Loader::InsertSheetInDoc(StyleSheet* aSheet, */ int32_t insertionPoint; for (insertionPoint = sheetCount - 1; insertionPoint >= 0; --insertionPoint) { - StyleSheet* curSheet = aDocument->GetStyleSheetAt(insertionPoint); + StyleSheet* curSheet = aDocument->SheetAt(insertionPoint); NS_ASSERTION(curSheet, "There must be a sheet here!"); nsCOMPtr<nsINode> sheetOwner = curSheet->GetOwnerNode(); if (sheetOwner && !aLinkingContent) { @@ -2209,9 +2209,9 @@ Loader::LoadChildSheet(StyleSheet* aParentSheet, nsCOMPtr<nsINode> owningNode; - // check for an owning document: if none, don't bother walking up the parent - // sheets - if (aParentSheet->GetOwningDocument()) { + // check for an associated document: if none, don't bother walking up the + // parent sheets + if (aParentSheet->GetAssociatedDocument()) { StyleSheet* topSheet = aParentSheet; while (StyleSheet* parent = topSheet->GetParentSheet()) { topSheet = parent; diff --git a/layout/style/ServoStyleSet.cpp b/layout/style/ServoStyleSet.cpp index 519d17aa85..331e2322a1 100644 --- a/layout/style/ServoStyleSet.cpp +++ b/layout/style/ServoStyleSet.cpp @@ -339,7 +339,7 @@ ServoStyleSet::AddDocStyleSheet(ServoStyleSheet* aSheet, mSheets[SheetType::Doc].RemoveElement(aSheet); size_t index = - aDocument->FindDocStyleSheetInsertionPoint(mSheets[SheetType::Doc], aSheet); + aDocument->FindDocStyleSheetInsertionPoint(mSheets[SheetType::Doc], *aSheet); mSheets[SheetType::Doc].InsertElementAt(index, aSheet); // Maintain a mirrored list of sheets on the servo side. diff --git a/layout/style/ServoStyleSheet.cpp b/layout/style/ServoStyleSheet.cpp index cfeae20d2f..5f2a925b59 100644 --- a/layout/style/ServoStyleSheet.cpp +++ b/layout/style/ServoStyleSheet.cpp @@ -30,19 +30,23 @@ ServoStyleSheet::HasRules() const } void -ServoStyleSheet::SetOwningDocument(nsIDocument* aDocument) +ServoStyleSheet::SetAssociatedDocument(nsIDocument* aDocument, + DocumentAssociationMode aAssociationMode) { + MOZ_ASSERT_IF(!aDocument, aAssociationMode == NotOwnedByDocument); + // XXXheycam: Traverse to child ServoStyleSheets to set this, like - // CSSStyleSheet::SetOwningDocument does. + // CSSStyleSheet::SetAssociatedDocument does. mDocument = aDocument; + mDocumentAssociationMode = aAssociationMode; } ServoStyleSheet* ServoStyleSheet::GetParentSheet() const { // XXXheycam: When we implement support for child sheets, we'll have - // to fix SetOwningDocument to propagate the owning document down + // to fix SetAssociatedDocument to propagate the associated document down // to the children. MOZ_CRASH("stylo: not implemented"); } diff --git a/layout/style/ServoStyleSheet.h b/layout/style/ServoStyleSheet.h index 079f196eb9..c54c15e7d5 100644 --- a/layout/style/ServoStyleSheet.h +++ b/layout/style/ServoStyleSheet.h @@ -29,7 +29,8 @@ public: bool HasRules() const; - void SetOwningDocument(nsIDocument* aDocument); + void SetAssociatedDocument(nsIDocument* aDocument, + DocumentAssociationMode aAssociationMode); ServoStyleSheet* GetParentSheet() const; void AppendStyleSheet(ServoStyleSheet* aSheet); diff --git a/layout/style/StyleRule.cpp b/layout/style/StyleRule.cpp index 598cb7c74e..aca97a505e 100644 --- a/layout/style/StyleRule.cpp +++ b/layout/style/StyleRule.cpp @@ -1078,6 +1078,16 @@ public: return mRule ? mRule->GetDocument() : nullptr; } + virtual DocGroup* GetDocGroup() const override + { + if (!mRule) { + return nullptr; + } + + nsIDocument* document = mRule->GetDocument(); + return document ? document->GetDocGroup() : nullptr; + } + friend class css::DOMCSSStyleRule; protected: @@ -1209,13 +1219,13 @@ DOMCSSDeclarationImpl::SetCSSDeclaration(DeclarationBlock* aDecl) NS_PRECONDITION(mRule, "can only be called when |GetCSSDeclaration| returned a declaration"); - nsCOMPtr<nsIDocument> owningDoc; + nsCOMPtr<nsIDocument> doc; RefPtr<CSSStyleSheet> sheet = mRule->GetStyleSheet(); if (sheet) { - owningDoc = sheet->GetOwningDocument(); + doc = sheet->GetAssociatedDocument(); } - mozAutoDocUpdate updateBatch(owningDoc, UPDATE_STYLE, true); + mozAutoDocUpdate updateBatch(doc, UPDATE_STYLE, true); mRule->SetDeclaration(aDecl->AsGecko()); @@ -1223,8 +1233,8 @@ DOMCSSDeclarationImpl::SetCSSDeclaration(DeclarationBlock* aDecl) sheet->DidDirty(); } - if (owningDoc) { - owningDoc->StyleRuleChanged(sheet, mRule); + if (doc) { + doc->StyleRuleChanged(sheet, mRule); } return NS_OK; } diff --git a/layout/style/StyleSheet.cpp b/layout/style/StyleSheet.cpp index f307f39188..f125cf97e0 100644 --- a/layout/style/StyleSheet.cpp +++ b/layout/style/StyleSheet.cpp @@ -12,6 +12,8 @@ #include "mozilla/StyleSheetInlines.h" #include "mozilla/CSSStyleSheet.h" +#include "mozAutoDocUpdate.h" +#include "nsIMediaList.h" #include "nsNullPrincipal.h" namespace mozilla { @@ -22,6 +24,7 @@ StyleSheet::StyleSheet(StyleBackendType aType, css::SheetParsingMode aParsingMod , mParsingMode(aParsingMode) , mType(aType) , mDisabled(false) + , mDocumentAssociationMode(NotOwnedByDocument) { } @@ -34,6 +37,9 @@ StyleSheet::StyleSheet(const StyleSheet& aCopy, , mParsingMode(aCopy.mParsingMode) , mType(aCopy.mType) , mDisabled(aCopy.mDisabled) + // We only use this constructor during cloning. It's the cloner's + // responsibility to notify us if we end up being owned by a document. + , mDocumentAssociationMode(NotOwnedByDocument) { } @@ -349,7 +355,7 @@ StyleSheet::AreRulesAvailable(nsIPrincipal& aSubjectPrincipal, JSObject* StyleSheet::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { - return CSSStyleSheetBinding::Wrap(aCx, this, aGivenProto); + return dom::CSSStyleSheetBinding::Wrap(aCx, this, aGivenProto); } } // namespace mozilla diff --git a/layout/style/StyleSheet.h b/layout/style/StyleSheet.h index 863f6d22fa..7a1a71466a 100644 --- a/layout/style/StyleSheet.h +++ b/layout/style/StyleSheet.h @@ -95,8 +95,22 @@ public: inline bool HasRules() const; // style sheet owner info - nsIDocument* GetOwningDocument() const { return mDocument; } - inline void SetOwningDocument(nsIDocument* aDocument); + enum DocumentAssociationMode { + // OwnedByDocument means mDocument owns us (possibly via a chain of other + // stylesheets). + OwnedByDocument, + // NotOwnedByDocument means we're owned by something that might have a + // different lifetime than mDocument. + NotOwnedByDocument + }; + nsIDocument* GetAssociatedDocument() const { return mDocument; } + bool IsOwnedByDocument() const { + return mDocumentAssociationMode == OwnedByDocument; + } + // aDocument must not be null. + inline void SetAssociatedDocument(nsIDocument* aDocument, + DocumentAssociationMode aMode); + inline void ClearAssociatedDocument(); nsINode* GetOwnerNode() const { return mOwningNode; } inline StyleSheet* GetParentSheet() const; @@ -206,6 +220,11 @@ protected: const StyleBackendType mType; bool mDisabled; + + // mDocumentAssociationMode determines whether mDocument directly owns us (in + // the sense that if it's known-live then we're known-live). Always + // NotOwnedByDocument when mDocument is null. + DocumentAssociationMode mDocumentAssociationMode; }; } // namespace mozilla diff --git a/layout/style/StyleSheetInlines.h b/layout/style/StyleSheetInlines.h index d03a3741b7..c0b8495f87 100644 --- a/layout/style/StyleSheetInlines.h +++ b/layout/style/StyleSheetInlines.h @@ -83,9 +83,17 @@ StyleSheet::HasRules() const } void -StyleSheet::SetOwningDocument(nsIDocument* aDocument) +StyleSheet::SetAssociatedDocument(nsIDocument* aDocument, + DocumentAssociationMode aAssociationMode) { - MOZ_STYLO_FORWARD(SetOwningDocument, (aDocument)) + MOZ_ASSERT(aDocument); + MOZ_STYLO_FORWARD(SetAssociatedDocument, (aDocument, aAssociationMode)) +} + +void +StyleSheet::ClearAssociatedDocument() +{ + MOZ_STYLO_FORWARD(SetAssociatedDocument, (nullptr, NotOwnedByDocument)); } StyleSheet* diff --git a/layout/style/crashtests/1017798-1.html b/layout/style/crashtests/1017798-1.html index 097217d188..0460c8756f 100644 --- a/layout/style/crashtests/1017798-1.html +++ b/layout/style/crashtests/1017798-1.html @@ -50,27 +50,27 @@ gaia_switch/examples/index.html from the Gaia repository. window.GaiaSwitch = (function(win) { // Extend from the HTMLElement prototype - var proto = Object.create(HTMLElement.prototype); + class GaiaSwitch extends HTMLElement { + connectedCallback() { + var shadow = this.createShadowRoot(); + this._template = template.content.cloneNode(true); + this._input = this._template.querySelector('input[type="checkbox"]'); + + var checked = this.getAttribute('checked'); + if (checked !== null) { + this._input.checked = true; + } - proto.createdCallback = function() { - var shadow = this.createShadowRoot(); - this._template = template.content.cloneNode(true); - this._input = this._template.querySelector('input[type="checkbox"]'); + shadow.appendChild(this._template); - var checked = this.getAttribute('checked'); - if (checked !== null) { - this._input.checked = true; + ComponentUtils.style.call(this, ''); } - - shadow.appendChild(this._template); - - ComponentUtils.style.call(this, ''); }; /** * Proxy the checked property to the input element. */ - Object.defineProperty( proto, 'checked', { + Object.defineProperty( GaiaSwitch.prototype, 'checked', { get: function() { return this._input.checked; }, @@ -82,7 +82,7 @@ window.GaiaSwitch = (function(win) { /** * Proxy the name property to the input element. */ - Object.defineProperty( proto, 'name', { + Object.defineProperty( GaiaSwitch.prototype, 'name', { get: function() { return this.getAttribute('name'); }, diff --git a/layout/style/moz.build b/layout/style/moz.build index 3dc2a19af7..40b9fd6598 100644 --- a/layout/style/moz.build +++ b/layout/style/moz.build @@ -88,6 +88,7 @@ EXPORTS.mozilla += [ 'CSSVariableValues.h', 'DeclarationBlock.h', 'DeclarationBlockInlines.h', + 'DocumentStyleRootIterator.h', 'HandleRefPtr.h', 'IncrementalClearCOMRuleArray.h', 'LayerAnimationInfo.h', @@ -151,6 +152,7 @@ UNIFIED_SOURCES += [ 'CSSVariableResolver.cpp', 'CSSVariableValues.cpp', 'Declaration.cpp', + 'DocumentStyleRootIterator.cpp', 'ErrorReporter.cpp', 'FontFace.cpp', 'FontFaceSet.cpp', diff --git a/layout/style/nsAnimationManager.cpp b/layout/style/nsAnimationManager.cpp index aa1b6fe787..04086a3ae9 100644 --- a/layout/style/nsAnimationManager.cpp +++ b/layout/style/nsAnimationManager.cpp @@ -367,7 +367,7 @@ UpdateOldAnimationPropertiesWithNew( // Update the old from the new so we can keep the original object // identity (and any expando properties attached to it). if (aOld.GetEffect()) { - AnimationEffectReadOnly* oldEffect = aOld.GetEffect(); + dom::AnimationEffectReadOnly* oldEffect = aOld.GetEffect(); animationChanged = oldEffect->SpecifiedTiming() != aNewTiming; oldEffect->SetSpecifiedTiming(aNewTiming); diff --git a/layout/style/nsCSSPseudoClassList.h b/layout/style/nsCSSPseudoClassList.h index 7015783383..12f43af5b0 100644 --- a/layout/style/nsCSSPseudoClassList.h +++ b/layout/style/nsCSSPseudoClassList.h @@ -90,9 +90,6 @@ CSS_PSEUDO_CLASS(nthLastOfType, ":nth-last-of-type", 0, "") // Match nodes that are HTML but not XHTML CSS_PSEUDO_CLASS(mozIsHTML, ":-moz-is-html", 0, "") -// Match all custom elements whose created callback has not yet been invoked - CSS_STATE_PSEUDO_CLASS(unresolved, ":unresolved", 0, "", NS_EVENT_STATE_UNRESOLVED) - // Matches nodes that are in a native-anonymous subtree (i.e., nodes in // a subtree of C++ anonymous content constructed by Gecko for its own // purposes). @@ -211,6 +208,19 @@ CSS_STATE_PSEUDO_CLASS(mozMathIncrementScriptLevel, ":-moz-math-increment-script-level", 0, "", NS_EVENT_STATE_INCREMENT_SCRIPT_LEVEL) +CSS_STATE_PSEUDO_CLASS(mozHasDirAttr, ":-moz-has-dir-attr", + CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS, "", + NS_EVENT_STATE_HAS_DIR_ATTR) +CSS_STATE_PSEUDO_CLASS(mozDirAttrLTR, ":-moz-dir-attr-ltr", + CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS, "", + NS_EVENT_STATE_DIR_ATTR_LTR) +CSS_STATE_PSEUDO_CLASS(mozDirAttrRTL, ":-moz-dir-attr-rtl", + CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS, "", + NS_EVENT_STATE_DIR_ATTR_RTL) +CSS_STATE_PSEUDO_CLASS(mozDirAttrLikeAuto, ":-moz-dir-attr-like-auto", + CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS, "", + NS_EVENT_STATE_DIR_ATTR_LIKE_AUTO) + // CSS 3 UI // http://www.w3.org/TR/2004/CR-css3-ui-20040511/#pseudo-classes CSS_STATE_PSEUDO_CLASS(required, ":required", 0, "", NS_EVENT_STATE_REQUIRED) diff --git a/layout/style/nsCSSPseudoElements.h b/layout/style/nsCSSPseudoElements.h index acf818a2c1..64eb2f00ce 100644 --- a/layout/style/nsCSSPseudoElements.h +++ b/layout/style/nsCSSPseudoElements.h @@ -36,6 +36,10 @@ #define CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE (1<<3) // Is content prevented from parsing selectors containing this pseudo-element? #define CSS_PSEUDO_ELEMENT_UA_SHEET_ONLY (1<<4) +// Can we use the ChromeOnly document.createElement(..., { pseudo: "::foo" }) +// API for creating pseudo-implementing native anonymous content in JS with this +// pseudo-element? +#define CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC (1<<5) namespace mozilla { @@ -98,6 +102,11 @@ public: static bool PseudoElementSupportsUserActionState(const Type aType); + static bool PseudoElementIsJSCreatedNAC(Type aType) + { + return PseudoElementHasFlags(aType, CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC); + } + static bool IsEnabled(Type aType, EnabledState aEnabledState) { return !PseudoElementHasFlags(aType, CSS_PSEUDO_ELEMENT_UA_SHEET_ONLY) || diff --git a/layout/style/nsCSSRules.cpp b/layout/style/nsCSSRules.cpp index 4b90b6f0c0..dc79e471d8 100644 --- a/layout/style/nsCSSRules.cpp +++ b/layout/style/nsCSSRules.cpp @@ -1515,6 +1515,13 @@ nsCSSFontFaceStyleDecl::GetParentObject() return ContainingRule()->GetDocument(); } +DocGroup* +nsCSSFontFaceStyleDecl::GetDocGroup() const +{ + nsIDocument* document = ContainingRule()->GetDocument(); + return document ? document->GetDocGroup() : nullptr; +} + JSObject* nsCSSFontFaceStyleDecl::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) { @@ -1991,6 +1998,17 @@ nsCSSKeyframeStyleDeclaration::GetParentObject() return mRule ? mRule->GetDocument() : nullptr; } +DocGroup* +nsCSSKeyframeStyleDeclaration::GetDocGroup() const +{ + if (!mRule) { + return nullptr; + } + + nsIDocument* document = mRule->GetDocument(); + return document ? document->GetDocGroup() : nullptr; +} + // ------------------------------------------- // nsCSSKeyframeRule // @@ -2538,6 +2556,17 @@ nsCSSPageStyleDeclaration::GetParentObject() return mRule ? mRule->GetDocument() : nullptr; } +DocGroup* +nsCSSPageStyleDeclaration::GetDocGroup() const +{ + if (!mRule) { + return nullptr; + } + + nsIDocument* document = mRule->GetDocument(); + return document ? document->GetDocGroup() : nullptr; +} + // ------------------------------------------- // nsCSSPageRule // diff --git a/layout/style/nsCSSRules.h b/layout/style/nsCSSRules.h index daefaf3f98..1bd468cb6c 100644 --- a/layout/style/nsCSSRules.h +++ b/layout/style/nsCSSRules.h @@ -39,6 +39,11 @@ class nsMediaList; namespace mozilla { +namespace dom { +class DocGroup; +class DocGroup; +} // namespace dom + class ErrorResult; namespace css { @@ -209,6 +214,7 @@ public: using nsICSSDeclaration::GetPropertyCSSValue; virtual nsINode *GetParentObject() override; + virtual mozilla::dom::DocGroup* GetDocGroup() const override; virtual void IndexedGetter(uint32_t aIndex, bool& aFound, nsAString& aPropName) override; nsresult GetPropertyValue(nsCSSFontDesc aFontDescID, @@ -366,6 +372,7 @@ public: nsICSSDeclaration) virtual nsINode* GetParentObject() override; + virtual mozilla::dom::DocGroup* GetDocGroup() const override; protected: virtual ~nsCSSKeyframeStyleDeclaration(); @@ -496,6 +503,7 @@ public: nsICSSDeclaration) virtual nsINode *GetParentObject() override; + virtual mozilla::dom::DocGroup* GetDocGroup() const override; protected: virtual ~nsCSSPageStyleDeclaration(); diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index 910c1de8ae..20e5651bd4 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -638,6 +638,34 @@ nsComputedDOMStyle::SetFrameStyleContext(nsStyleContext* aContext) mStyleContext = aContext; } +/** + * The following function checks whether we need to explicitly resolve the style + * again, even though we have a style context coming from the frame. + * + * This basically checks whether the style is or may be under a ::first-line or + * ::first-letter frame, in which case we can't return the frame style, and we + * need to resolve it. See bug 505515. + */ +static bool +MustReresolveStyle(const nsStyleContext* aContext) +{ + MOZ_ASSERT(aContext); + + if (aContext->HasPseudoElementData()) { + if (!aContext->GetPseudo() || + aContext->StyleSource().IsServoComputedValues()) { + // TODO(emilio): When ::first-line is supported in Servo, we may want to + // fix this to avoid re-resolving pseudo-element styles. + return true; + } + + return aContext->GetParent() && + aContext->GetParent()->HasPseudoElementData(); + } + + return false; +} + void nsComputedDOMStyle::UpdateCurrentStyleSources(bool aNeedsLayoutFlush) { @@ -690,9 +718,20 @@ nsComputedDOMStyle::UpdateCurrentStyleSources(bool aNeedsLayoutFlush) // XXX the !mElement->IsHTMLElement(nsGkAtoms::area) // check is needed due to bug 135040 (to avoid using // mPrimaryFrame). Remove it once that's fixed. - if (!mPseudo && mStyleType == eAll && - !mElement->IsHTMLElement(nsGkAtoms::area)) { - mOuterFrame = mElement->GetPrimaryFrame(); + if (mStyleType == eAll && !mElement->IsHTMLElement(nsGkAtoms::area)) { + mOuterFrame = nullptr; + + if (!mPseudo) { + mOuterFrame = mElement->GetPrimaryFrame(); + } else if (mPseudo == nsCSSPseudoElements::before || + mPseudo == nsCSSPseudoElements::after) { + nsIAtom* property = mPseudo == nsCSSPseudoElements::before + ? nsGkAtoms::beforePseudoProperty + : nsGkAtoms::afterPseudoProperty; + + auto* pseudo = static_cast<Element*>(mElement->GetProperty(property)); + mOuterFrame = pseudo ? pseudo->GetPrimaryFrame() : nullptr; + } mInnerFrame = mOuterFrame; if (mOuterFrame) { nsIAtom* type = mOuterFrame->GetType(); @@ -711,13 +750,15 @@ nsComputedDOMStyle::UpdateCurrentStyleSources(bool aNeedsLayoutFlush) } } - if (!mStyleContext || mStyleContext->HasPseudoElementData()) { + if (!mStyleContext || MustReresolveStyle(mStyleContext)) { #ifdef DEBUG if (mStyleContext) { // We want to check that going through this path because of // HasPseudoElementData is rare, because it slows us down a good // bit. So check that we're really inside something associated - // with a pseudo-element that contains elements. + // with a pseudo-element that contains elements. (We also allow + // the element to be NAC, just in case some chrome JS calls + // getComputedStyle on a NAC-implemented pseudo.) nsStyleContext* topWithPseudoElementData = mStyleContext; while (topWithPseudoElementData->GetParent()->HasPseudoElementData()) { topWithPseudoElementData = topWithPseudoElementData->GetParent(); @@ -728,7 +769,8 @@ nsComputedDOMStyle::UpdateCurrentStyleSources(bool aNeedsLayoutFlush) NS_LITERAL_STRING("we should be in a pseudo-element that is expected to contain elements (")); assertMsg.Append(nsDependentString(pseudoAtom->GetUTF16String())); assertMsg.Append(')'); - NS_ASSERTION(nsCSSPseudoElements::PseudoElementContainsElements(pseudo), + NS_ASSERTION(nsCSSPseudoElements::PseudoElementContainsElements(pseudo) || + mElement->IsNativeAnonymous(), NS_LossyConvertUTF16toASCII(assertMsg).get()); } #endif diff --git a/layout/style/nsComputedDOMStyle.h b/layout/style/nsComputedDOMStyle.h index e94d8dbf64..5af518c2eb 100644 --- a/layout/style/nsComputedDOMStyle.h +++ b/layout/style/nsComputedDOMStyle.h @@ -27,6 +27,7 @@ namespace mozilla { namespace dom { +class DocGroup; class Element; } // namespace dom struct ComputedGridTrackInfo; @@ -84,6 +85,11 @@ public: return mElement; } + virtual mozilla::dom::DocGroup* GetDocGroup() const override + { + return mElement ? mElement->GetDocGroup() : nullptr; + } + static already_AddRefed<nsStyleContext> GetStyleContextForElement(mozilla::dom::Element* aElement, nsIAtom* aPseudo, nsIPresShell* aPresShell, diff --git a/layout/style/nsDOMCSSAttrDeclaration.cpp b/layout/style/nsDOMCSSAttrDeclaration.cpp index ce638a9c2e..7b659a8cc2 100644 --- a/layout/style/nsDOMCSSAttrDeclaration.cpp +++ b/layout/style/nsDOMCSSAttrDeclaration.cpp @@ -178,6 +178,12 @@ nsDOMCSSAttributeDeclaration::GetParentObject() return mElement; } +/* virtual */ DocGroup* +nsDOMCSSAttributeDeclaration::GetDocGroup() const +{ + return mElement ? mElement->OwnerDoc()->GetDocGroup() : nullptr; +} + NS_IMETHODIMP nsDOMCSSAttributeDeclaration::SetPropertyValue(const nsCSSPropertyID aPropID, const nsAString& aValue) diff --git a/layout/style/nsDOMCSSAttrDeclaration.h b/layout/style/nsDOMCSSAttrDeclaration.h index 7c0fbacc05..482d665ea7 100644 --- a/layout/style/nsDOMCSSAttrDeclaration.h +++ b/layout/style/nsDOMCSSAttrDeclaration.h @@ -35,6 +35,8 @@ public: NS_IMETHOD GetParentRule(nsIDOMCSSRule **aParent) override; virtual nsINode* GetParentObject() override; + typedef mozilla::dom::DocGroup DocGroup; + virtual DocGroup* GetDocGroup() const override; NS_IMETHOD SetPropertyValue(const nsCSSPropertyID aPropID, const nsAString& aValue) override; diff --git a/layout/style/nsDOMCSSDeclaration.cpp b/layout/style/nsDOMCSSDeclaration.cpp index bd6c6069d0..51ce8c335c 100644 --- a/layout/style/nsDOMCSSDeclaration.cpp +++ b/layout/style/nsDOMCSSDeclaration.cpp @@ -267,7 +267,7 @@ nsDOMCSSDeclaration::GetCSSParsingEnvironmentForRule(css::Rule* aRule, return; } - nsIDocument* document = sheet->GetOwningDocument(); + nsIDocument* document = sheet->GetAssociatedDocument(); aCSSParseEnv.mSheetURI = sheet->GetSheetURI(); aCSSParseEnv.mBaseURI = sheet->GetBaseURI(); aCSSParseEnv.mPrincipal = sheet->Principal(); diff --git a/layout/style/nsICSSDeclaration.h b/layout/style/nsICSSDeclaration.h index ff6ec4a782..fa80c17f5a 100644 --- a/layout/style/nsICSSDeclaration.h +++ b/layout/style/nsICSSDeclaration.h @@ -31,6 +31,11 @@ #include "nsCOMPtr.h" class nsINode; +namespace mozilla { +namespace dom { +class DocGroup; +} // namespace dom +} // namespace mozilla // dbeabbfa-6cb3-4f5c-aec2-dd558d9d681f #define NS_ICSSDECLARATION_IID \ @@ -62,6 +67,7 @@ public: const nsAString& aValue) = 0; virtual nsINode *GetParentObject() = 0; + virtual mozilla::dom::DocGroup* GetDocGroup() const = 0; // Also have to declare all the nsIDOMCSSStyleDeclaration methods, // since we want to be able to call them from the WebIDL versions. diff --git a/layout/style/nsStyleSet.cpp b/layout/style/nsStyleSet.cpp index 414eee4d43..558b9fa7f1 100644 --- a/layout/style/nsStyleSet.cpp +++ b/layout/style/nsStyleSet.cpp @@ -734,7 +734,7 @@ nsStyleSet::AddDocStyleSheet(CSSStyleSheet* aSheet, nsIDocument* aDocument) bool present = sheets.RemoveElement(aSheet); - size_t index = aDocument->FindDocStyleSheetInsertionPoint(sheets, aSheet); + size_t index = aDocument->FindDocStyleSheetInsertionPoint(sheets, *aSheet); sheets.InsertElementAt(index, aSheet); if (!present) { diff --git a/layout/style/nsStyleUtil.cpp b/layout/style/nsStyleUtil.cpp index 840cd03c33..9c3c0f449a 100644 --- a/layout/style/nsStyleUtil.cpp +++ b/layout/style/nsStyleUtil.cpp @@ -8,6 +8,7 @@ #include "nsIContent.h" #include "nsCSSProps.h" +#include "nsContentUtils.h" #include "nsRuleNode.h" #include "nsROCSSPrimitiveValue.h" #include "nsStyleStruct.h" diff --git a/layout/style/nsTransitionManager.cpp b/layout/style/nsTransitionManager.cpp index 118702e8f5..ace215a9fb 100644 --- a/layout/style/nsTransitionManager.cpp +++ b/layout/style/nsTransitionManager.cpp @@ -378,7 +378,7 @@ CSSTransition::HasLowerCompositeOrderThan(const CSSTransition& aOther) const } /* static */ Nullable<TimeDuration> -CSSTransition::GetCurrentTimeAt(const DocumentTimeline& aTimeline, +CSSTransition::GetCurrentTimeAt(const dom::DocumentTimeline& aTimeline, const TimeStamp& aBaseTime, const TimeDuration& aStartTime, double aPlaybackRate) @@ -395,7 +395,7 @@ CSSTransition::GetCurrentTimeAt(const DocumentTimeline& aTimeline, } void -CSSTransition::SetEffectFromStyle(AnimationEffectReadOnly* aEffect) +CSSTransition::SetEffectFromStyle(dom::AnimationEffectReadOnly* aEffect) { Animation::SetEffectNoUpdate(aEffect); diff --git a/layout/style/res/forms.css b/layout/style/res/forms.css index 95025221d1..c91c2f7392 100644 --- a/layout/style/res/forms.css +++ b/layout/style/res/forms.css @@ -1099,6 +1099,7 @@ input[type=number]::-moz-number-spin-box { } input[type=number]::-moz-number-spin-up { + writing-mode: horizontal-tb; -moz-appearance: spinner-upbutton; display: block; /* bug 926670 */ flex: none; @@ -1116,6 +1117,7 @@ input[type=number]::-moz-number-spin-up { } input[type=number]::-moz-number-spin-down { + writing-mode: horizontal-tb; -moz-appearance: spinner-downbutton; display: block; /* bug 926670 */ flex: none; diff --git a/layout/style/res/html.css b/layout/style/res/html.css index 1f572467f3..ea8efbe24d 100644 --- a/layout/style/res/html.css +++ b/layout/style/res/html.css @@ -7,18 +7,18 @@ /* bidi */ -[dir] { +:-moz-has-dir-attr { unicode-bidi: isolate; } -[dir="rtl"] { +:-moz-dir-attr-rtl { direction: rtl; } -[dir="ltr"] { +:-moz-dir-attr-ltr { direction: ltr; } -bdi:dir(ltr), [dir="auto"]:dir(ltr) { direction: ltr; } -bdi:dir(rtl), [dir="auto"]:dir(rtl) { direction: rtl; } +:-moz-dir-attr-like-auto:dir(ltr) { direction: ltr; } +:-moz-dir-attr-like-auto:dir(rtl) { direction: rtl; } /* To ensure http://www.w3.org/TR/REC-html40/struct/dirlang.html#style-bidi: * @@ -89,10 +89,15 @@ xmp { bdi, output { unicode-bidi: isolate; } -bdo, bdo[dir] { +/* We need the "bdo:-moz-has-dir-attr" bit because "bdo" has lower + specificity than the ":-moz-has-dir-attr" selector above. */ +bdo, bdo:-moz-has-dir-attr { unicode-bidi: isolate-override; } -textarea[dir="auto"], pre[dir="auto"] { unicode-bidi: plaintext; } +textarea:-moz-dir-attr-like-auto, +pre:-moz-dir-attr-like-auto { + unicode-bidi: plaintext; +} /* blocks */ diff --git a/layout/style/res/ua.css b/layout/style/res/ua.css index 931b32eb86..504f5dc570 100644 --- a/layout/style/res/ua.css +++ b/layout/style/res/ua.css @@ -471,3 +471,9 @@ div:-moz-native-anonymous.moz-custom-content-container { width: 100%; height: 100%; } + +/* Shadow DOM v1 + * https://drafts.csswg.org/css-scoping/#slots-in-shadow-tree */ +slot { + display: contents; +}
\ No newline at end of file diff --git a/layout/style/test/chrome/chrome.ini b/layout/style/test/chrome/chrome.ini index e34fce671a..dd3bdf8f56 100644 --- a/layout/style/test/chrome/chrome.ini +++ b/layout/style/test/chrome/chrome.ini @@ -10,6 +10,7 @@ support-files = mismatch.png [test_author_specified_style.html] +[test_bug1346623.html] [test_bug418986-2.xul] [test_bug1157097.html] [test_bug1160724.xul] diff --git a/layout/style/test/chrome/test_bug1346623.html b/layout/style/test/chrome/test_bug1346623.html new file mode 100644 index 0000000000..d24d666469 --- /dev/null +++ b/layout/style/test/chrome/test_bug1346623.html @@ -0,0 +1,60 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for bug 1346623</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> +</head> +<body onload="startTest();"> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1346623">Mozilla Bug 1346623</a> +<div id="display"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +var Ci = Components.interfaces; +var winUtils = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); + +function startTest() { + // load some styles at the agent level + var css = ` + #ac-parent { color: green; } + #ac-child.abc { } + `; + var sheetURL = "data:text/css," + encodeURIComponent(css); + winUtils.loadSheetUsingURIString(sheetURL, winUtils.AGENT_SHEET); + + // add canvas anonymous content + var bq = document.createElement("blockquote"); + bq.id = "ac-parent"; + bq.textContent = "This blockquote text should be green."; + var div = document.createElement("div"); + div.id = "ac-child"; + div.textContent = " This div text should be green."; + bq.appendChild(div); + var ac = document.insertAnonymousContent(bq); + document.body.offsetWidth; + + is(ac.getComputedStylePropertyValue("ac-child", "color"), "rgb(0, 128, 0)", + "color before reframing"); + + // reframe the root + document.documentElement.style.display = "flex"; + document.body.offsetWidth; + + // restyle the div + ac.setAttributeForElement("ac-child", "class", "abc"); + document.body.offsetWidth; + + is(ac.getComputedStylePropertyValue("ac-child", "color"), "rgb(0, 128, 0)", + "color after reframing"); + SimpleTest.finish(); +} + +SimpleTest.waitForExplicitFinish(); +</script> +</pre> +</body> +</html> diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index e66f588612..80379ffe7b 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1250,7 +1250,6 @@ pref("dom.event.clipboardevents.enabled", true); pref("dom.event.highrestimestamp.enabled", true); pref("dom.webcomponents.enabled", false); -pref("dom.webcomponents.customelements.enabled", false); pref("javascript.enabled", true); // Enable Array.prototype.values diff --git a/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/ElementName.java b/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/ElementName.java index 4e1acea502..4b87d3fde2 100644 --- a/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/ElementName.java +++ b/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/ElementName.java @@ -1987,10 +1987,6 @@ public final class ElementName // CPPONLY: NS_NewHTMLUnknownElement, // CPPONLY: NS_NewSVGUnknownElement, TreeBuilder.OTHER); - public static final ElementName CONTENT = new ElementName("content", "content", - // CPPONLY: NS_NewHTMLContentElement, - // CPPONLY: NS_NewSVGUnknownElement, - TreeBuilder.OTHER); public static final ElementName GT = new ElementName("gt", "gt", // CPPONLY: NS_NewHTMLUnknownElement, // CPPONLY: NS_NewSVGUnknownElement, @@ -2175,6 +2171,10 @@ public final class ElementName // CPPONLY: NS_NewHTMLUnknownElement, // CPPONLY: NS_NewSVGUnknownElement, TreeBuilder.OTHER); + public static final ElementName SLOT = new ElementName("slot", "slot", + // CPPONLY: NS_NewHTMLSlotElement, + // CPPONLY: NS_NewSVGUnknownElement, + TreeBuilder.OTHER); public static final ElementName SCRIPT = new ElementName("script", "script", // CPPONLY: NS_NewHTMLScriptElement, // CPPONLY: NS_NewSVGScriptElement, @@ -2215,10 +2215,6 @@ public final class ElementName // CPPONLY: NS_NewHTMLUnknownElement, // CPPONLY: NS_NewSVGUnknownElement, TreeBuilder.OTHER); - public static final ElementName SHADOW = new ElementName("shadow", "shadow", - // CPPONLY: NS_NewHTMLShadowElement, - // CPPONLY: NS_NewSVGUnknownElement, - TreeBuilder.OTHER); public static final ElementName VIEW = new ElementName("view", "view", // CPPONLY: NS_NewHTMLUnknownElement, // CPPONLY: NS_NewSVGViewElement, @@ -2277,16 +2273,16 @@ public final class ElementName FIELDSET, DATA, LI, - COMPLEXES, + CANVAS, QUOTIENT, PRE, ARTICLE, DIALOG, ARCTAN, LISTENER, - RATIONALS, + REALS, MROOT, - MATRIXROW, + MROW, GEQ, G, DD, @@ -2296,12 +2292,12 @@ public final class ElementName INTERVAL, MN, BR, - POWER, - MMULTISCRIPTS, - CONTENT, + NOTANUMBER, + MPRESCRIPTS, + CARTESIANPRODUCT, INTERSECT, RT, - TFOOT, + SCRIPT, APPLY, COS, MTD, @@ -2321,18 +2317,18 @@ public final class ElementName OPTION, MALIGNGROUP, FECOMPONENTTRANSFER, - MERROR, - VECTOR, - IMPLIES, - PRIMES, - APPLET, + MUNDEROVER, + SELECTOR, + EXISTS, + NATURALNUMBERS, + DT, EMPTYSET, FEPOINTLIGHT, LOWLIMIT, NOTSUBSET, PRODUCT, SELECT, - MENU, + VECTORPRODUCT, FECOLORMATRIX, INFINITY, BIG, @@ -2371,16 +2367,16 @@ public final class ElementName OPTGROUP, CENTER, FEGAUSSIANBLUR, - MOVER, - NOBR, - SOLIDCOLOR, - ADDRESS, - DETAILS, - MS, - NOFRAMES, - PLUS, - TIMES, - BASEFONT, + METER, + MLABELEDTR, + TR, + ARCCOS, + DEFS, + INTEGERS, + MINUS, + PROGRESS, + SEMANTICS, + ARCCOT, DETERMINANT, FONT_FACE_FORMAT, FEOFFSET, @@ -2393,8 +2389,8 @@ public final class ElementName PLAINTEXT, RADIALGRADIENT, SUBSET, - UPLIMIT, - FEDROPSHADOW, + TEXT, + SDEV, VIEW, ISINDEX, FEMORPHOLOGY, @@ -2472,25 +2468,25 @@ public final class ElementName FOOTER, HANDLER, MARKER, - MUNDEROVER, - MLABELEDTR, - NOTANUMBER, - TR, - SELECTOR, - ARCCOS, - CANVAS, - DEFS, - EXISTS, - INTEGERS, - MPRESCRIPTS, - MINUS, - NATURALNUMBERS, - PROGRESS, - REALS, - SEMANTICS, - DT, - ARCCOT, - CARTESIANPRODUCT, + MOVER, + MERROR, + NOBR, + POWER, + SOLIDCOLOR, + VECTOR, + ADDRESS, + COMPLEXES, + DETAILS, + IMPLIES, + MS, + MMULTISCRIPTS, + NOFRAMES, + PRIMES, + PLUS, + RATIONALS, + TIMES, + APPLET, + BASEFONT, GT, DATALIST, EQUIVALENT, @@ -2514,12 +2510,12 @@ public final class ElementName RECT, ROOT, SCALARPRODUCT, - SCRIPT, - TEXT, - VECTORPRODUCT, - SDEV, - MROW, - SHADOW, + SLOT, + TFOOT, + UPLIMIT, + MENU, + FEDROPSHADOW, + MATRIXROW, APPROX, FECONVOLVEMATRIX, MATRIX, @@ -2672,7 +2668,6 @@ public final class ElementName HEADER, OR, MUNDER, - METER, }; private final static int[] ELEMENT_HASHES = { 1909280949, @@ -2680,16 +2675,16 @@ public final class ElementName 2001349704, 1681770564, 1818230786, - 1983002201, + 1982935782, 2007257240, 58773795, 1747176599, 1782357526, 1897999926, 1970938456, - 1990969577, + 1990969429, 2005181733, - 2055515017, + 2055514836, 54061139, 62390273, 1730150402, @@ -2699,12 +2694,12 @@ public final class ElementName 1868641064, 1902641154, 1963982850, - 1973040373, - 1988486813, - 1999917383, + 1971981018, + 1988486811, + 1999745104, 2002882873, 2005925890, - 2008851557, + 2008340774, 2082727685, 51965171, 57200451, @@ -2724,18 +2719,18 @@ public final class ElementName 1905563974, 1938171179, 1967788867, - 1971628838, - 1976348214, - 1986140359, - 1989812374, - 1998724870, + 1971467002, + 1974775352, + 1984294038, + 1988972590, + 1998585858, 2000825752, 2001392796, 2004557976, 2005543977, 2006560839, 2008125638, - 2021937364, + 2009706573, 2068523853, 2087049448, 51434643, @@ -2774,16 +2769,16 @@ public final class ElementName 1939219752, 1966223078, 1968053806, - 1971466997, - 1971938532, - 1974771450, - 1982173479, - 1983633431, - 1986527234, - 1988763672, - 1990074116, - 1991909525, - 1999397992, + 1971465813, + 1971703386, + 1973420034, + 1982106678, + 1983533124, + 1986351224, + 1988502165, + 1990037800, + 1991350601, + 1998883894, 2000439531, 2001281328, 2001349736, @@ -2796,8 +2791,8 @@ public final class ElementName 2006896969, 2007781534, 2008165414, - 2009276567, - 2051837468, + 2008994116, + 2041712436, 2060065124, 2070023911, 2085266636, @@ -2875,25 +2870,25 @@ public final class ElementName 1967795958, 1968840263, 1971461414, - 1971467002, - 1971703386, - 1971981018, - 1973420034, - 1974775352, - 1982106678, - 1982935782, - 1983533124, - 1984294038, - 1986351224, - 1988486811, - 1988502165, - 1988972590, - 1990037800, - 1990969429, - 1991350601, - 1998585858, - 1998883894, - 1999745104, + 1971466997, + 1971628838, + 1971938532, + 1973040373, + 1974771450, + 1976348214, + 1982173479, + 1983002201, + 1983633431, + 1986140359, + 1986527234, + 1988486813, + 1988763672, + 1989812374, + 1990074116, + 1990969577, + 1991909525, + 1998724870, + 1999397992, 2000158722, 2000525512, 2000965834, @@ -2917,12 +2912,12 @@ public final class ElementName 2007601444, 2007803172, 2008133709, - 2008340774, - 2008994116, - 2009706573, - 2041712436, - 2055514836, - 2058653206, + 2008325940, + 2008851557, + 2009276567, + 2021937364, + 2051837468, + 2055515017, 2066000646, 2068523856, 2072193862, @@ -3075,6 +3070,5 @@ public final class ElementName 1968836118, 1970798594, 1971457766, - 1971465813, }; } diff --git a/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/MetaScanner.java b/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/MetaScanner.java index 9a3dc16b22..be7576ff0e 100644 --- a/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/MetaScanner.java +++ b/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/MetaScanner.java @@ -798,13 +798,13 @@ public abstract class MetaScanner { } if (contentIndex == CONTENT.length && content == null) { content = Portability.newStringFromBuffer(strBuf, 0, strBufLen - // CPPONLY: , treeBuilder + // CPPONLY: , treeBuilder, false ); return; } if (charsetIndex == CHARSET.length && charset == null) { charset = Portability.newStringFromBuffer(strBuf, 0, strBufLen - // CPPONLY: , treeBuilder + // CPPONLY: , treeBuilder, false ); return; } diff --git a/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/Portability.java b/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/Portability.java index 2b3f96625a..8f941ce018 100644 --- a/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/Portability.java +++ b/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/Portability.java @@ -42,7 +42,7 @@ public final class Portability { } public static String newStringFromBuffer(@NoLength char[] buf, int offset, int length - // CPPONLY: , TreeBuilder treeBuilder + // CPPONLY: , TreeBuilder treeBuilder, boolean maybeAtomize ) { return new String(buf, offset, length); } diff --git a/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/Tokenizer.java b/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/Tokenizer.java index 996bd9cebf..3d617fd01f 100644 --- a/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/Tokenizer.java +++ b/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/Tokenizer.java @@ -917,7 +917,7 @@ public class Tokenizer implements Locator { */ protected String strBufToString() { String str = Portability.newStringFromBuffer(strBuf, 0, strBufLen - // CPPONLY: , tokenHandler + // CPPONLY: , tokenHandler, !newAttributesEachTime && attributeName == AttributeName.CLASS ); clearStrBufAfterUse(); return str; diff --git a/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/TreeBuilder.java b/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/TreeBuilder.java index cc60f4c4bb..ef9576ee38 100644 --- a/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/TreeBuilder.java +++ b/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/TreeBuilder.java @@ -3337,7 +3337,7 @@ public abstract class TreeBuilder<T> implements TokenHandler, } charset = Portability.newStringFromBuffer(buffer, start, end - start - // CPPONLY: , tb + // CPPONLY: , tb, false ); } return charset; diff --git a/parser/html/nsHtml5AtomList.h b/parser/html/nsHtml5AtomList.h index 71617e4ea0..0728f439b6 100644 --- a/parser/html/nsHtml5AtomList.h +++ b/parser/html/nsHtml5AtomList.h @@ -1049,6 +1049,7 @@ HTML5_ATOM(root, "root") HTML5_ATOM(select, "select") HTML5_ATOM(scalarproduct, "scalarproduct") HTML5_ATOM(subset, "subset") +HTML5_ATOM(slot, "slot") HTML5_ATOM(script, "script") HTML5_ATOM(tfoot, "tfoot") HTML5_ATOM(uplimit, "uplimit") @@ -1059,7 +1060,6 @@ HTML5_ATOM(fedropshadow, "fedropshadow") HTML5_ATOM(feDropShadow, "feDropShadow") HTML5_ATOM(mrow, "mrow") HTML5_ATOM(matrixrow, "matrixrow") -HTML5_ATOM(shadow, "shadow") HTML5_ATOM(view, "view") HTML5_ATOM(approx, "approx") HTML5_ATOM(fecolormatrix, "fecolormatrix") diff --git a/parser/html/nsHtml5ElementName.cpp b/parser/html/nsHtml5ElementName.cpp index 84a0560124..ecdfb102d4 100644 --- a/parser/html/nsHtml5ElementName.cpp +++ b/parser/html/nsHtml5ElementName.cpp @@ -416,7 +416,6 @@ nsHtml5ElementName* nsHtml5ElementName::ELT_APPLET = nullptr; nsHtml5ElementName* nsHtml5ElementName::ELT_ARCCOT = nullptr; nsHtml5ElementName* nsHtml5ElementName::ELT_BASEFONT = nullptr; nsHtml5ElementName* nsHtml5ElementName::ELT_CARTESIANPRODUCT = nullptr; -nsHtml5ElementName* nsHtml5ElementName::ELT_CONTENT = nullptr; nsHtml5ElementName* nsHtml5ElementName::ELT_GT = nullptr; nsHtml5ElementName* nsHtml5ElementName::ELT_DETERMINANT = nullptr; nsHtml5ElementName* nsHtml5ElementName::ELT_DATALIST = nullptr; @@ -463,6 +462,7 @@ nsHtml5ElementName* nsHtml5ElementName::ELT_ROOT = nullptr; nsHtml5ElementName* nsHtml5ElementName::ELT_SELECT = nullptr; nsHtml5ElementName* nsHtml5ElementName::ELT_SCALARPRODUCT = nullptr; nsHtml5ElementName* nsHtml5ElementName::ELT_SUBSET = nullptr; +nsHtml5ElementName* nsHtml5ElementName::ELT_SLOT = nullptr; nsHtml5ElementName* nsHtml5ElementName::ELT_SCRIPT = nullptr; nsHtml5ElementName* nsHtml5ElementName::ELT_TFOOT = nullptr; nsHtml5ElementName* nsHtml5ElementName::ELT_TEXT = nullptr; @@ -473,7 +473,6 @@ nsHtml5ElementName* nsHtml5ElementName::ELT_SDEV = nullptr; nsHtml5ElementName* nsHtml5ElementName::ELT_FEDROPSHADOW = nullptr; nsHtml5ElementName* nsHtml5ElementName::ELT_MROW = nullptr; nsHtml5ElementName* nsHtml5ElementName::ELT_MATRIXROW = nullptr; -nsHtml5ElementName* nsHtml5ElementName::ELT_SHADOW = nullptr; nsHtml5ElementName* nsHtml5ElementName::ELT_VIEW = nullptr; nsHtml5ElementName* nsHtml5ElementName::ELT_APPROX = nullptr; nsHtml5ElementName* nsHtml5ElementName::ELT_FECOLORMATRIX = nullptr; @@ -488,7 +487,7 @@ nsHtml5ElementName* nsHtml5ElementName::ELT_RUBY = nullptr; nsHtml5ElementName* nsHtml5ElementName::ELT_SUMMARY = nullptr; nsHtml5ElementName* nsHtml5ElementName::ELT_TBODY = nullptr; nsHtml5ElementName** nsHtml5ElementName::ELEMENT_NAMES = 0; -static int32_t const ELEMENT_HASHES_DATA[] = { 1909280949, 1753057319, 2001349704, 1681770564, 1818230786, 1983002201, 2007257240, 58773795, 1747176599, 1782357526, 1897999926, 1970938456, 1990969577, 2005181733, 2055515017, 54061139, 62390273, 1730150402, 1749395095, 1756625221, 1798693940, 1868641064, 1902641154, 1963982850, 1973040373, 1988486813, 1999917383, 2002882873, 2005925890, 2008851557, 2082727685, 51965171, 57200451, 60350803, 69730305, 1703292116, 1733890180, 1748355193, 1749813541, 1754634617, 1763839627, 1797540167, 1805647874, 1857622310, 1881498736, 1899272519, 1905563974, 1938171179, 1967788867, 1971628838, 1976348214, 1986140359, 1989812374, 1998724870, 2000825752, 2001392796, 2004557976, 2005543977, 2006560839, 2008125638, 2021937364, 2068523853, 2087049448, 51434643, 52488851, 56151587, 57210387, 59826259, 60354131, 63438849, 926941186, 1686489160, 1715300574, 1732381397, 1737099991, 1748100148, 1748642422, 1749715159, 1751288021, 1753467414, 1755158905, 1757259017, 1771722827, 1786534215, 1797645367, 1803929812, 1807501636, 1853642948, 1865773108, 1873350948, 1887579800, 1898223949, 1900544002, 1904285766, 1907435316, 1925844629, 1939219752, 1966223078, 1968053806, 1971466997, 1971938532, 1974771450, 1982173479, 1983633431, 1986527234, 1988763672, 1990074116, 1991909525, 1999397992, 2000439531, 2001281328, 2001349736, 2001495140, 2003183333, 2004719812, 2005279787, 2005719336, 2006036556, 2006896969, 2007781534, 2008165414, 2009276567, 2051837468, 2060065124, 2070023911, 2085266636, 2092255447, 50910499, 51957043, 52485715, 53012355, 55110883, 56680499, 57206291, 57732851, 59768833, 60345427, 60352083, 61395251, 62973651, 67633153, 893386754, 960495618, 1682547543, 1689922072, 1713515574, 1716349149, 1731545140, 1733076167, 1736576231, 1740181637, 1747814436, 1748228205, 1748607578, 1748879564, 1749656156, 1749801286, 1749917205, 1751493207, 1753343188, 1753588936, 1755076808, 1756474198, 1757146773, 1757293380, 1766632184, 1773808452, 1783388497, 1797361975, 1797585096, 1798677556, 1803876550, 1805233752, 1806806678, 1813512194, 1818755074, 1854228698, 1864368130, 1867237670, 1870268949, 1874102998, 1881669634, 1889085973, 1898223945, 1898971138, 1899694294, 1901940917, 1903761465, 1904515399, 1906135367, 1907959605, 1919418370, 1934172497, 1938173140, 1941221172, 1965334268, 1967128578, 1967795958, 1968840263, 1971461414, 1971467002, 1971703386, 1971981018, 1973420034, 1974775352, 1982106678, 1982935782, 1983533124, 1984294038, 1986351224, 1988486811, 1988502165, 1988972590, 1990037800, 1990969429, 1991350601, 1998585858, 1998883894, 1999745104, 2000158722, 2000525512, 2000965834, 2001309869, 2001349720, 2001392795, 2001392798, 2002780162, 2003062853, 2004557973, 2004635806, 2005160150, 2005231925, 2005324101, 2005543979, 2005766372, 2006028454, 2006329158, 2006592552, 2006974466, 2007601444, 2007803172, 2008133709, 2008340774, 2008994116, 2009706573, 2041712436, 2055514836, 2058653206, 2066000646, 2068523856, 2072193862, 2083120164, 2087012585, 2091479332, 2092557349, 50908899, 50916387, 51438659, 51961587, 51965683, 52486755, 52490899, 54054451, 55104723, 55111395, 56677619, 56682579, 57205395, 57207619, 57731155, 57733651, 59244545, 59821379, 60345171, 60347747, 60351123, 60352339, 60875283, 61925907, 62450211, 62974707, 67108865, 68681729, 876609538, 910163970, 943718402, 1679960596, 1682186266, 1685703382, 1686491348, 1699324759, 1703936002, 1713736758, 1715310660, 1719741029, 1730965751, 1732069431, 1733054663, 1733372532, 1736200310, 1736576583, 1738539010, 1747048757, 1747306711, 1747838298, 1748225318, 1748346119, 1748359220, 1748621670, 1748846791, 1749272732, 1749649513, 1749673195, 1749723735, 1749813486, 1749905526, 1749932347, 1751386406, 1752979652, 1753319686, 1753362711, 1753479494, 1754031332, 1754894485, 1755148615, 1756098852, 1756600614, 1757137429, 1757157700, 1757268168, 1758044696, 1765431364, 1766992520, 1773295687, 1781815495, 1783210839, 1783388498, 1790207270, 1797368887, 1797544247, 1797628983, 1798417460, 1798686984, 1800730821, 1803876557, 1803929861, 1805502724, 1806799156, 1806981428, 1807599880, 1817013469, 1818700314, 1820327938, 1854228692, 1854245076, 1857653029, 1865714391, 1867061545, 1868312196, 1870135298, 1873281026, 1874053333, 1881288348, 1881613047, 1884120164, 1887743720, 1897398274, 1898130486, 1898223946, 1898753862, 1899170008, 1899272521, 1899796819, 1900845386, 1902116866, 1903302038, 1904283860, 1904412884, 1904946933, 1906087319, 1907085604, 1907661127, 1908709605, 1914900309, 1925049415, 1932928296, 1935549734, 1938172967, 1938817026, 1941178676, 1948778498, 1965115924, 1965634084, 1966386470, 1967760215, 1967795910, 1967957189, 1968836118, 1970798594, 1971457766, 1971465813 }; +static int32_t const ELEMENT_HASHES_DATA[] = { 1909280949, 1753057319, 2001349704, 1681770564, 1818230786, 1982935782, 2007257240, 58773795, 1747176599, 1782357526, 1897999926, 1970938456, 1990969429, 2005181733, 2055514836, 54061139, 62390273, 1730150402, 1749395095, 1756625221, 1798693940, 1868641064, 1902641154, 1963982850, 1971981018, 1988486811, 1999745104, 2002882873, 2005925890, 2008340774, 2082727685, 51965171, 57200451, 60350803, 69730305, 1703292116, 1733890180, 1748355193, 1749813541, 1754634617, 1763839627, 1797540167, 1805647874, 1857622310, 1881498736, 1899272519, 1905563974, 1938171179, 1967788867, 1971467002, 1974775352, 1984294038, 1988972590, 1998585858, 2000825752, 2001392796, 2004557976, 2005543977, 2006560839, 2008125638, 2009706573, 2068523853, 2087049448, 51434643, 52488851, 56151587, 57210387, 59826259, 60354131, 63438849, 926941186, 1686489160, 1715300574, 1732381397, 1737099991, 1748100148, 1748642422, 1749715159, 1751288021, 1753467414, 1755158905, 1757259017, 1771722827, 1786534215, 1797645367, 1803929812, 1807501636, 1853642948, 1865773108, 1873350948, 1887579800, 1898223949, 1900544002, 1904285766, 1907435316, 1925844629, 1939219752, 1966223078, 1968053806, 1971465813, 1971703386, 1973420034, 1982106678, 1983533124, 1986351224, 1988502165, 1990037800, 1991350601, 1998883894, 2000439531, 2001281328, 2001349736, 2001495140, 2003183333, 2004719812, 2005279787, 2005719336, 2006036556, 2006896969, 2007781534, 2008165414, 2008994116, 2041712436, 2060065124, 2070023911, 2085266636, 2092255447, 50910499, 51957043, 52485715, 53012355, 55110883, 56680499, 57206291, 57732851, 59768833, 60345427, 60352083, 61395251, 62973651, 67633153, 893386754, 960495618, 1682547543, 1689922072, 1713515574, 1716349149, 1731545140, 1733076167, 1736576231, 1740181637, 1747814436, 1748228205, 1748607578, 1748879564, 1749656156, 1749801286, 1749917205, 1751493207, 1753343188, 1753588936, 1755076808, 1756474198, 1757146773, 1757293380, 1766632184, 1773808452, 1783388497, 1797361975, 1797585096, 1798677556, 1803876550, 1805233752, 1806806678, 1813512194, 1818755074, 1854228698, 1864368130, 1867237670, 1870268949, 1874102998, 1881669634, 1889085973, 1898223945, 1898971138, 1899694294, 1901940917, 1903761465, 1904515399, 1906135367, 1907959605, 1919418370, 1934172497, 1938173140, 1941221172, 1965334268, 1967128578, 1967795958, 1968840263, 1971461414, 1971466997, 1971628838, 1971938532, 1973040373, 1974771450, 1976348214, 1982173479, 1983002201, 1983633431, 1986140359, 1986527234, 1988486813, 1988763672, 1989812374, 1990074116, 1990969577, 1991909525, 1998724870, 1999397992, 2000158722, 2000525512, 2000965834, 2001309869, 2001349720, 2001392795, 2001392798, 2002780162, 2003062853, 2004557973, 2004635806, 2005160150, 2005231925, 2005324101, 2005543979, 2005766372, 2006028454, 2006329158, 2006592552, 2006974466, 2007601444, 2007803172, 2008133709, 2008325940, 2008851557, 2009276567, 2021937364, 2051837468, 2055515017, 2066000646, 2068523856, 2072193862, 2083120164, 2087012585, 2091479332, 2092557349, 50908899, 50916387, 51438659, 51961587, 51965683, 52486755, 52490899, 54054451, 55104723, 55111395, 56677619, 56682579, 57205395, 57207619, 57731155, 57733651, 59244545, 59821379, 60345171, 60347747, 60351123, 60352339, 60875283, 61925907, 62450211, 62974707, 67108865, 68681729, 876609538, 910163970, 943718402, 1679960596, 1682186266, 1685703382, 1686491348, 1699324759, 1703936002, 1713736758, 1715310660, 1719741029, 1730965751, 1732069431, 1733054663, 1733372532, 1736200310, 1736576583, 1738539010, 1747048757, 1747306711, 1747838298, 1748225318, 1748346119, 1748359220, 1748621670, 1748846791, 1749272732, 1749649513, 1749673195, 1749723735, 1749813486, 1749905526, 1749932347, 1751386406, 1752979652, 1753319686, 1753362711, 1753479494, 1754031332, 1754894485, 1755148615, 1756098852, 1756600614, 1757137429, 1757157700, 1757268168, 1758044696, 1765431364, 1766992520, 1773295687, 1781815495, 1783210839, 1783388498, 1790207270, 1797368887, 1797544247, 1797628983, 1798417460, 1798686984, 1800730821, 1803876557, 1803929861, 1805502724, 1806799156, 1806981428, 1807599880, 1817013469, 1818700314, 1820327938, 1854228692, 1854245076, 1857653029, 1865714391, 1867061545, 1868312196, 1870135298, 1873281026, 1874053333, 1881288348, 1881613047, 1884120164, 1887743720, 1897398274, 1898130486, 1898223946, 1898753862, 1899170008, 1899272521, 1899796819, 1900845386, 1902116866, 1903302038, 1904283860, 1904412884, 1904946933, 1906087319, 1907085604, 1907661127, 1908709605, 1914900309, 1925049415, 1932928296, 1935549734, 1938172967, 1938817026, 1941178676, 1948778498, 1965115924, 1965634084, 1966386470, 1967760215, 1967795910, 1967957189, 1968836118, 1970798594, 1971457766 }; staticJArray<int32_t,int32_t> nsHtml5ElementName::ELEMENT_HASHES = { ELEMENT_HASHES_DATA, MOZ_ARRAY_LENGTH(ELEMENT_HASHES_DATA) }; void nsHtml5ElementName::initializeStatics() @@ -824,7 +823,6 @@ nsHtml5ElementName::initializeStatics() ELT_ARCCOT = new nsHtml5ElementName(nsHtml5Atoms::arccot, nsHtml5Atoms::arccot, NS_NewHTMLUnknownElement, NS_NewSVGUnknownElement, nsHtml5TreeBuilder::OTHER); ELT_BASEFONT = new nsHtml5ElementName(nsHtml5Atoms::basefont, nsHtml5Atoms::basefont, NS_NewHTMLElement, NS_NewSVGUnknownElement, nsHtml5TreeBuilder::LINK_OR_BASEFONT_OR_BGSOUND | SPECIAL); ELT_CARTESIANPRODUCT = new nsHtml5ElementName(nsHtml5Atoms::cartesianproduct, nsHtml5Atoms::cartesianproduct, NS_NewHTMLUnknownElement, NS_NewSVGUnknownElement, nsHtml5TreeBuilder::OTHER); - ELT_CONTENT = new nsHtml5ElementName(nsHtml5Atoms::content, nsHtml5Atoms::content, NS_NewHTMLContentElement, NS_NewSVGUnknownElement, nsHtml5TreeBuilder::OTHER); ELT_GT = new nsHtml5ElementName(nsHtml5Atoms::gt, nsHtml5Atoms::gt, NS_NewHTMLUnknownElement, NS_NewSVGUnknownElement, nsHtml5TreeBuilder::OTHER); ELT_DETERMINANT = new nsHtml5ElementName(nsHtml5Atoms::determinant, nsHtml5Atoms::determinant, NS_NewHTMLUnknownElement, NS_NewSVGUnknownElement, nsHtml5TreeBuilder::OTHER); ELT_DATALIST = new nsHtml5ElementName(nsHtml5Atoms::datalist, nsHtml5Atoms::datalist, NS_NewHTMLDataListElement, NS_NewSVGUnknownElement, nsHtml5TreeBuilder::OTHER); @@ -871,6 +869,7 @@ nsHtml5ElementName::initializeStatics() ELT_SELECT = new nsHtml5ElementName(nsHtml5Atoms::select, nsHtml5Atoms::select, NS_NewHTMLSelectElement, NS_NewSVGUnknownElement, nsHtml5TreeBuilder::SELECT | SPECIAL); ELT_SCALARPRODUCT = new nsHtml5ElementName(nsHtml5Atoms::scalarproduct, nsHtml5Atoms::scalarproduct, NS_NewHTMLUnknownElement, NS_NewSVGUnknownElement, nsHtml5TreeBuilder::OTHER); ELT_SUBSET = new nsHtml5ElementName(nsHtml5Atoms::subset, nsHtml5Atoms::subset, NS_NewHTMLUnknownElement, NS_NewSVGUnknownElement, nsHtml5TreeBuilder::OTHER); + ELT_SLOT = new nsHtml5ElementName(nsHtml5Atoms::slot, nsHtml5Atoms::slot, NS_NewHTMLSlotElement, NS_NewSVGUnknownElement, nsHtml5TreeBuilder::OTHER); ELT_SCRIPT = new nsHtml5ElementName(nsHtml5Atoms::script, nsHtml5Atoms::script, NS_NewHTMLScriptElement, NS_NewSVGScriptElement, nsHtml5TreeBuilder::SCRIPT | SPECIAL); ELT_TFOOT = new nsHtml5ElementName(nsHtml5Atoms::tfoot, nsHtml5Atoms::tfoot, NS_NewHTMLTableSectionElement, NS_NewSVGUnknownElement, nsHtml5TreeBuilder::TBODY_OR_THEAD_OR_TFOOT | SPECIAL | FOSTER_PARENTING | OPTIONAL_END_TAG); ELT_TEXT = new nsHtml5ElementName(nsHtml5Atoms::text, nsHtml5Atoms::text, NS_NewHTMLUnknownElement, NS_NewSVGTextElement, nsHtml5TreeBuilder::OTHER); @@ -881,7 +880,6 @@ nsHtml5ElementName::initializeStatics() ELT_FEDROPSHADOW = new nsHtml5ElementName(nsHtml5Atoms::fedropshadow, nsHtml5Atoms::feDropShadow, NS_NewHTMLUnknownElement, NS_NewSVGFEDropShadowElement, nsHtml5TreeBuilder::OTHER); ELT_MROW = new nsHtml5ElementName(nsHtml5Atoms::mrow, nsHtml5Atoms::mrow, NS_NewHTMLUnknownElement, NS_NewSVGUnknownElement, nsHtml5TreeBuilder::OTHER); ELT_MATRIXROW = new nsHtml5ElementName(nsHtml5Atoms::matrixrow, nsHtml5Atoms::matrixrow, NS_NewHTMLUnknownElement, NS_NewSVGUnknownElement, nsHtml5TreeBuilder::OTHER); - ELT_SHADOW = new nsHtml5ElementName(nsHtml5Atoms::shadow, nsHtml5Atoms::shadow, NS_NewHTMLShadowElement, NS_NewSVGUnknownElement, nsHtml5TreeBuilder::OTHER); ELT_VIEW = new nsHtml5ElementName(nsHtml5Atoms::view, nsHtml5Atoms::view, NS_NewHTMLUnknownElement, NS_NewSVGViewElement, nsHtml5TreeBuilder::OTHER); ELT_APPROX = new nsHtml5ElementName(nsHtml5Atoms::approx, nsHtml5Atoms::approx, NS_NewHTMLUnknownElement, NS_NewSVGUnknownElement, nsHtml5TreeBuilder::OTHER); ELT_FECOLORMATRIX = new nsHtml5ElementName(nsHtml5Atoms::fecolormatrix, nsHtml5Atoms::feColorMatrix, NS_NewHTMLUnknownElement, NS_NewSVGFEColorMatrixElement, nsHtml5TreeBuilder::OTHER); @@ -895,22 +893,22 @@ nsHtml5ElementName::initializeStatics() ELT_RUBY = new nsHtml5ElementName(nsHtml5Atoms::ruby, nsHtml5Atoms::ruby, NS_NewHTMLElement, NS_NewSVGUnknownElement, nsHtml5TreeBuilder::RUBY_OR_SPAN_OR_SUB_OR_SUP_OR_VAR); ELT_SUMMARY = new nsHtml5ElementName(nsHtml5Atoms::summary, nsHtml5Atoms::summary, NS_NewHTMLSummaryElement, NS_NewSVGUnknownElement, nsHtml5TreeBuilder::ADDRESS_OR_ARTICLE_OR_ASIDE_OR_DETAILS_OR_DIALOG_OR_DIR_OR_FIGCAPTION_OR_FIGURE_OR_FOOTER_OR_HEADER_OR_HGROUP_OR_MAIN_OR_NAV_OR_SECTION_OR_SUMMARY | SPECIAL); ELT_TBODY = new nsHtml5ElementName(nsHtml5Atoms::tbody, nsHtml5Atoms::tbody, NS_NewHTMLTableSectionElement, NS_NewSVGUnknownElement, nsHtml5TreeBuilder::TBODY_OR_THEAD_OR_TFOOT | SPECIAL | FOSTER_PARENTING | OPTIONAL_END_TAG); - ELEMENT_NAMES = new nsHtml5ElementName*[401]; + ELEMENT_NAMES = new nsHtml5ElementName*[400]; ELEMENT_NAMES[0] = ELT_VKERN; ELEMENT_NAMES[1] = ELT_LOGBASE; ELEMENT_NAMES[2] = ELT_FIELDSET; ELEMENT_NAMES[3] = ELT_DATA; ELEMENT_NAMES[4] = ELT_LI; - ELEMENT_NAMES[5] = ELT_COMPLEXES; + ELEMENT_NAMES[5] = ELT_CANVAS; ELEMENT_NAMES[6] = ELT_QUOTIENT; ELEMENT_NAMES[7] = ELT_PRE; ELEMENT_NAMES[8] = ELT_ARTICLE; ELEMENT_NAMES[9] = ELT_DIALOG; ELEMENT_NAMES[10] = ELT_ARCTAN; ELEMENT_NAMES[11] = ELT_LISTENER; - ELEMENT_NAMES[12] = ELT_RATIONALS; + ELEMENT_NAMES[12] = ELT_REALS; ELEMENT_NAMES[13] = ELT_MROOT; - ELEMENT_NAMES[14] = ELT_MATRIXROW; + ELEMENT_NAMES[14] = ELT_MROW; ELEMENT_NAMES[15] = ELT_GEQ; ELEMENT_NAMES[16] = ELT_G; ELEMENT_NAMES[17] = ELT_DD; @@ -920,12 +918,12 @@ nsHtml5ElementName::initializeStatics() ELEMENT_NAMES[21] = ELT_INTERVAL; ELEMENT_NAMES[22] = ELT_MN; ELEMENT_NAMES[23] = ELT_BR; - ELEMENT_NAMES[24] = ELT_POWER; - ELEMENT_NAMES[25] = ELT_MMULTISCRIPTS; - ELEMENT_NAMES[26] = ELT_CONTENT; + ELEMENT_NAMES[24] = ELT_NOTANUMBER; + ELEMENT_NAMES[25] = ELT_MPRESCRIPTS; + ELEMENT_NAMES[26] = ELT_CARTESIANPRODUCT; ELEMENT_NAMES[27] = ELT_INTERSECT; ELEMENT_NAMES[28] = ELT_RT; - ELEMENT_NAMES[29] = ELT_TFOOT; + ELEMENT_NAMES[29] = ELT_SCRIPT; ELEMENT_NAMES[30] = ELT_APPLY; ELEMENT_NAMES[31] = ELT_COS; ELEMENT_NAMES[32] = ELT_MTD; @@ -945,18 +943,18 @@ nsHtml5ElementName::initializeStatics() ELEMENT_NAMES[46] = ELT_OPTION; ELEMENT_NAMES[47] = ELT_MALIGNGROUP; ELEMENT_NAMES[48] = ELT_FECOMPONENTTRANSFER; - ELEMENT_NAMES[49] = ELT_MERROR; - ELEMENT_NAMES[50] = ELT_VECTOR; - ELEMENT_NAMES[51] = ELT_IMPLIES; - ELEMENT_NAMES[52] = ELT_PRIMES; - ELEMENT_NAMES[53] = ELT_APPLET; + ELEMENT_NAMES[49] = ELT_MUNDEROVER; + ELEMENT_NAMES[50] = ELT_SELECTOR; + ELEMENT_NAMES[51] = ELT_EXISTS; + ELEMENT_NAMES[52] = ELT_NATURALNUMBERS; + ELEMENT_NAMES[53] = ELT_DT; ELEMENT_NAMES[54] = ELT_EMPTYSET; ELEMENT_NAMES[55] = ELT_FEPOINTLIGHT; ELEMENT_NAMES[56] = ELT_LOWLIMIT; ELEMENT_NAMES[57] = ELT_NOTSUBSET; ELEMENT_NAMES[58] = ELT_PRODUCT; ELEMENT_NAMES[59] = ELT_SELECT; - ELEMENT_NAMES[60] = ELT_MENU; + ELEMENT_NAMES[60] = ELT_VECTORPRODUCT; ELEMENT_NAMES[61] = ELT_FECOLORMATRIX; ELEMENT_NAMES[62] = ELT_INFINITY; ELEMENT_NAMES[63] = ELT_BIG; @@ -995,16 +993,16 @@ nsHtml5ElementName::initializeStatics() ELEMENT_NAMES[96] = ELT_OPTGROUP; ELEMENT_NAMES[97] = ELT_CENTER; ELEMENT_NAMES[98] = ELT_FEGAUSSIANBLUR; - ELEMENT_NAMES[99] = ELT_MOVER; - ELEMENT_NAMES[100] = ELT_NOBR; - ELEMENT_NAMES[101] = ELT_SOLIDCOLOR; - ELEMENT_NAMES[102] = ELT_ADDRESS; - ELEMENT_NAMES[103] = ELT_DETAILS; - ELEMENT_NAMES[104] = ELT_MS; - ELEMENT_NAMES[105] = ELT_NOFRAMES; - ELEMENT_NAMES[106] = ELT_PLUS; - ELEMENT_NAMES[107] = ELT_TIMES; - ELEMENT_NAMES[108] = ELT_BASEFONT; + ELEMENT_NAMES[99] = ELT_METER; + ELEMENT_NAMES[100] = ELT_MLABELEDTR; + ELEMENT_NAMES[101] = ELT_TR; + ELEMENT_NAMES[102] = ELT_ARCCOS; + ELEMENT_NAMES[103] = ELT_DEFS; + ELEMENT_NAMES[104] = ELT_INTEGERS; + ELEMENT_NAMES[105] = ELT_MINUS; + ELEMENT_NAMES[106] = ELT_PROGRESS; + ELEMENT_NAMES[107] = ELT_SEMANTICS; + ELEMENT_NAMES[108] = ELT_ARCCOT; ELEMENT_NAMES[109] = ELT_DETERMINANT; ELEMENT_NAMES[110] = ELT_FONT_FACE_FORMAT; ELEMENT_NAMES[111] = ELT_FEOFFSET; @@ -1017,8 +1015,8 @@ nsHtml5ElementName::initializeStatics() ELEMENT_NAMES[118] = ELT_PLAINTEXT; ELEMENT_NAMES[119] = ELT_RADIALGRADIENT; ELEMENT_NAMES[120] = ELT_SUBSET; - ELEMENT_NAMES[121] = ELT_UPLIMIT; - ELEMENT_NAMES[122] = ELT_FEDROPSHADOW; + ELEMENT_NAMES[121] = ELT_TEXT; + ELEMENT_NAMES[122] = ELT_SDEV; ELEMENT_NAMES[123] = ELT_VIEW; ELEMENT_NAMES[124] = ELT_ISINDEX; ELEMENT_NAMES[125] = ELT_FEMORPHOLOGY; @@ -1096,25 +1094,25 @@ nsHtml5ElementName::initializeStatics() ELEMENT_NAMES[197] = ELT_FOOTER; ELEMENT_NAMES[198] = ELT_HANDLER; ELEMENT_NAMES[199] = ELT_MARKER; - ELEMENT_NAMES[200] = ELT_MUNDEROVER; - ELEMENT_NAMES[201] = ELT_MLABELEDTR; - ELEMENT_NAMES[202] = ELT_NOTANUMBER; - ELEMENT_NAMES[203] = ELT_TR; - ELEMENT_NAMES[204] = ELT_SELECTOR; - ELEMENT_NAMES[205] = ELT_ARCCOS; - ELEMENT_NAMES[206] = ELT_CANVAS; - ELEMENT_NAMES[207] = ELT_DEFS; - ELEMENT_NAMES[208] = ELT_EXISTS; - ELEMENT_NAMES[209] = ELT_INTEGERS; - ELEMENT_NAMES[210] = ELT_MPRESCRIPTS; - ELEMENT_NAMES[211] = ELT_MINUS; - ELEMENT_NAMES[212] = ELT_NATURALNUMBERS; - ELEMENT_NAMES[213] = ELT_PROGRESS; - ELEMENT_NAMES[214] = ELT_REALS; - ELEMENT_NAMES[215] = ELT_SEMANTICS; - ELEMENT_NAMES[216] = ELT_DT; - ELEMENT_NAMES[217] = ELT_ARCCOT; - ELEMENT_NAMES[218] = ELT_CARTESIANPRODUCT; + ELEMENT_NAMES[200] = ELT_MOVER; + ELEMENT_NAMES[201] = ELT_MERROR; + ELEMENT_NAMES[202] = ELT_NOBR; + ELEMENT_NAMES[203] = ELT_POWER; + ELEMENT_NAMES[204] = ELT_SOLIDCOLOR; + ELEMENT_NAMES[205] = ELT_VECTOR; + ELEMENT_NAMES[206] = ELT_ADDRESS; + ELEMENT_NAMES[207] = ELT_COMPLEXES; + ELEMENT_NAMES[208] = ELT_DETAILS; + ELEMENT_NAMES[209] = ELT_IMPLIES; + ELEMENT_NAMES[210] = ELT_MS; + ELEMENT_NAMES[211] = ELT_MMULTISCRIPTS; + ELEMENT_NAMES[212] = ELT_NOFRAMES; + ELEMENT_NAMES[213] = ELT_PRIMES; + ELEMENT_NAMES[214] = ELT_PLUS; + ELEMENT_NAMES[215] = ELT_RATIONALS; + ELEMENT_NAMES[216] = ELT_TIMES; + ELEMENT_NAMES[217] = ELT_APPLET; + ELEMENT_NAMES[218] = ELT_BASEFONT; ELEMENT_NAMES[219] = ELT_GT; ELEMENT_NAMES[220] = ELT_DATALIST; ELEMENT_NAMES[221] = ELT_EQUIVALENT; @@ -1138,12 +1136,12 @@ nsHtml5ElementName::initializeStatics() ELEMENT_NAMES[239] = ELT_RECT; ELEMENT_NAMES[240] = ELT_ROOT; ELEMENT_NAMES[241] = ELT_SCALARPRODUCT; - ELEMENT_NAMES[242] = ELT_SCRIPT; - ELEMENT_NAMES[243] = ELT_TEXT; - ELEMENT_NAMES[244] = ELT_VECTORPRODUCT; - ELEMENT_NAMES[245] = ELT_SDEV; - ELEMENT_NAMES[246] = ELT_MROW; - ELEMENT_NAMES[247] = ELT_SHADOW; + ELEMENT_NAMES[242] = ELT_SLOT; + ELEMENT_NAMES[243] = ELT_TFOOT; + ELEMENT_NAMES[244] = ELT_UPLIMIT; + ELEMENT_NAMES[245] = ELT_MENU; + ELEMENT_NAMES[246] = ELT_FEDROPSHADOW; + ELEMENT_NAMES[247] = ELT_MATRIXROW; ELEMENT_NAMES[248] = ELT_APPROX; ELEMENT_NAMES[249] = ELT_FECONVOLVEMATRIX; ELEMENT_NAMES[250] = ELT_MATRIX; @@ -1296,7 +1294,6 @@ nsHtml5ElementName::initializeStatics() ELEMENT_NAMES[397] = ELT_HEADER; ELEMENT_NAMES[398] = ELT_OR; ELEMENT_NAMES[399] = ELT_MUNDER; - ELEMENT_NAMES[400] = ELT_METER; } void @@ -1633,7 +1630,6 @@ nsHtml5ElementName::releaseStatics() delete ELT_ARCCOT; delete ELT_BASEFONT; delete ELT_CARTESIANPRODUCT; - delete ELT_CONTENT; delete ELT_GT; delete ELT_DETERMINANT; delete ELT_DATALIST; @@ -1680,6 +1676,7 @@ nsHtml5ElementName::releaseStatics() delete ELT_SELECT; delete ELT_SCALARPRODUCT; delete ELT_SUBSET; + delete ELT_SLOT; delete ELT_SCRIPT; delete ELT_TFOOT; delete ELT_TEXT; @@ -1690,7 +1687,6 @@ nsHtml5ElementName::releaseStatics() delete ELT_FEDROPSHADOW; delete ELT_MROW; delete ELT_MATRIXROW; - delete ELT_SHADOW; delete ELT_VIEW; delete ELT_APPROX; delete ELT_FECOLORMATRIX; diff --git a/parser/html/nsHtml5ElementName.h b/parser/html/nsHtml5ElementName.h index 5236d18873..131b9e6281 100644 --- a/parser/html/nsHtml5ElementName.h +++ b/parser/html/nsHtml5ElementName.h @@ -539,7 +539,6 @@ class nsHtml5ElementName static nsHtml5ElementName* ELT_ARCCOT; static nsHtml5ElementName* ELT_BASEFONT; static nsHtml5ElementName* ELT_CARTESIANPRODUCT; - static nsHtml5ElementName* ELT_CONTENT; static nsHtml5ElementName* ELT_GT; static nsHtml5ElementName* ELT_DETERMINANT; static nsHtml5ElementName* ELT_DATALIST; @@ -586,6 +585,7 @@ class nsHtml5ElementName static nsHtml5ElementName* ELT_SELECT; static nsHtml5ElementName* ELT_SCALARPRODUCT; static nsHtml5ElementName* ELT_SUBSET; + static nsHtml5ElementName* ELT_SLOT; static nsHtml5ElementName* ELT_SCRIPT; static nsHtml5ElementName* ELT_TFOOT; static nsHtml5ElementName* ELT_TEXT; @@ -596,7 +596,6 @@ class nsHtml5ElementName static nsHtml5ElementName* ELT_FEDROPSHADOW; static nsHtml5ElementName* ELT_MROW; static nsHtml5ElementName* ELT_MATRIXROW; - static nsHtml5ElementName* ELT_SHADOW; static nsHtml5ElementName* ELT_VIEW; static nsHtml5ElementName* ELT_APPROX; static nsHtml5ElementName* ELT_FECOLORMATRIX; diff --git a/parser/html/nsHtml5MetaScanner.cpp b/parser/html/nsHtml5MetaScanner.cpp index 07d81c1a12..9fa3d3a705 100644 --- a/parser/html/nsHtml5MetaScanner.cpp +++ b/parser/html/nsHtml5MetaScanner.cpp @@ -757,11 +757,11 @@ nsHtml5MetaScanner::handleAttributeValue() return; } if (contentIndex == CONTENT.length && !content) { - content = nsHtml5Portability::newStringFromBuffer(strBuf, 0, strBufLen, treeBuilder); + content = nsHtml5Portability::newStringFromBuffer(strBuf, 0, strBufLen, treeBuilder, false); return; } if (charsetIndex == CHARSET.length && !charset) { - charset = nsHtml5Portability::newStringFromBuffer(strBuf, 0, strBufLen, treeBuilder); + charset = nsHtml5Portability::newStringFromBuffer(strBuf, 0, strBufLen, treeBuilder, false); return; } if (httpEquivIndex == HTTP_EQUIV.length && httpEquivState == HTTP_EQUIV_NOT_SEEN) { diff --git a/parser/html/nsHtml5Portability.cpp b/parser/html/nsHtml5Portability.cpp index 5a76b3c561..400f062048 100644 --- a/parser/html/nsHtml5Portability.cpp +++ b/parser/html/nsHtml5Portability.cpp @@ -16,12 +16,30 @@ nsHtml5Portability::newLocalNameFromBuffer(char16_t* buf, int32_t offset, int32_ return interner->GetAtom(nsDependentSubstring(buf, buf + length)); } +static bool +ContainsWhiteSpace(mozilla::Span<char16_t> aSpan) +{ + for (char16_t c : aSpan) { + if (nsContentUtils::IsHTMLWhitespace(c)) { + return true; + } + } + return false; +} + nsHtml5String nsHtml5Portability::newStringFromBuffer(char16_t* buf, int32_t offset, int32_t length, - nsHtml5TreeBuilder* treeBuilder) + nsHtml5TreeBuilder* treeBuilder, + bool maybeAtomize) { + if (!length) { + return nsHtml5String::EmptyString(); + } + if (maybeAtomize && !ContainsWhiteSpace(mozilla::MakeSpan(buf + offset, length))) { + return nsHtml5String::FromAtom(NS_AtomizeMainThread(nsDependentSubstring(buf + offset, length))); + } return nsHtml5String::FromBuffer(buf + offset, length, treeBuilder); } diff --git a/parser/html/nsHtml5Portability.h b/parser/html/nsHtml5Portability.h index 20d9e08e87..2a9d9422b5 100644 --- a/parser/html/nsHtml5Portability.h +++ b/parser/html/nsHtml5Portability.h @@ -61,7 +61,7 @@ class nsHtml5Portability { public: static nsIAtom* newLocalNameFromBuffer(char16_t* buf, int32_t offset, int32_t length, nsHtml5AtomTable* interner); - static nsHtml5String newStringFromBuffer(char16_t* buf, int32_t offset, int32_t length, nsHtml5TreeBuilder* treeBuilder); + static nsHtml5String newStringFromBuffer(char16_t* buf, int32_t offset, int32_t length, nsHtml5TreeBuilder* treeBuilder, bool maybeAtomize); static nsHtml5String newEmptyString(); static nsHtml5String newStringFromLiteral(const char* literal); static nsHtml5String newStringFromString(nsHtml5String string); diff --git a/parser/html/nsHtml5String.cpp b/parser/html/nsHtml5String.cpp index d26eeaedee..e72798016b 100644 --- a/parser/html/nsHtml5String.cpp +++ b/parser/html/nsHtml5String.cpp @@ -7,82 +7,49 @@ #include "nsUTF8Utils.h" #include "nsHtml5TreeBuilder.h" -nsHtml5String::nsHtml5String(already_AddRefed<nsStringBuffer> aBuffer, - uint32_t aLength) - : mBuffer(aBuffer.take()) - , mLength(aLength) -{ - if (mBuffer) { - MOZ_ASSERT(aLength); - } else { - MOZ_ASSERT(!aLength || aLength == UINT32_MAX); - } -} - void nsHtml5String::ToString(nsAString& aString) { - if (mBuffer) { - mBuffer->ToString(mLength, aString); - } else { - aString.Truncate(); - if (mLength) { + switch (GetKind()) { + case eStringBuffer: + return AsStringBuffer()->ToString(Length(), aString); + case eAtom: + return AsAtom()->ToString(aString); + case eEmpty: + aString.Truncate(); + return; + default: + aString.Truncate(); aString.SetIsVoid(true); - } + return; } } void -nsHtml5String::CopyToBuffer(char16_t* aBuffer) +nsHtml5String::CopyToBuffer(char16_t* aBuffer) const { - if (mBuffer) { - memcpy(aBuffer, mBuffer->Data(), mLength * sizeof(char16_t)); - } + memcpy(aBuffer, AsPtr(), Length() * sizeof(char16_t)); } bool -nsHtml5String::LowerCaseEqualsASCII(const char* aLowerCaseLiteral) +nsHtml5String::LowerCaseEqualsASCII(const char* aLowerCaseLiteral) const { - if (!mBuffer) { - if (mLength) { - // This string is null - return false; - } - // this string is empty - return !(*aLowerCaseLiteral); - } return !nsCharTraits<char16_t>::compareLowerCaseToASCIINullTerminated( - reinterpret_cast<char16_t*>(mBuffer->Data()), Length(), aLowerCaseLiteral); + AsPtr(), Length(), aLowerCaseLiteral); } bool -nsHtml5String::EqualsASCII(const char* aLiteral) +nsHtml5String::EqualsASCII(const char* aLiteral) const { - if (!mBuffer) { - if (mLength) { - // This string is null - return false; - } - // this string is empty - return !(*aLiteral); - } return !nsCharTraits<char16_t>::compareASCIINullTerminated( - reinterpret_cast<char16_t*>(mBuffer->Data()), Length(), aLiteral); + AsPtr(), Length(), aLiteral); } bool -nsHtml5String::LowerCaseStartsWithASCII(const char* aLowerCaseLiteral) +nsHtml5String::LowerCaseStartsWithASCII(const char* aLowerCaseLiteral) const { - if (!mBuffer) { - if (mLength) { - // This string is null - return false; - } - // this string is empty - return !(*aLowerCaseLiteral); - } const char* litPtr = aLowerCaseLiteral; - const char16_t* strPtr = reinterpret_cast<char16_t*>(mBuffer->Data()); + const char16_t* strPtr = AsPtr(); const char16_t* end = strPtr + Length(); char16_t litChar; while ((litChar = *litPtr) && (strPtr != end)) { @@ -102,37 +69,47 @@ nsHtml5String::LowerCaseStartsWithASCII(const char* aLowerCaseLiteral) } bool -nsHtml5String::Equals(nsHtml5String aOther) +nsHtml5String::Equals(nsHtml5String aOther) const { MOZ_ASSERT(operator bool()); MOZ_ASSERT(aOther); - if (mLength != aOther.mLength) { + if (Length() != aOther.Length()) { return false; } - if (!mBuffer) { - return true; - } - MOZ_ASSERT(aOther.mBuffer); return !memcmp( - mBuffer->Data(), aOther.mBuffer->Data(), Length() * sizeof(char16_t)); + AsPtr(), aOther.AsPtr(), Length() * sizeof(char16_t)); } nsHtml5String nsHtml5String::Clone() { - MOZ_ASSERT(operator bool()); - RefPtr<nsStringBuffer> ref(mBuffer); - return nsHtml5String(ref.forget(), mLength); + switch (GetKind()) { + case eStringBuffer: + AsStringBuffer()->AddRef(); + break; + case eAtom: + AsAtom()->AddRef(); + break; + default: + break; + } + return nsHtml5String(mBits); } void nsHtml5String::Release() { - if (mBuffer) { - mBuffer->Release(); - mBuffer = nullptr; - } - mLength = UINT32_MAX; + switch (GetKind()) { + case eStringBuffer: + AsStringBuffer()->Release(); + break; + case eAtom: + AsAtom()->Release(); + break; + default: + break; + } + mBits = eNull; } // static @@ -142,7 +119,7 @@ nsHtml5String::FromBuffer(char16_t* aBuffer, nsHtml5TreeBuilder* aTreeBuilder) { if (!aLength) { - return nsHtml5String(nullptr, 0U); + return nsHtml5String(eEmpty); } // Work with nsStringBuffer directly to make sure that storage is actually // nsStringBuffer and to make sure the allocation strategy matches @@ -163,12 +140,12 @@ nsHtml5String::FromBuffer(char16_t* aBuffer, char16_t* data = reinterpret_cast<char16_t*>(buffer->Data()); data[0] = 0xFFFD; data[1] = 0; - return nsHtml5String(buffer.forget(), 1); + return nsHtml5String(reinterpret_cast<uintptr_t>(buffer.forget().take()) | eStringBuffer); } char16_t* data = reinterpret_cast<char16_t*>(buffer->Data()); memcpy(data, aBuffer, aLength * sizeof(char16_t)); data[aLength] = 0; - return nsHtml5String(buffer.forget(), aLength); + return nsHtml5String(reinterpret_cast<uintptr_t>(buffer.forget().take()) | eStringBuffer); } // static @@ -177,7 +154,7 @@ nsHtml5String::FromLiteral(const char* aLiteral) { size_t length = std::strlen(aLiteral); if (!length) { - return nsHtml5String(nullptr, 0U); + return nsHtml5String(eEmpty); } // Work with nsStringBuffer directly to make sure that storage is actually // nsStringBuffer and to make sure the allocation strategy matches @@ -192,7 +169,7 @@ nsHtml5String::FromLiteral(const char* aLiteral) LossyConvertEncoding8to16 converter(data); converter.write(aLiteral, length); data[length] = 0; - return nsHtml5String(buffer.forget(), length); + return nsHtml5String(reinterpret_cast<uintptr_t>(buffer.forget().take()) | eStringBuffer); } // static @@ -201,11 +178,11 @@ nsHtml5String::FromString(const nsAString& aString) { auto length = aString.Length(); if (!length) { - return nsHtml5String(nullptr, 0U); + return nsHtml5String(eEmpty); } RefPtr<nsStringBuffer> buffer = nsStringBuffer::FromString(aString); - if (buffer) { - return nsHtml5String(buffer.forget(), length); + if (buffer && (length == buffer->StorageSize()/sizeof(char16_t) - 1)) { + return nsHtml5String(reinterpret_cast<uintptr_t>(buffer.forget().take()) | eStringBuffer); } buffer = nsStringBuffer::Alloc((length + 1) * sizeof(char16_t)); if (!buffer) { @@ -214,13 +191,20 @@ nsHtml5String::FromString(const nsAString& aString) char16_t* data = reinterpret_cast<char16_t*>(buffer->Data()); memcpy(data, aString.BeginReading(), length * sizeof(char16_t)); data[length] = 0; - return nsHtml5String(buffer.forget(), length); + return nsHtml5String(reinterpret_cast<uintptr_t>(buffer.forget().take()) | eStringBuffer); +} + +// static +nsHtml5String +nsHtml5String::FromAtom(already_AddRefed<nsIAtom> aAtom) +{ + return nsHtml5String(reinterpret_cast<uintptr_t>(aAtom.take()) | eAtom); } // static nsHtml5String nsHtml5String::EmptyString() { - return nsHtml5String(nullptr, 0U); + return nsHtml5String(eEmpty); -}
\ No newline at end of file +} diff --git a/parser/html/nsHtml5String.h b/parser/html/nsHtml5String.h index 191bf6be83..54954fe1a2 100644 --- a/parser/html/nsHtml5String.h +++ b/parser/html/nsHtml5String.h @@ -6,24 +6,61 @@ #define nsHtml5String_h #include "nsString.h" +#include "nsIAtom.h" class nsHtml5TreeBuilder; /** - * A pass-by-value type that combines an unsafe `nsStringBuffer*` with its - * logical length (`uint32_t`). (`nsStringBuffer` knows its capacity but not - * its logical length, i.e. how much of the capacity is in use.) + * A pass-by-value type that can represent + * * nullptr + * * empty string + * * Non-empty string as exactly-sized (capacity is length) `nsStringBuffer*` + * * Non-empty string as an nsIAtom* * * Holding or passing this type is as unsafe as holding or passing - * `nsStringBuffer*`. - * - * Empty strings and null strings are distinct. Since an empty nsString does - * not have a an `nsStringBuffer`, both empty and null `nsHtml5String` have - * `nullptr` as `mBuffer`. If `mBuffer` is `nullptr`, the empty case is marked - * with `mLength` being zero and the null case with `mLength` being non-zero. + * `nsStringBuffer*`/`nsIAtom*`. */ class nsHtml5String final { +private: + + static const uintptr_t kKindMask = uintptr_t(3); + + static const uintptr_t kPtrMask = ~kKindMask; + + enum Kind : uintptr_t { + eNull = 0, + eEmpty = 1, + eStringBuffer = 2, + eAtom = 3, + }; + + inline Kind GetKind() const { return (Kind)(mBits & kKindMask); } + + inline nsStringBuffer* AsStringBuffer() const + { + MOZ_ASSERT(GetKind() == eStringBuffer); + return reinterpret_cast<nsStringBuffer*>(mBits & kPtrMask); + } + + inline nsIAtom* AsAtom() const + { + MOZ_ASSERT(GetKind() == eAtom); + return reinterpret_cast<nsIAtom*>(mBits & kPtrMask); + } + + inline const char16_t* AsPtr() const + { + switch (GetKind()) { + case eStringBuffer: + return reinterpret_cast<char16_t*>(AsStringBuffer()->Data()); + case eAtom: + return AsAtom()->GetUTF16String(); + default: + return nullptr; + } + } + public: /** * Default constructor. @@ -37,29 +74,50 @@ public: * Constructor from nullptr. */ inline MOZ_IMPLICIT nsHtml5String(decltype(nullptr)) - : mBuffer(nullptr) - , mLength(UINT32_MAX) + : mBits(eNull) { } - inline uint32_t Length() const { return mBuffer ? mLength : 0; } + inline uint32_t Length() const + { + switch (GetKind()) { + case eStringBuffer: + return (AsStringBuffer()->StorageSize()/sizeof(char16_t) - 1); + case eAtom: + return AsAtom()->GetLength(); + default: + return 0; + } + } /** * False iff the string is logically null */ - inline MOZ_IMPLICIT operator bool() const { return !(!mBuffer && mLength); } + inline MOZ_IMPLICIT operator bool() const { return mBits; } + + /** + * Get the underlying nsIAtom* or nullptr if this nsHtml5String + * does not hold an atom. + */ + inline nsIAtom* MaybeAsAtom() + { + if (GetKind() == eAtom) { + return AsAtom(); + } + return nullptr; + } void ToString(nsAString& aString); - void CopyToBuffer(char16_t* aBuffer); + void CopyToBuffer(char16_t* aBuffer) const; - bool LowerCaseEqualsASCII(const char* aLowerCaseLiteral); + bool LowerCaseEqualsASCII(const char* aLowerCaseLiteral) const; - bool EqualsASCII(const char* aLiteral); + bool EqualsASCII(const char* aLiteral) const; - bool LowerCaseStartsWithASCII(const char* aLowerCaseLiteral); + bool LowerCaseStartsWithASCII(const char* aLowerCaseLiteral) const; - bool Equals(nsHtml5String aOther); + bool Equals(nsHtml5String aOther) const; nsHtml5String Clone(); @@ -73,23 +131,23 @@ public: static nsHtml5String FromString(const nsAString& aString); + static nsHtml5String FromAtom(already_AddRefed<nsIAtom> aAtom); + static nsHtml5String EmptyString(); private: - /** - * Constructor from raw parts. - */ - nsHtml5String(already_AddRefed<nsStringBuffer> aBuffer, uint32_t aLength); /** - * nullptr if the string is logically null or logically empty + * Constructor from raw bits. */ - nsStringBuffer* mBuffer; + explicit nsHtml5String(uintptr_t aBits) : mBits(aBits) {}; /** - * The length of the string. non-zero if the string is logically null. + * Zero if null, one if empty, otherwise tagged pointer + * to either nsIAtom or nsStringBuffer. The two least-significant + * bits are tag bits. */ - uint32_t mLength; + uintptr_t mBits; }; -#endif // nsHtml5String_h
\ No newline at end of file +#endif // nsHtml5String_h diff --git a/parser/html/nsHtml5Tokenizer.cpp b/parser/html/nsHtml5Tokenizer.cpp index 8fea32eb82..60285ce8ee 100644 --- a/parser/html/nsHtml5Tokenizer.cpp +++ b/parser/html/nsHtml5Tokenizer.cpp @@ -225,7 +225,7 @@ nsHtml5Tokenizer::emitOrAppendCharRefBuf(int32_t returnState) nsHtml5String nsHtml5Tokenizer::strBufToString() { - nsHtml5String str = nsHtml5Portability::newStringFromBuffer(strBuf, 0, strBufLen, tokenHandler); + nsHtml5String str = nsHtml5Portability::newStringFromBuffer(strBuf, 0, strBufLen, tokenHandler, !newAttributesEachTime && attributeName == nsHtml5AttributeName::ATTR_CLASS); clearStrBufAfterUse(); return str; } diff --git a/parser/html/nsHtml5TreeBuilder.cpp b/parser/html/nsHtml5TreeBuilder.cpp index efbc339675..76a2398f52 100644 --- a/parser/html/nsHtml5TreeBuilder.cpp +++ b/parser/html/nsHtml5TreeBuilder.cpp @@ -2181,7 +2181,7 @@ nsHtml5TreeBuilder::extractCharsetFromContent(nsHtml5String attributeValue, nsHt if (end == -1) { end = buffer.length; } - charset = nsHtml5Portability::newStringFromBuffer(buffer, start, end - start, tb); + charset = nsHtml5Portability::newStringFromBuffer(buffer, start, end - start, tb, false); } return charset; } diff --git a/parser/html/nsHtml5TreeOperation.cpp b/parser/html/nsHtml5TreeOperation.cpp index 22c8058595..46907d3a4a 100644 --- a/parser/html/nsHtml5TreeOperation.cpp +++ b/parser/html/nsHtml5TreeOperation.cpp @@ -361,29 +361,34 @@ nsHtml5TreeOperation::SetHTMLElementAttributes(dom::Element* aElement, nsIAtom* aName, nsHtml5HtmlAttributes* aAttributes) { - int32_t len = aAttributes->getLength(); + int32_t len = aAttributes->getLength(); for (int32_t i = 0; i < len; i++) { - // prefix doesn't need regetting. it is always null or a static atom - // local name is never null - nsCOMPtr<nsIAtom> localName = - Reget(aAttributes->getLocalNameNoBoundsCheck(i)); - nsCOMPtr<nsIAtom> prefix = aAttributes->getPrefixNoBoundsCheck(i); - int32_t nsuri = aAttributes->getURINoBoundsCheck(i); + nsHtml5String val = aAttributes->getValueNoBoundsCheck(i); + nsIAtom* klass = val.MaybeAsAtom(); + if (klass) { + aElement->SetSingleClassFromParser(klass); + } else { + // prefix doesn't need regetting. it is always null or a static atom + // local name is never null + RefPtr<nsIAtom> localName = + Reget(aAttributes->getLocalNameNoBoundsCheck(i)); + RefPtr<nsIAtom> prefix = aAttributes->getPrefixNoBoundsCheck(i); + int32_t nsuri = aAttributes->getURINoBoundsCheck(i); - nsString value; // Not Auto, because using it to hold nsStringBuffer* - aAttributes->getValueNoBoundsCheck(i).ToString(value); - if (nsHtml5Atoms::a == aName && nsHtml5Atoms::name == localName) { - // This is an HTML5-incompliant Geckoism. - // Remove when fixing bug 582361 - NS_ConvertUTF16toUTF8 cname(value); - NS_ConvertUTF8toUTF16 uv(nsUnescape(cname.BeginWriting())); - aElement->SetAttr(nsuri, + nsString value; // Not Auto, because using it to hold nsStringBuffer* + val.ToString(value); + if (nsGkAtoms::a == aName && nsGkAtoms::name == localName) { + // This is an HTML5-incompliant Geckoism. + // Remove when fixing bug 582361 + NS_ConvertUTF16toUTF8 cname(value); + NS_ConvertUTF8toUTF16 uv(nsUnescape(cname.BeginWriting())); + aElement->SetAttr(nsuri, localName, prefix, uv, false); - } else { - aElement->SetAttr(nsuri, + } else { + aElement->SetAttr(nsuri, localName, prefix, value, @@ -391,6 +396,7 @@ nsHtml5TreeOperation::SetHTMLElementAttributes(dom::Element* aElement, } } } +} nsIContent* nsHtml5TreeOperation::CreateHTMLElement( @@ -580,16 +586,22 @@ nsHtml5TreeOperation::CreateSVGElement( int32_t len = aAttributes->getLength(); for (int32_t i = 0; i < len; i++) { - // prefix doesn't need regetting. it is always null or a static atom - // local name is never null - nsCOMPtr<nsIAtom> localName = - Reget(aAttributes->getLocalNameNoBoundsCheck(i)); - nsCOMPtr<nsIAtom> prefix = aAttributes->getPrefixNoBoundsCheck(i); - int32_t nsuri = aAttributes->getURINoBoundsCheck(i); + nsHtml5String val = aAttributes->getValueNoBoundsCheck(i); + nsIAtom* klass = val.MaybeAsAtom(); + if (klass) { + newContent->SetSingleClassFromParser(klass); + } else { + // prefix doesn't need regetting. it is always null or a static atom + // local name is never null + nsCOMPtr<nsIAtom> localName = + Reget(aAttributes->getLocalNameNoBoundsCheck(i)); + nsCOMPtr<nsIAtom> prefix = aAttributes->getPrefixNoBoundsCheck(i); + int32_t nsuri = aAttributes->getURINoBoundsCheck(i); - nsString value; // Not Auto, because using it to hold nsStringBuffer* - aAttributes->getValueNoBoundsCheck(i).ToString(value); - newContent->SetAttr(nsuri, localName, prefix, value, false); + nsString value; // Not Auto, because using it to hold nsStringBuffer* + val.ToString(value); + newContent->SetAttr(nsuri, localName, prefix, value, false); + } } return newContent; } @@ -618,16 +630,22 @@ nsHtml5TreeOperation::CreateMathMLElement(nsIAtom* aName, int32_t len = aAttributes->getLength(); for (int32_t i = 0; i < len; i++) { - // prefix doesn't need regetting. it is always null or a static atom - // local name is never null - nsCOMPtr<nsIAtom> localName = - Reget(aAttributes->getLocalNameNoBoundsCheck(i)); - nsCOMPtr<nsIAtom> prefix = aAttributes->getPrefixNoBoundsCheck(i); - int32_t nsuri = aAttributes->getURINoBoundsCheck(i); + nsHtml5String val = aAttributes->getValueNoBoundsCheck(i); + nsIAtom* klass = val.MaybeAsAtom(); + if (klass) { + newContent->SetSingleClassFromParser(klass); + } else { + // prefix doesn't need regetting. it is always null or a static atom + // local name is never null + nsCOMPtr<nsIAtom> localName = + Reget(aAttributes->getLocalNameNoBoundsCheck(i)); + nsCOMPtr<nsIAtom> prefix = aAttributes->getPrefixNoBoundsCheck(i); + int32_t nsuri = aAttributes->getURINoBoundsCheck(i); - nsString value; // Not Auto, because using it to hold nsStringBuffer* - aAttributes->getValueNoBoundsCheck(i).ToString(value); - newContent->SetAttr(nsuri, localName, prefix, value, false); + nsString value; // Not Auto, because using it to hold nsStringBuffer* + val.ToString(value); + newContent->SetAttr(nsuri, localName, prefix, value, false); + } } return newContent; } diff --git a/parser/htmlparser/moz.build b/parser/htmlparser/moz.build index 7e5da29a5a..3a91ae1421 100644 --- a/parser/htmlparser/moz.build +++ b/parser/htmlparser/moz.build @@ -15,6 +15,7 @@ XPIDL_SOURCES += [ XPIDL_MODULE = 'htmlparser' EXPORTS += [ + 'nsElementTable.h', 'nsHTMLTagList.h', 'nsHTMLTags.h', 'nsIContentSink.h', diff --git a/parser/htmlparser/nsElementTable.cpp b/parser/htmlparser/nsElementTable.cpp index 5c69726d1f..a04282bf14 100644 --- a/parser/htmlparser/nsElementTable.cpp +++ b/parser/htmlparser/nsElementTable.cpp @@ -1,627 +1,212 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 sw=2 et tw=78: */ +/* -*- 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 "nsIAtom.h" #include "nsElementTable.h" -/***************************************************************************** - Now it's time to list all the html elements all with their capabilities... -******************************************************************************/ +struct HTMLElement +{ +#ifdef DEBUG + nsHTMLTag mTagID; +#endif + bool mIsBlock; + bool mIsContainer; +}; + +#ifdef DEBUG +#define ELEM(tag, block, container) { eHTMLTag_##tag, block, container }, +#else +#define ELEM(tag, block, container) { block, container }, +#endif + +#define ____ false // This makes the table easier to read. -// The Element Table (sung to the tune of Modern Major General) +// Note that the mIsBlock field disagrees with +// https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements for +// the following elements: center, details, dialog, dir, dt, figcaption, +// listing, menu, multicol, noscript, output, summary, tfoot, video. +// +// mrbkap thinks that the field values were pulled from the old HTML4 DTD and +// then got modified in mostly random ways to make the old parser's behavior +// compatible with the web. So it might make sense to change the mIsBlock +// values for the abovementioned tags at some point. +// -const nsHTMLElement gHTMLElements[] = { - { - /*tag*/ eHTMLTag_unknown, - /*parent,leaf*/ kNone, true - }, - { - /*tag*/ eHTMLTag_a, - /*parent,leaf*/ kSpecial, false - }, - { - /*tag*/ eHTMLTag_abbr, - /*parent,leaf*/ kPhrase, false - }, - { - /*tag*/ eHTMLTag_acronym, - /*parent,leaf*/ kPhrase, false - }, - { - /*tag*/ eHTMLTag_address, - /*parent,leaf*/ kBlock, false - }, - { - /*tag*/ eHTMLTag_applet, - /*parent,leaf*/ kSpecial, false - }, - { - /*tag*/ eHTMLTag_area, - /*parent,leaf*/ kNone, true - }, - { - /*tag*/ eHTMLTag_article, - /*parent,leaf*/ kBlock, false - }, - { - /*tag*/ eHTMLTag_aside, - /*parent,leaf*/ kBlock, false - }, - { - /*tag*/ eHTMLTag_audio, - /*parent,leaf*/ kSpecial, false - }, - { - /*tag*/ eHTMLTag_b, - /*parent,leaf*/ kFontStyle, false - }, - { - /*tag*/ eHTMLTag_base, - /*parent,leaf*/ kHeadContent, true - }, - { - /*tag*/ eHTMLTag_basefont, - /*parent,leaf*/ kSpecial, true - }, - { - /*tag*/ eHTMLTag_bdo, - /*parent,leaf*/ kSpecial, false - }, - { - /*tag*/ eHTMLTag_bgsound, - /*parent,leaf*/ (kFlowEntity|kHeadMisc), true - }, - { - /*tag*/ eHTMLTag_big, - /*parent,leaf*/ kFontStyle, false - }, - { - /*tag*/ eHTMLTag_blockquote, - /*parent,leaf*/ kBlock, false - }, - { - /*tag*/ eHTMLTag_body, - /*parent,leaf*/ kHTMLContent, false - }, - { - /*tag*/ eHTMLTag_br, - /*parent,leaf*/ kSpecial, true - }, - { - /*tag*/ eHTMLTag_button, - /*parent,leaf*/ kFormControl, false - }, - { - /*tag*/ eHTMLTag_canvas, - /*parent,leaf*/ kSpecial, false - }, - { - /*tag*/ eHTMLTag_caption, - /*parent,leaf*/ kNone, false - }, - { - /*tag*/ eHTMLTag_center, - /*parent,leaf*/ kBlock, false - }, - { - /*tag*/ eHTMLTag_cite, - /*parent,leaf*/ kPhrase, false - }, - { - /*tag*/ eHTMLTag_code, - /*parent,leaf*/ kPhrase, false - }, - { - /*tag*/ eHTMLTag_col, - /*parent,leaf*/ kNone, true - }, - { - /*tag*/ eHTMLTag_colgroup, - /*parent,leaf*/ kNone, false - }, - { - /*tag*/ eHTMLTag_content, - /*parent,leaf*/ kNone, false - }, - { - /*tag*/ eHTMLTag_data, - /*parent,leaf*/ kPhrase, false - }, - { - /*tag*/ eHTMLTag_datalist, - /*parent,leaf*/ kSpecial, false - }, - { - /*tag*/ eHTMLTag_dd, - /*parent,leaf*/ kInlineEntity, false - }, - { - /*tag*/ eHTMLTag_del, - /*parent,leaf*/ kFlowEntity, false - }, - { - /*tag*/ eHTMLTag_details, - /*parent,leaf*/ kBlock, false - }, - { - /*tag*/ eHTMLTag_dfn, - /*parent,leaf*/ kPhrase, false - }, - { - /*tag*/ eHTMLTag_dialog, - /*parent,leaf*/ kBlock, false - }, - { - /*tag*/ eHTMLTag_dir, - /*parent,leaf*/ kList, false - }, - { - /*tag*/ eHTMLTag_div, - /*parent,leaf*/ kBlock, false - }, - { - /*tag*/ eHTMLTag_dl, - /*parent,leaf*/ kBlock, false - }, - { - /*tag*/ eHTMLTag_dt, - /*parent,leaf*/ kInlineEntity, false - }, - { - /*tag*/ eHTMLTag_em, - /*parent,leaf*/ kPhrase, false - }, - { - /*tag*/ eHTMLTag_embed, - /*parent,leaf*/ kSpecial, true - }, - { - /*tag*/ eHTMLTag_fieldset, - /*parent,leaf*/ kBlock, false - }, - { - /*tag*/ eHTMLTag_figcaption, - /*parent,leaf*/ kPhrase, false - }, - { - /*tag*/ eHTMLTag_figure, - /*parent,leaf*/ kBlock, false - }, - { - /*tag*/ eHTMLTag_font, - /*parent,leaf*/ kFontStyle, false - }, - { - /*tag*/ eHTMLTag_footer, - /*parent,leaf*/ kBlock, false - }, - { - /*tag*/ eHTMLTag_form, - /*parent,leaf*/ kBlock, false - }, - { - /*tag*/ eHTMLTag_frame, - /*parent,leaf*/ kNone, true - }, - { - /*tag*/ eHTMLTag_frameset, - /*parent,leaf*/ kHTMLContent, false - }, - { - /*tag*/ eHTMLTag_h1, - /*parent,leaf*/ kHeading, false - }, - { - /*tag*/ eHTMLTag_h2, - /*parent,leaf*/ kHeading, false - }, - { - /*tag*/ eHTMLTag_h3, - /*parent,leaf*/ kHeading, false - }, - { - /*tag*/ eHTMLTag_h4, - /*parent,leaf*/ kHeading, false - }, - { - /*tag*/ eHTMLTag_h5, - /*parent,leaf*/ kHeading, false - }, - { - /*tag*/ eHTMLTag_h6, - /*parent,leaf*/ kHeading, false - }, - { - /*tag*/ eHTMLTag_head, - /*parent,leaf*/ kHTMLContent, false - }, - { - /*tag*/ eHTMLTag_header, - /*parent,leaf*/ kBlock, false - }, - { - /*tag*/ eHTMLTag_hgroup, - /*parent,leaf*/ kBlock, false - }, - { - /*tag*/ eHTMLTag_hr, - /*parent,leaf*/ kBlock, true - }, - { - /*tag*/ eHTMLTag_html, - /*parent,leaf*/ kNone, false - }, - { - /*tag*/ eHTMLTag_i, - /*parent,leaf*/ kFontStyle, false - }, - { - /*tag*/ eHTMLTag_iframe, - /*parent,leaf*/ kSpecial, false - }, - { - /*tag*/ eHTMLTag_image, - /*parent,leaf*/ kSpecial, true - }, - { - /*tag*/ eHTMLTag_img, - /*parent,leaf*/ kSpecial, true - }, - { - /*tag*/ eHTMLTag_input, - /*parent,leaf*/ kFormControl, true - }, - { - /*tag*/ eHTMLTag_ins, - /*parent,leaf*/ kFlowEntity, false - }, - { - /*tag*/ eHTMLTag_kbd, - /*parent,leaf*/ kPhrase, false - }, - { - /*tag*/ eHTMLTag_keygen, - /*parent,leaf*/ kFlowEntity, true - }, - { - /*tag*/ eHTMLTag_label, - /*parent,leaf*/ kFormControl, false - }, - { - /*tag*/ eHTMLTag_legend, - /*parent,leaf*/ kNone, false - }, - { - /*tag*/ eHTMLTag_li, - /*parent,leaf*/ kBlockEntity, false - }, - { - /*tag*/ eHTMLTag_link, - /*parent,leaf*/ kAllTags - kHeadContent, true - }, - { - /*tag*/ eHTMLTag_listing, - /*parent,leaf*/ kPreformatted, false - }, - { - /*tag*/ eHTMLTag_main, - /*parent,leaf*/ kBlock, false - }, - { - /*tag*/ eHTMLTag_map, - /*parent,leaf*/ kSpecial, false - }, - { - /*tag*/ eHTMLTag_mark, - /*parent,leaf*/ kSpecial, false - }, - { - /*tag*/ eHTMLTag_marquee, - /*parent,leaf*/ kSpecial, false - }, - { - /*tag*/ eHTMLTag_menu, - /*parent,leaf*/ kList, false - }, - { - /*tag*/ eHTMLTag_menuitem, - /*parent,leaf*/ kFlowEntity, false - }, - { - /*tag*/ eHTMLTag_meta, - /*parent,leaf*/ kHeadContent, true - }, - { - /*tag*/ eHTMLTag_meter, - /*parent,leaf*/ kFormControl, false - }, - { - /*tag*/ eHTMLTag_multicol, - /*parent,leaf*/ kBlock, false - }, - { - /*tag*/ eHTMLTag_nav, - /*parent,leaf*/ kBlock, false - }, - { - /*tag*/ eHTMLTag_nobr, - /*parent,leaf*/ kExtensions, false - }, - { - /*tag*/ eHTMLTag_noembed, - /*parent,leaf*/ kFlowEntity, false - }, - { - /*tag*/ eHTMLTag_noframes, - /*parent,leaf*/ kFlowEntity, false - }, - { - /*tag*/ eHTMLTag_noscript, - /*parent,leaf*/ kFlowEntity|kHeadMisc, false - }, - { - /*tag*/ eHTMLTag_object, - /*parent,leaf*/ kSpecial, false - }, - { - /*tag*/ eHTMLTag_ol, - /*parent,leaf*/ kList, false - }, - { - /*tag*/ eHTMLTag_optgroup, - /*parent,leaf*/ kNone, false - }, - { - /*tag*/ eHTMLTag_option, - /*parent,leaf*/ kNone, false - }, - { - /*tag*/ eHTMLTag_output, - /*parent,leaf*/ kSpecial, false - }, - { - /*tag*/ eHTMLTag_p, - /*parent,leaf*/ kBlock, false - }, - { - /*tag*/ eHTMLTag_param, - /*parent,leaf*/ kSpecial, true - }, - { - /*tag*/ eHTMLTag_picture, - /*parent,leaf*/ kSpecial, false - }, - { - /*tag*/ eHTMLTag_plaintext, - /*parent,leaf*/ kExtensions, false - }, - { - /*tag*/ eHTMLTag_pre, - /*parent,leaf*/ kBlock|kPreformatted, false - }, - { - /*tag*/ eHTMLTag_progress, - /*parent,leaf*/ kFormControl, false - }, - { - /*tag*/ eHTMLTag_q, - /*parent,leaf*/ kSpecial, false - }, - { - /*tag*/ eHTMLTag_rb, - /*parent,leaf*/ kPhrase, false - }, - { - /*tag*/ eHTMLTag_rp, - /*parent,leaf*/ kPhrase, false - }, - { - /*tag*/ eHTMLTag_rt, - /*parent,leaf*/ kPhrase, false - }, - { - /*tag*/ eHTMLTag_rtc, - /*parent,leaf*/ kPhrase, false - }, - { - /*tag*/ eHTMLTag_ruby, - /*parent,leaf*/ kPhrase, false - }, - { - /*tag*/ eHTMLTag_s, - /*parent,leaf*/ kFontStyle, false - }, - { - /*tag*/ eHTMLTag_samp, - /*parent,leaf*/ kPhrase, false - }, - { - /*tag*/ eHTMLTag_script, - /*parent,leaf*/ (kSpecial|kHeadContent), false - }, - { - /*tag*/ eHTMLTag_section, - /*parent,leaf*/ kBlock, false - }, - { - /*tag*/ eHTMLTag_select, - /*parent,leaf*/ kFormControl, false - }, - { - /*tag*/ eHTMLTag_shadow, - /*parent,leaf*/ kFlowEntity, false - }, - { - /*tag*/ eHTMLTag_small, - /*parent,leaf*/ kFontStyle, false - }, - { - /*tag*/ eHTMLTag_source, - /*parent,leaf*/ kSpecial, true - }, - { - /*tag*/ eHTMLTag_span, - /*parent,leaf*/ kSpecial, false - }, - { - /*tag*/ eHTMLTag_strike, - /*parent,leaf*/ kFontStyle, false - }, - { - /*tag*/ eHTMLTag_strong, - /*parent,leaf*/ kPhrase, false - }, - { - /*tag*/ eHTMLTag_style, - /*parent,leaf*/ kAllTags - kHeadContent, false - }, - { - /*tag*/ eHTMLTag_sub, - /*parent,leaf*/ kSpecial, false - }, - { - /*tag*/ eHTMLTag_summary, - /*parent,leaf*/ kBlock, false - }, - { - /*tag*/ eHTMLTag_sup, - /*parent,leaf*/ kSpecial, false - }, - { - /*tag*/ eHTMLTag_table, - /*parent,leaf*/ kBlock, false - }, - { - /*tag*/ eHTMLTag_tbody, - /*parent,leaf*/ kNone, false - }, - { - /*tag*/ eHTMLTag_td, - /*parent,leaf*/ kNone, false - }, - { - /*tag*/ eHTMLTag_textarea, - /*parent,leaf*/ kFormControl, false - }, - { - /*tag*/ eHTMLTag_tfoot, - /*parent,leaf*/ kNone, false - }, - { - /*tag*/ eHTMLTag_th, - /*parent,leaf*/ kNone, false - }, - { - /*tag*/ eHTMLTag_thead, - /*parent,leaf*/ kNone, false - }, - { - /*tag*/ eHTMLTag_template, - /*parent,leaf*/ kNone, false - }, - { - /*tag*/ eHTMLTag_time, - /*parent,leaf*/ kPhrase, false - }, - { - /*tag*/ eHTMLTag_title, - /*parent,leaf*/ kHeadContent, false - }, - { - /*tag*/ eHTMLTag_tr, - /*parent,leaf*/ kNone, false - }, - { - /*tag*/ eHTMLTag_track, - /*parent,leaf*/ kSpecial, true - }, - { - /*tag*/ eHTMLTag_tt, - /*parent,leaf*/ kFontStyle, false - }, - { - /*tag*/ eHTMLTag_u, - /*parent,leaf*/ kFontStyle, false - }, - { - /*tag*/ eHTMLTag_ul, - /*parent,leaf*/ kList, false - }, - { - /*tag*/ eHTMLTag_var, - /*parent,leaf*/ kPhrase, false - }, - { - /*tag*/ eHTMLTag_video, - /*parent,leaf*/ kSpecial, false - }, - { - /*tag*/ eHTMLTag_wbr, - /*parent,leaf*/ kExtensions, true - }, - { - /*tag*/ eHTMLTag_xmp, - /*parent,leaf*/ kInlineEntity|kPreformatted, false - }, - { - /*tag*/ eHTMLTag_text, - /*parent,leaf*/ kFlowEntity, true - }, - { - /*tag*/ eHTMLTag_whitespace, - /*parent,leaf*/ kFlowEntity|kHeadMisc, true - }, - { - /*tag*/ eHTMLTag_newline, - /*parent,leaf*/ kFlowEntity|kHeadMisc, true - }, - { - /*tag*/ eHTMLTag_comment, - /*parent,leaf*/ kFlowEntity|kHeadMisc, false - }, - { - /*tag*/ eHTMLTag_entity, - /*parent,leaf*/ kFlowEntity, false - }, - { - /*tag*/ eHTMLTag_doctypeDecl, - /*parent,leaf*/ kFlowEntity, false - }, - { - /*tag*/ eHTMLTag_markupDecl, - /*parent,leaf*/ kFlowEntity, false - }, - { - /*tag*/ eHTMLTag_instruction, - /*parent,leaf*/ kFlowEntity, false - }, - { - /*tag*/ eHTMLTag_userdefined, - /*parent,leaf*/ (kFlowEntity|kHeadMisc), false - }, +static const HTMLElement gHTMLElements[] = { + ELEM(unknown, ____, ____) + ELEM(a, ____, true) + ELEM(abbr, ____, true) + ELEM(acronym, ____, true) + ELEM(address, true, true) + ELEM(applet, ____, true) + ELEM(area, ____, ____) + ELEM(article, true, true) + ELEM(aside, true, true) + ELEM(audio, ____, true) + ELEM(b, ____, true) + ELEM(base, ____, ____) + ELEM(basefont, ____, ____) + ELEM(bdo, ____, true) + ELEM(bgsound, ____, ____) + ELEM(big, ____, true) + ELEM(blockquote, true, true) + ELEM(body, ____, true) + ELEM(br, ____, ____) + ELEM(button, ____, true) + ELEM(canvas, ____, true) + ELEM(caption, ____, true) + ELEM(center, true, true) + ELEM(cite, ____, true) + ELEM(code, ____, true) + ELEM(col, ____, ____) + ELEM(colgroup, ____, true) + ELEM(data, ____, true) + ELEM(datalist, ____, true) + ELEM(dd, ____, true) + ELEM(del, ____, true) + ELEM(details, true, true) + ELEM(dfn, ____, true) + ELEM(dialog, true, true) + ELEM(dir, true, true) + ELEM(div, true, true) + ELEM(dl, true, true) + ELEM(dt, ____, true) + ELEM(em, ____, true) + ELEM(embed, ____, ____) + ELEM(fieldset, true, true) + ELEM(figcaption, ____, true) + ELEM(figure, true, true) + ELEM(font, ____, true) + ELEM(footer, true, true) + ELEM(form, true, true) + ELEM(frame, ____, ____) + ELEM(frameset, ____, true) + ELEM(h1, true, true) + ELEM(h2, true, true) + ELEM(h3, true, true) + ELEM(h4, true, true) + ELEM(h5, true, true) + ELEM(h6, true, true) + ELEM(head, ____, true) + ELEM(header, true, true) + ELEM(hgroup, true, true) + ELEM(hr, true, ____) + ELEM(html, ____, true) + ELEM(i, ____, true) + ELEM(iframe, ____, true) + ELEM(image, ____, ____) + ELEM(img, ____, ____) + ELEM(input, ____, ____) + ELEM(ins, ____, true) + ELEM(kbd, ____, true) + ELEM(keygen, ____, ____) + ELEM(label, ____, true) + ELEM(legend, ____, true) + ELEM(li, true, true) + ELEM(link, ____, ____) + ELEM(listing, true, true) + ELEM(main, true, true) + ELEM(map, ____, true) + ELEM(mark, ____, true) + ELEM(marquee, ____, true) + ELEM(menu, true, true) + ELEM(menuitem, ____, true) + ELEM(meta, ____, ____) + ELEM(meter, ____, true) + ELEM(multicol, true, true) + ELEM(nav, true, true) + ELEM(nobr, ____, true) + ELEM(noembed, ____, true) + ELEM(noframes, ____, true) + ELEM(noscript, ____, true) + ELEM(object, ____, true) + ELEM(ol, true, true) + ELEM(optgroup, ____, true) + ELEM(option, ____, true) + ELEM(output, ____, true) + ELEM(p, true, true) + ELEM(param, ____, ____) + ELEM(picture, ____, true) + ELEM(plaintext, ____, true) + ELEM(pre, true, true) + ELEM(progress, ____, true) + ELEM(q, ____, true) + ELEM(rb, ____, true) + ELEM(rp, ____, true) + ELEM(rt, ____, true) + ELEM(rtc, ____, true) + ELEM(ruby, ____, true) + ELEM(s, ____, true) + ELEM(samp, ____, true) + ELEM(script, ____, true) + ELEM(section, true, true) + ELEM(select, ____, true) + ELEM(small, ____, true) + ELEM(slot, ____, true) + ELEM(source, ____, ____) + ELEM(span, ____, true) + ELEM(strike, ____, true) + ELEM(strong, ____, true) + ELEM(style, ____, true) + ELEM(sub, ____, true) + ELEM(summary, true, true) + ELEM(sup, ____, true) + ELEM(table, true, true) + ELEM(tbody, ____, true) + ELEM(td, ____, true) + ELEM(textarea, ____, true) + ELEM(tfoot, ____, true) + ELEM(th, ____, true) + ELEM(thead, ____, true) + ELEM(template, ____, true) + ELEM(time, ____, true) + ELEM(title, ____, true) + ELEM(tr, ____, true) + ELEM(track, ____, ____) + ELEM(tt, ____, true) + ELEM(u, ____, true) + ELEM(ul, true, true) + ELEM(var, ____, true) + ELEM(video, ____, true) + ELEM(wbr, ____, ____) + ELEM(xmp, ____, true) + ELEM(text, ____, ____) + ELEM(whitespace, ____, ____) + ELEM(newline, ____, ____) + ELEM(comment, ____, true) + ELEM(entity, ____, true) + ELEM(doctypeDecl, ____, true) + ELEM(markupDecl, ____, true) + ELEM(instruction, ____, true) + ELEM(userdefined, ____, true) }; -/*********************************************************************************************/ +#undef ELEM +#undef ____ -bool nsHTMLElement::IsContainer(eHTMLTags aChild) +bool +nsHTMLElement::IsContainer(nsHTMLTag aId) { - return !gHTMLElements[aChild].mLeaf; + return gHTMLElements[aId].mIsContainer; } -bool nsHTMLElement::IsMemberOf(int32_t aSet) const +bool +nsHTMLElement::IsBlock(nsHTMLTag aId) { - return TestBits(aSet,mParentBits); + return gHTMLElements[aId].mIsBlock; } #ifdef DEBUG -void CheckElementTable() +void +CheckElementTable() { - for (eHTMLTags t = eHTMLTag_unknown; t <= eHTMLTag_userdefined; t = eHTMLTags(t + 1)) { - NS_ASSERTION(gHTMLElements[t].mTagID == t, "gHTMLElements entries does match tag list."); + for (nsHTMLTag t = eHTMLTag_unknown; + t <= eHTMLTag_userdefined; + t = nsHTMLTag(t + 1)) { + MOZ_ASSERT(gHTMLElements[t].mTagID == t, + "gHTMLElements entries does match tag list."); } } #endif diff --git a/parser/htmlparser/nsElementTable.h b/parser/htmlparser/nsElementTable.h index a4b78c09e4..f9f63e3db4 100644 --- a/parser/htmlparser/nsElementTable.h +++ b/parser/htmlparser/nsElementTable.h @@ -1,92 +1,22 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- 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/. */ - -/** - * MODULE NOTES: - * @update gess 4/1/98 - * - */ - - - -#ifndef _NSELEMENTABLE -#define _NSELEMENTABLE +#ifndef nsElementTable_h +#define nsElementTable_h #include "nsHTMLTags.h" -#include "nsIDTD.h" - -//********************************************************************************************* -// The following ints define the standard groups of HTML elements... -//********************************************************************************************* - -static const int kNone= 0x0; - -static const int kHTMLContent = 0x0001; // HEAD, (FRAMESET | BODY) -static const int kHeadContent = 0x0002; // Elements that *must* be in the head. -static const int kHeadMisc = 0x0004; // Elements that *can* be in the head. - -static const int kSpecial = 0x0008; // A, IMG, APPLET, OBJECT, FONT, BASEFONT, BR, SCRIPT, - // MAP, Q, SUB, SUP, SPAN, BDO, IFRAME - -static const int kFormControl = 0x0010; // INPUT SELECT TEXTAREA LABEL BUTTON -static const int kPreformatted = 0x0020; // PRE -static const int kPreExclusion = 0x0040; // IMG, OBJECT, APPLET, BIG, SMALL, SUB, SUP, FONT, BASEFONT -static const int kFontStyle = 0x0080; // TT, I, B, U, S, STRIKE, BIG, SMALL -static const int kPhrase = 0x0100; // EM, STRONG, DFN, CODE, SAMP, KBD, VAR, CITE, ABBR, ACRONYM -static const int kHeading = 0x0200; // H1..H6 -static const int kBlockMisc = 0x0400; // OBJECT, SCRIPT -static const int kBlock = 0x0800; // ADDRESS, BLOCKQUOTE, CENTER, DIV, DL, FIELDSET, FORM, - // ISINDEX, HR, NOSCRIPT, NOFRAMES, P, TABLE -static const int kList = 0x1000; // UL, OL, DIR, MENU -static const int kPCDATA = 0x2000; // plain text and entities... -static const int kSelf = 0x4000; // whatever THIS tag is... -static const int kExtensions = 0x8000; // BGSOUND, WBR, NOBR -static const int kTable = 0x10000;// TR,TD,THEAD,TBODY,TFOOT,CAPTION,TH -static const int kDLChild = 0x20000;// DL, DT -static const int kCDATA = 0x40000;// just plain text... - -static const int kInlineEntity = (kPCDATA|kFontStyle|kPhrase|kSpecial|kFormControl|kExtensions); // #PCDATA, %fontstyle, %phrase, %special, %formctrl -static const int kBlockEntity = (kHeading|kList|kPreformatted|kBlock); // %heading, %list, %preformatted, %block -static const int kFlowEntity = (kBlockEntity|kInlineEntity); // %blockentity, %inlineentity -static const int kAllTags = 0xffffff; - - -//********************************************************************************************* -// The following ints define the standard groups of HTML elements... -//********************************************************************************************* #ifdef DEBUG -extern void CheckElementTable(); +void CheckElementTable(); #endif +struct nsHTMLElement +{ + static bool IsContainer(nsHTMLTag aTag); + static bool IsBlock(nsHTMLTag aTag); +}; -/** - * We're asking the question: is aTest a member of bitset. - * - * @param - * @return TRUE or FALSE - */ -inline bool TestBits(int aBitset,int aTest) { - if(aTest) { - int32_t result=(aBitset & aTest); - return bool(result==aTest); - } - return false; -} - -struct nsHTMLElement { - bool IsMemberOf(int32_t aType) const; - - eHTMLTags mTagID; - int mParentBits; //defines groups that can contain this element - bool mLeaf; - - static bool IsContainer(eHTMLTags aTag); -}; - -extern const nsHTMLElement gHTMLElements[]; - -#endif +#endif // nsElementTable_h diff --git a/parser/htmlparser/nsHTMLTagList.h b/parser/htmlparser/nsHTMLTagList.h index e6f1f644cb..6f2316ac79 100644 --- a/parser/htmlparser/nsHTMLTagList.h +++ b/parser/htmlparser/nsHTMLTagList.h @@ -71,7 +71,6 @@ HTML_HTMLELEMENT_TAG(cite) HTML_HTMLELEMENT_TAG(code) HTML_TAG(col, TableCol, TableCol) HTML_TAG(colgroup, TableCol, TableCol) -HTML_TAG(content, Content, Content) HTML_TAG(data, Data, Data) HTML_TAG(datalist, DataList, DataList) HTML_HTMLELEMENT_TAG(dd) @@ -153,8 +152,8 @@ HTML_HTMLELEMENT_TAG(samp) HTML_TAG(script, Script, Script) HTML_HTMLELEMENT_TAG(section) HTML_TAG(select, Select, Select) -HTML_TAG(shadow, Shadow, Shadow) HTML_HTMLELEMENT_TAG(small) +HTML_TAG(slot, Slot, Slot) HTML_TAG(source, Source, Source) HTML_TAG(span, Span, Span) HTML_HTMLELEMENT_TAG(strike) diff --git a/parser/htmlparser/nsHTMLTags.cpp b/parser/htmlparser/nsHTMLTags.cpp index e98d2c4cd8..681c374897 100644 --- a/parser/htmlparser/nsHTMLTags.cpp +++ b/parser/htmlparser/nsHTMLTags.cpp @@ -161,7 +161,7 @@ nsHTMLTags::ReleaseTable(void) // static nsHTMLTag -nsHTMLTags::LookupTag(const nsAString& aTagName) +nsHTMLTags::StringTagToId(const nsAString& aTagName) { uint32_t length = aTagName.Length(); @@ -195,7 +195,7 @@ nsHTMLTags::LookupTag(const nsAString& aTagName) buf[i] = 0; - return CaseSensitiveLookupTag(buf); + return CaseSensitiveStringTagToId(buf); } #ifdef DEBUG @@ -210,33 +210,33 @@ nsHTMLTags::TestTagTable() // Make sure we can find everything we are supposed to for (int i = 0; i < NS_HTML_TAG_MAX; ++i) { tag = sTagUnicodeTable[i]; - id = LookupTag(nsDependentString(tag)); + id = StringTagToId(nsDependentString(tag)); NS_ASSERTION(id != eHTMLTag_userdefined, "can't find tag id"); const char16_t* check = GetStringValue(id); NS_ASSERTION(0 == nsCRT::strcmp(check, tag), "can't map id back to tag"); nsAutoString uname(tag); ToUpperCase(uname); - NS_ASSERTION(id == LookupTag(uname), "wrong id"); + NS_ASSERTION(id == StringTagToId(uname), "wrong id"); - NS_ASSERTION(id == CaseSensitiveLookupTag(tag), "wrong id"); + NS_ASSERTION(id == CaseSensitiveStringTagToId(tag), "wrong id"); atom = NS_Atomize(tag); - NS_ASSERTION(id == CaseSensitiveLookupTag(atom), "wrong id"); + NS_ASSERTION(id == CaseSensitiveAtomTagToId(atom), "wrong id"); NS_ASSERTION(atom == GetAtom(id), "can't map id back to atom"); } // Make sure we don't find things that aren't there - id = LookupTag(NS_LITERAL_STRING("@")); + id = StringTagToId(NS_LITERAL_STRING("@")); NS_ASSERTION(id == eHTMLTag_userdefined, "found @"); - id = LookupTag(NS_LITERAL_STRING("zzzzz")); + id = StringTagToId(NS_LITERAL_STRING("zzzzz")); NS_ASSERTION(id == eHTMLTag_userdefined, "found zzzzz"); atom = NS_Atomize("@"); - id = CaseSensitiveLookupTag(atom); + id = CaseSensitiveAtomTagToId(atom); NS_ASSERTION(id == eHTMLTag_userdefined, "found @"); atom = NS_Atomize("zzzzz"); - id = CaseSensitiveLookupTag(atom); + id = CaseSensitiveAtomTagToId(atom); NS_ASSERTION(id == eHTMLTag_userdefined, "found zzzzz"); tag = GetStringValue((nsHTMLTag) 0); diff --git a/parser/htmlparser/nsHTMLTags.h b/parser/htmlparser/nsHTMLTags.h index 27a23b89fe..b67e17372b 100644 --- a/parser/htmlparser/nsHTMLTags.h +++ b/parser/htmlparser/nsHTMLTags.h @@ -46,8 +46,13 @@ public: static void ReleaseTable(void); // Functions for converting string or atom to id - static nsHTMLTag LookupTag(const nsAString& aTagName); - static nsHTMLTag CaseSensitiveLookupTag(const char16_t* aTagName) + static nsHTMLTag StringTagToId(const nsAString& aTagName); + static nsHTMLTag AtomTagToId(nsIAtom* aTagName) + { + return StringTagToId(nsDependentAtomString(aTagName)); + } + + static nsHTMLTag CaseSensitiveStringTagToId(const char16_t* aTagName) { NS_ASSERTION(gTagTable, "no lookup table, needs addref"); NS_ASSERTION(aTagName, "null tagname!"); @@ -56,7 +61,7 @@ public: return tag ? (nsHTMLTag)NS_PTR_TO_INT32(tag) : eHTMLTag_userdefined; } - static nsHTMLTag CaseSensitiveLookupTag(nsIAtom* aTagName) + static nsHTMLTag CaseSensitiveAtomTagToId(nsIAtom* aTagName) { NS_ASSERTION(gTagAtomTable, "no lookup table, needs addref"); NS_ASSERTION(aTagName, "null tagname!"); @@ -91,6 +96,4 @@ private: static PLHashTable* gTagAtomTable; }; -#define eHTMLTags nsHTMLTag - #endif /* nsHTMLTags_h___ */ diff --git a/parser/htmlparser/nsParserService.cpp b/parser/htmlparser/nsParserService.cpp index d89badd01d..5893f19a9b 100644 --- a/parser/htmlparser/nsParserService.cpp +++ b/parser/htmlparser/nsParserService.cpp @@ -25,19 +25,19 @@ NS_IMPL_ISUPPORTS(nsParserService, nsIParserService) int32_t nsParserService::HTMLAtomTagToId(nsIAtom* aAtom) const { - return nsHTMLTags::LookupTag(nsDependentAtomString(aAtom)); + return nsHTMLTags::StringTagToId(nsDependentAtomString(aAtom)); } int32_t nsParserService::HTMLCaseSensitiveAtomTagToId(nsIAtom* aAtom) const { - return nsHTMLTags::CaseSensitiveLookupTag(aAtom); + return nsHTMLTags::CaseSensitiveAtomTagToId(aAtom); } int32_t nsParserService::HTMLStringTagToId(const nsAString& aTag) const { - return nsHTMLTags::LookupTag(aTag); + return nsHTMLTags::StringTagToId(aTag); } const char16_t* @@ -76,7 +76,7 @@ nsParserService::HTMLConvertUnicodeToEntity(int32_t aUnicode, NS_IMETHODIMP nsParserService::IsContainer(int32_t aId, bool& aIsContainer) const { - aIsContainer = nsHTMLElement::IsContainer((eHTMLTags)aId); + aIsContainer = nsHTMLElement::IsContainer((nsHTMLTag)aId); return NS_OK; } @@ -84,16 +84,7 @@ nsParserService::IsContainer(int32_t aId, bool& aIsContainer) const NS_IMETHODIMP nsParserService::IsBlock(int32_t aId, bool& aIsBlock) const { - if((aId>eHTMLTag_unknown) && (aId<eHTMLTag_userdefined)) { - aIsBlock=((gHTMLElements[aId].IsMemberOf(kBlock)) || - (gHTMLElements[aId].IsMemberOf(kBlockEntity)) || - (gHTMLElements[aId].IsMemberOf(kHeading)) || - (gHTMLElements[aId].IsMemberOf(kPreformatted))|| - (gHTMLElements[aId].IsMemberOf(kList))); - } - else { - aIsBlock = false; - } + aIsBlock = nsHTMLElement::IsBlock((nsHTMLTag)aId); return NS_OK; } diff --git a/testing/profiles/prefs_general.js b/testing/profiles/prefs_general.js index 960cbda15a..c90806a4d1 100644 --- a/testing/profiles/prefs_general.js +++ b/testing/profiles/prefs_general.js @@ -57,7 +57,6 @@ user_pref("media.gmp-manager.updateEnabled", false); user_pref("dom.w3c_touch_events.enabled", 1); user_pref("layout.accessiblecaret.enabled_on_touch", false); user_pref("dom.webcomponents.enabled", true); -user_pref("dom.webcomponents.customelements.enabled", true); user_pref("dom.htmlimports.enabled", true); // Existing tests assume there is no font size inflation. user_pref("font.size.inflation.emPerLine", 0); @@ -329,4 +328,4 @@ user_pref("plugin.load_flash_only", false); // cannot easily be upgraded. user_pref("media.libavcodec.allow-obsolete", true); -user_pref("media.openUnsupportedTypeWithExternalApp", false);
\ No newline at end of file +user_pref("media.openUnsupportedTypeWithExternalApp", false); diff --git a/testing/web-platform/meta/dom/interfaces.html.ini b/testing/web-platform/meta/dom/interfaces.html.ini index 2a0c6da049..988efdf978 100644 --- a/testing/web-platform/meta/dom/interfaces.html.ini +++ b/testing/web-platform/meta/dom/interfaces.html.ini @@ -27,9 +27,6 @@ [Element interface: operation attachShadow(ShadowRootInit)] expected: FAIL - [Element interface: attribute assignedSlot] - expected: FAIL - [Element interface: element must inherit property "slot" with the proper type (7)] expected: FAIL @@ -42,9 +39,6 @@ [Element interface: element must inherit property "assignedSlot" with the proper type (48)] expected: FAIL - [Text interface: attribute assignedSlot] - expected: FAIL - [Text interface: document.createTextNode("abc") must inherit property "assignedSlot" with the proper type (2)] expected: FAIL diff --git a/testing/web-platform/meta/html/dom/interfaces.html.ini b/testing/web-platform/meta/html/dom/interfaces.html.ini index db6a464d0a..d0ebb0c7d6 100644 --- a/testing/web-platform/meta/html/dom/interfaces.html.ini +++ b/testing/web-platform/meta/html/dom/interfaces.html.ini @@ -1,6 +1,6 @@ [interfaces.html] type: testharness - prefs: [dom.forms.inputmode:true, dom.dialog_element.enabled:true] + prefs: [dom.forms.inputmode:true, dom.dialog_element.enabled:true, dom.webcomponents.enabled:true] [Document interface: attribute domain] expected: FAIL @@ -2524,21 +2524,6 @@ [HTMLSlotElement interface: operation assignedNodes(AssignedNodesOptions)] expected: FAIL - [HTMLSlotElement must be primary interface of document.createElement("slot")] - expected: FAIL - - [Stringification of document.createElement("slot")] - expected: FAIL - - [HTMLSlotElement interface: document.createElement("slot") must inherit property "name" with the proper type (0)] - expected: FAIL - - [HTMLSlotElement interface: document.createElement("slot") must inherit property "assignedNodes" with the proper type (1)] - expected: FAIL - - [HTMLSlotElement interface: calling assignedNodes(AssignedNodesOptions) on document.createElement("slot") with too few arguments must throw TypeError] - expected: FAIL - [HTMLInputElement interface: createInput("text") must inherit property "dirName" with the proper type (6)] expected: FAIL diff --git a/testing/web-platform/meta/html/dom/reflection-misc.html.ini b/testing/web-platform/meta/html/dom/reflection-misc.html.ini index bc65d4191b..05bb9c4cdd 100644 --- a/testing/web-platform/meta/html/dom/reflection-misc.html.ini +++ b/testing/web-platform/meta/html/dom/reflection-misc.html.ini @@ -1,6 +1,6 @@ [reflection-misc.html] type: testharness - prefs: [dom.dialog_element.enabled: true] + prefs: [dom.dialog_element.enabled: true, dom.webcomponents.enabled:true] [html.tabIndex: setAttribute() to object "3" followed by getAttribute()] expected: FAIL diff --git a/testing/web-platform/meta/shadow-dom/Document-prototype-adoptNode.html.ini b/testing/web-platform/meta/shadow-dom/Document-prototype-adoptNode.html.ini deleted file mode 100644 index 830ac82245..0000000000 --- a/testing/web-platform/meta/shadow-dom/Document-prototype-adoptNode.html.ini +++ /dev/null @@ -1,8 +0,0 @@ -[Document-prototype-adoptNode.html] - type: testharness - [adoptNode on a shadow root in open mode must throw a HierarchyRequestError] - expected: FAIL - - [adoptNode on a shadow root in closed mode must throw a HierarchyRequestError] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/Document-prototype-importNode.html.ini b/testing/web-platform/meta/shadow-dom/Document-prototype-importNode.html.ini deleted file mode 100644 index 0ca7987cf4..0000000000 --- a/testing/web-platform/meta/shadow-dom/Document-prototype-importNode.html.ini +++ /dev/null @@ -1,8 +0,0 @@ -[Document-prototype-importNode.html] - type: testharness - [importNode on a shadow root in open mode must throw a NotSupportedError] - expected: FAIL - - [importNode on a shadow root in closed mode must throw a NotSupportedError] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/Element-interface-attachShadow.html.ini b/testing/web-platform/meta/shadow-dom/Element-interface-attachShadow.html.ini deleted file mode 100644 index b9df3f236c..0000000000 --- a/testing/web-platform/meta/shadow-dom/Element-interface-attachShadow.html.ini +++ /dev/null @@ -1,14 +0,0 @@ -[Element-interface-attachShadow.html] - type: testharness - [Check the existence of Element.attachShadow] - expected: FAIL - - [Element.attachShadow must create an instance of ShadowRoot] - expected: FAIL - - [Element.attachShadow must throw a InvalidStateError if the context object already hosts a shadow tree] - expected: FAIL - - [Element.attachShadow must throw a NotSupportedError for button, details, input, marquee, meter, progress, select, textarea, and keygen elements] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/Element-interface-shadowRoot-attribute.html.ini b/testing/web-platform/meta/shadow-dom/Element-interface-shadowRoot-attribute.html.ini deleted file mode 100644 index 66573a688f..0000000000 --- a/testing/web-platform/meta/shadow-dom/Element-interface-shadowRoot-attribute.html.ini +++ /dev/null @@ -1,8 +0,0 @@ -[Element-interface-shadowRoot-attribute.html] - type: testharness - [shadowRoot attribute must return the open shadow root associated with the element] - expected: FAIL - - [shadowRoot attribute must return null if the shadow root attached to the element is closed] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/Extensions-to-Event-Interface.html.ini b/testing/web-platform/meta/shadow-dom/Extensions-to-Event-Interface.html.ini index c58edca31d..9760e42eae 100644 --- a/testing/web-platform/meta/shadow-dom/Extensions-to-Event-Interface.html.ini +++ b/testing/web-platform/meta/shadow-dom/Extensions-to-Event-Interface.html.ini @@ -1,13 +1,5 @@ [Extensions-to-Event-Interface.html] type: testharness - [composedPath() must exist on Event] - expected: FAIL - - [composedPath() must return an empty array when the event has not been dispatched] - expected: FAIL - - [composedPath() must return an empty array when the event is no longer dispatched] - expected: FAIL [The event must propagate out of open mode shadow boundaries when the composed flag is set] expected: FAIL diff --git a/testing/web-platform/meta/shadow-dom/HTMLSlotElement-interface.html.ini b/testing/web-platform/meta/shadow-dom/HTMLSlotElement-interface.html.ini deleted file mode 100644 index c045a3006b..0000000000 --- a/testing/web-platform/meta/shadow-dom/HTMLSlotElement-interface.html.ini +++ /dev/null @@ -1,44 +0,0 @@ -[HTMLSlotElement-interface.html] - type: testharness - [HTMLSlotElement must be defined on window] - expected: FAIL - - ["name" attribute on HTMLSlotElement must reflect "name" attribute] - expected: FAIL - - [assignedNodes() on a HTMLSlotElement must return an empty array when the slot element is not in a tree or in a document tree] - expected: FAIL - - [assignedNodes({"flattened":false}) on a HTMLSlotElement must return an empty array when the slot element is not in a tree or in a document tree] - expected: FAIL - - [assignedNodes({"flattened":true}) on a HTMLSlotElement must return an empty array when the slot element is not in a tree or in a document tree] - expected: FAIL - - [assignedNodes() must return the list of assigned nodes when none of the assigned nodes themselves are slots] - expected: FAIL - - [assignedNodes({"flattened":false}) must return the list of assigned nodes when none of the assigned nodes themselves are slots] - expected: FAIL - - [assignedNodes({"flattened":true}) must return the list of assigned nodes when none of the assigned nodes themselves are slots] - expected: FAIL - - [assignedNodes() must update when slot and name attributes are modified] - expected: FAIL - - [assignedNodes({"flattened":false}) must update when slot and name attributes are modified] - expected: FAIL - - [assignedNodes({"flattened":true}) must update when slot and name attributes are modified] - expected: FAIL - - [assignedNodes must update when a default slot is introduced dynamically by a slot rename] - expected: FAIL - - [assignedNodes must update when slot elements are inserted or removed] - expected: FAIL - - [assignedNodes({flatten: true}) must return the distributed nodes, and assignedNodes() and assignedNodes({flatten: false}) must returned the assigned nodes] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/Slotable-interface.html.ini b/testing/web-platform/meta/shadow-dom/Slotable-interface.html.ini deleted file mode 100644 index 346edcd4f5..0000000000 --- a/testing/web-platform/meta/shadow-dom/Slotable-interface.html.ini +++ /dev/null @@ -1,14 +0,0 @@ -[Slotable-interface.html] - type: testharness - [assignedSlot attribute must be defined on Element and Text interfaces] - expected: FAIL - - [assignedSlot must return null when the node does not have an assigned node] - expected: FAIL - - [assignedSlot must return the assigned slot] - expected: FAIL - - [assignedSlot must return null when the assigned slot element is inside a closed shadow tree] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/event-composed-path-with-related-target.html.ini b/testing/web-platform/meta/shadow-dom/event-composed-path-with-related-target.html.ini deleted file mode 100644 index afe0ce8b0c..0000000000 --- a/testing/web-platform/meta/shadow-dom/event-composed-path-with-related-target.html.ini +++ /dev/null @@ -1,48 +0,0 @@ -[event-composed-path-with-related-target.html] - type: testharness - expected: ERROR - [Event path for an event with a relatedTarget. relatedTarget != target.] - expected: FAIL - - [Event path for an event with a relatedTarget. Event shoul be dispatched if 1) target and relatedTarget are same, and 2) they are not in a shadow tree.] - expected: FAIL - - [Event path for an event with a relatedTarget. Event should stop at the shadow root] - expected: FAIL - - [Event path for an event with a relatedTarget. Event should not be dispatched if 1) target and relatedTarget are same, and 2) both are in a shadow tree.] - expected: FAIL - - [Event path for an event with a relatedTarget. target and relaterTarget do not share any shadow-including ancestor. target is in a shadow tree.] - expected: FAIL - - [Event path for an event with a relatedTarget. target and relaterTarget do not share any shadow-including ancestor. target is not in a shadow tree] - expected: FAIL - - [Event path for an event with a relatedTarget. target and relaterTarget share the same shadow-including ancestor. Both are in shadow trees.] - expected: FAIL - - [Event path for an event with a relatedTarget. relaterTarget is a shadow-including ancestor of target.] - expected: FAIL - - [Event path for an event with a relatedTarget. target is a shadow-including ancestor of relatedTarget.] - expected: FAIL - - [Event path for an event with a relatedTarget. target is assigned to a slot.] - expected: FAIL - - [Event path for an event with a relatedTarget. relatedTarget is assigned to a slot.] - expected: FAIL - - [Event path for an event with a relatedTarget. Event should be dispatched at every slots.] - expected: FAIL - - [Event path for an event with a relatedTarget. Event should be dispatched at every slots. relatedTarget should be correctly retargeted.] - expected: FAIL - - [Event path for an event with a relatedTarget. Event should be dispatched even when target and relatedTarget are same.] - expected: FAIL - - [Event path for an event with a relatedTarget which is identical to target. Event should be dispatched and should stop at the shadow root.] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/event-composed-path.html.ini b/testing/web-platform/meta/shadow-dom/event-composed-path.html.ini index ecd6e30944..0566bf4f31 100644 --- a/testing/web-platform/meta/shadow-dom/event-composed-path.html.ini +++ b/testing/web-platform/meta/shadow-dom/event-composed-path.html.ini @@ -1,36 +1,9 @@ [event-composed-path.html] type: testharness - expected: ERROR - [Event Path without ShadowRoots.] - expected: FAIL - - [Event Path with an open ShadowRoot.] - expected: FAIL - - [Event Path with a closed ShadowRoot.] - expected: FAIL - - [Event Path with nested ShadowRoots: open > open.] - expected: FAIL - - [Event Path with nested ShadowRoots: open > closed.] - expected: FAIL - - [Event Path with nested ShadowRoots: closed > open.] - expected: FAIL - - [Event Path with nested ShadowRoots: closed > closed.] - expected: FAIL - - [Event Path with a slot in an open Shadow Root.] - expected: FAIL [Event Path with a slot in a closed Shadow Root.] expected: FAIL - [Event Path with slots in nested ShadowRoots: open > open.] - expected: FAIL - [Event Path with slots in nested ShadowRoots: closed > closed.] expected: FAIL diff --git a/testing/web-platform/meta/shadow-dom/event-composed.html.ini b/testing/web-platform/meta/shadow-dom/event-composed.html.ini deleted file mode 100644 index 6386a51c59..0000000000 --- a/testing/web-platform/meta/shadow-dom/event-composed.html.ini +++ /dev/null @@ -1,23 +0,0 @@ -[event-composed.html] - type: testharness - [An event should be scoped by default] - expected: FAIL - - [An event should not be scoped if composed is specified] - expected: FAIL - - [A synthetic MouseEvent should be scoped by default] - expected: FAIL - - [A synthetic MouseEvent with composed=true should not be scoped] - expected: FAIL - - [A synthetic FocusEvent should be scoped by default] - expected: FAIL - - [A synthetic FocusEvent with composed=true should not be scoped] - expected: FAIL - - [A UA click event should not be scoped] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/event-inside-slotted-node.html.ini b/testing/web-platform/meta/shadow-dom/event-inside-slotted-node.html.ini deleted file mode 100644 index 7e42670a30..0000000000 --- a/testing/web-platform/meta/shadow-dom/event-inside-slotted-node.html.ini +++ /dev/null @@ -1,38 +0,0 @@ -[event-inside-slotted-node.html] - type: testharness - [Firing an event inside a grand child of a detached open mode shadow host] - expected: FAIL - - [Firing an event inside a grand child of a detached closed mode shadow host] - expected: FAIL - - [Firing an event inside a grand child of an in-document open mode shadow host] - expected: FAIL - - [Firing an event inside a grand child of an in-document closed mode shadow host] - expected: FAIL - - [Firing an event on a node with two ancestors with a detached open and open shadow trees with an inner open shadow tree] - expected: FAIL - - [Firing an event on a node with two ancestors with a detached open and open shadow trees with an inner closed shadow tree] - expected: FAIL - - [Firing an event on a node with two ancestors with a detached open and closed shadow trees with an inner open shadow tree] - expected: FAIL - - [Firing an event on a node with two ancestors with a detached open and closed shadow trees with an inner closed shadow tree] - expected: FAIL - - [Firing an event on a node with two ancestors with a detached closed and open shadow trees with an inner open shadow tree] - expected: FAIL - - [Firing an event on a node with two ancestors with a detached closed and open shadow trees with an inner closed shadow tree] - expected: FAIL - - [Firing an event on a node with two ancestors with a detached closed and closed shadow trees with an inner open shadow tree] - expected: FAIL - - [Firing an event on a node with two ancestors with a detached closed and closed shadow trees with an inner closed shadow tree] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/scroll-to-the-fragment-in-shadow-tree.html.ini b/testing/web-platform/meta/shadow-dom/scroll-to-the-fragment-in-shadow-tree.html.ini deleted file mode 100644 index bdcb1e0e12..0000000000 --- a/testing/web-platform/meta/shadow-dom/scroll-to-the-fragment-in-shadow-tree.html.ini +++ /dev/null @@ -1,26 +0,0 @@ -[scroll-to-the-fragment-in-shadow-tree.html] - type: testharness - [The user agent should not scroll to an element with an ID exactly equal to the decoded fragid in an open shadow tree] - expected: FAIL - - [The user agent should not scroll to an element with an ID exactly equal to the decoded fragid in a closed shadow tree] - expected: FAIL - - [The user agent should not scroll to an anchor element with a name attribute exactly equal to the decoded fragid in an open shadow tree] - expected: FAIL - - [The user agent should not scroll to an anchor element with a name attribute exactly equal to the decoded fragid in a closed shadow tree] - expected: FAIL - - [The user agent should scroll to an element with an ID exactly equal to the decoded fragid in the document tree even if there was another element with the same ID inside an open shadow tree earlier in tree order] - expected: FAIL - - [The user agent should scroll to an element with an ID exactly equal to the decoded fragid in the document tree even if there was another element with the same ID inside a closed shadow tree earlier in tree order] - expected: FAIL - - [The user agent should scroll to an anchor element with a name attribute exactly equal to the decoded fragid in the document tree even if there was another element with the same ID inside an open shadow tree earlier in tree order] - expected: FAIL - - [The user agent should scroll to an anchor element with a name attribute exactly equal to the decoded fragid in the document tree even if there was another element with the same ID inside a closed shadow tree earlier in tree order] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/slotchange-event.html.ini b/testing/web-platform/meta/shadow-dom/slotchange-event.html.ini deleted file mode 100644 index 434e7d9ba6..0000000000 --- a/testing/web-platform/meta/shadow-dom/slotchange-event.html.ini +++ /dev/null @@ -1,99 +0,0 @@ -[slotchange-event.html] - type: testharness - expected: ERROR - [slotchange event must fire on a default slot element inside an open shadow root in a document] - expected: FAIL - - [slotchange event must fire on a default slot element inside a closed shadow root in a document] - expected: FAIL - - [slotchange event must fire on a default slot element inside an open shadow root not in a document] - expected: FAIL - - [slotchange event must fire on a default slot element inside a closed shadow root not in a document] - expected: FAIL - - [slotchange event must fire on a named slot element insidean open shadow root in a document] - expected: FAIL - - [slotchange event must fire on a named slot element insidea closed shadow root in a document] - expected: FAIL - - [slotchange event must fire on a named slot element insidean open shadow root not in a document] - expected: FAIL - - [slotchange event must fire on a named slot element insidea closed shadow root not in a document] - expected: FAIL - - [slotchange event must not fire on a slot element inside an open shadow root in a document when another slot's assigned nodes change] - expected: FAIL - - [slotchange event must not fire on a slot element inside a closed shadow root in a document when another slot's assigned nodes change] - expected: FAIL - - [slotchange event must not fire on a slot element inside an open shadow root not in a document when another slot's assigned nodes change] - expected: FAIL - - [slotchange event must not fire on a slot element inside a closed shadow root not in a document when another slot's assigned nodes change] - expected: FAIL - - [slotchange event must not fire on a slot element inside an open shadow root in a document when the shadow host was mutated before the slot was inserted or after the slot was removed] - expected: FAIL - - [slotchange event must not fire on a slot element inside a closed shadow root in a document when the shadow host was mutated before the slot was inserted or after the slot was removed] - expected: FAIL - - [slotchange event must not fire on a slot element inside an open shadow root not in a document when the shadow host was mutated before the slot was inserted or after the slot was removed] - expected: FAIL - - [slotchange event must not fire on a slot element inside a closed shadow root not in a document when the shadow host was mutated before the slot was inserted or after the slot was removed] - expected: FAIL - - [slotchange event must fire on a slot element inside an open shadow root in a document even if the slot was removed immediately after the assigned nodes were mutated] - expected: FAIL - - [slotchange event must fire on a slot element inside a closed shadow root in a document even if the slot was removed immediately after the assigned nodes were mutated] - expected: FAIL - - [slotchange event must fire on a slot element inside an open shadow root not in a document even if the slot was removed immediately after the assigned nodes were mutated] - expected: FAIL - - [slotchange event must fire on a slot element inside a closed shadow root not in a document even if the slot was removed immediately after the assigned nodes were mutated] - expected: FAIL - - [slotchange event must fire on a slot element inside an open shadow root in a document when innerHTML modifies the children of the shadow host] - expected: FAIL - - [slotchange event must fire on a slot element inside a closed shadow root in a document when innerHTML modifies the children of the shadow host] - expected: FAIL - - [slotchange event must fire on a slot element inside an open shadow root not in a document when innerHTML modifies the children of the shadow host] - expected: FAIL - - [slotchange event must fire on a slot element inside a closed shadow root not in a document when innerHTML modifies the children of the shadow host] - expected: FAIL - - [slotchange event must fire on a slot element inside an open shadow root in a document when nested slots's contents change] - expected: FAIL - - [slotchange event must fire on a slot element inside a closed shadow root in a document when nested slots's contents change] - expected: FAIL - - [slotchange event must fire on a slot element inside an open shadow root not in a document when nested slots's contents change] - expected: FAIL - - [slotchange event must fire on a slot element inside a closed shadow root not in a document when nested slots's contents change] - expected: FAIL - - [slotchange event must fire at the end of current microtask after mutation observers are invoked inside an open shadow root in a document when slots's contents change] - expected: FAIL - - [slotchange event must fire at the end of current microtask after mutation observers are invoked inside a closed shadow root in a document when slots's contents change] - expected: FAIL - - [slotchange event must fire at the end of current microtask after mutation observers are invoked inside an open shadow root not in a document when slots's contents change] - expected: FAIL - - [slotchange event must fire at the end of current microtask after mutation observers are invoked inside a closed shadow root not in a document when slots's contents change] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/slotchange.html.ini b/testing/web-platform/meta/shadow-dom/slotchange.html.ini deleted file mode 100644 index fff6fa28de..0000000000 --- a/testing/web-platform/meta/shadow-dom/slotchange.html.ini +++ /dev/null @@ -1,47 +0,0 @@ -[slotchange.html] - type: testharness - [slotchange event: Append a child to a host.] - expected: FAIL - - [slotchange event: Remove a child from a host.] - expected: FAIL - - [slotchange event: Remove a child before adding an event listener.] - expected: FAIL - - [slotchange event: Change slot= attribute to make it un-assigned.] - expected: FAIL - - [slotchange event: Change slot's name= attribute so that none is assigned.] - expected: FAIL - - [slotchange event: Change slot= attribute to make it assigned.] - expected: FAIL - - [slotchange event: Change slot's name= attribute so that a node is assigned to the slot.] - expected: FAIL - - [slotchange event: Add a fallback content.] - expected: FAIL - - [slotchange event: Remove a fallback content.] - expected: FAIL - - [slotchange event: Add a fallback content to nested slots.] - expected: FAIL - - [slotchange event: Remove a fallback content from nested slots.] - expected: FAIL - - [slotchange event: Insert a slot before an existing slot.] - expected: FAIL - - [slotchange event: Remove a preceding slot.] - expected: FAIL - - [slotchange event: A slot is assigned to another slot.] - expected: FAIL - - [slotchange event: Even if distributed nodes do not change, slotchange should be fired if assigned nodes are changed.] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/slots-fallback.html.ini b/testing/web-platform/meta/shadow-dom/slots-fallback.html.ini deleted file mode 100644 index d32ae812b8..0000000000 --- a/testing/web-platform/meta/shadow-dom/slots-fallback.html.ini +++ /dev/null @@ -1,32 +0,0 @@ -[slots-fallback.html] - type: testharness - [Slots fallback: Basic.] - expected: FAIL - - [Slots fallback: Slots in Slots.] - expected: FAIL - - [Slots fallback: Fallback contents should not be used if a node is assigned.] - expected: FAIL - - [Slots fallback: Slots in Slots: Assinged nodes should be used as fallback contents of another slot] - expected: FAIL - - [Slots fallback: Complex case.] - expected: FAIL - - [Slots fallback: Mutation. Append fallback contents.] - expected: FAIL - - [Slots fallback: Mutation. Remove fallback contents.] - expected: FAIL - - [Slots fallback: Mutation. Assign a node to a slot so that fallback contens are no longer used.] - expected: FAIL - - [Slots fallback: Mutation. Remove an assigned node from a slot so that fallback contens will be used.] - expected: FAIL - - [Slots fallback: Mutation. Remove a slot which is a fallback content of another slot.] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/slots.html.ini b/testing/web-platform/meta/shadow-dom/slots.html.ini deleted file mode 100644 index 3c047e4825..0000000000 --- a/testing/web-platform/meta/shadow-dom/slots.html.ini +++ /dev/null @@ -1,71 +0,0 @@ -[slots.html] - type: testharness - [Slots: Basic.] - expected: FAIL - - [Slots: Slots in closed.] - expected: FAIL - - [Slots: Slots not in a shadow tree.] - expected: FAIL - - [Slots: Distributed nooes for Slots not in a shadow tree.] - expected: FAIL - - [Slots: Name matching] - expected: FAIL - - [Slots: No direct host child.] - expected: FAIL - - [Slots: Default Slot.] - expected: FAIL - - [Slots: Slot in Slot does not matter in assignment.] - expected: FAIL - - [Slots: Slot is assigned to another slot] - expected: FAIL - - [Slots: Open > Closed.] - expected: FAIL - - [Slots: Closed > Closed.] - expected: FAIL - - [Slots: Closed > Open.] - expected: FAIL - - [Slots: Complex case: Basi line.] - expected: FAIL - - [Slots: Mutation: appendChild.] - expected: FAIL - - [Slots: Mutation: Change slot= attribute 1.] - expected: FAIL - - [Slots: Mutation: Change slot= attribute 2.] - expected: FAIL - - [Slots: Mutation: Change slot= attribute 3.] - expected: FAIL - - [Slots: Mutation: Remove a child.] - expected: FAIL - - [Slots: Mutation: Add a slot: after.] - expected: FAIL - - [Slots: Mutation: Add a slot: before.] - expected: FAIL - - [Slots: Mutation: Remove a slot.] - expected: FAIL - - [Slots: Mutation: Change slot name= attribute.] - expected: FAIL - - [Slots: Mutation: Change slot slot= attribute.] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/extensions-to-element-interface/methods/test-001.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/extensions-to-element-interface/methods/test-001.html.ini deleted file mode 100644 index 5b634b0f2a..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/extensions-to-element-interface/methods/test-001.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[test-001.html] - type: testharness - [A_10_02_02_01_T01] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/extensions-to-element-interface/methods/test-002.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/extensions-to-element-interface/methods/test-002.html.ini deleted file mode 100644 index 2d6ef002d0..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/extensions-to-element-interface/methods/test-002.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[test-002.html] - type: testharness - [A_10_02_02_02_T01] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-009.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-009.html.ini deleted file mode 100644 index 8f7d063c5d..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-009.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[test-009.html] - type: testharness - [A_10_01_01_04_01_T01] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-010.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-010.html.ini deleted file mode 100644 index ec74acf73a..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-010.html.ini +++ /dev/null @@ -1,8 +0,0 @@ -[test-010.html] - type: testharness - [A_10_01_01_04_02_T01_01] - expected: FAIL - - [A_10_01_01_04_02_T01_02] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-011.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-011.html.ini deleted file mode 100644 index a432c4eb24..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-011.html.ini +++ /dev/null @@ -1,8 +0,0 @@ -[test-011.html] - type: testharness - [A_10_01_01_05_01_T01] - expected: FAIL - - [A_10_01_01_05_01_T02] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-012.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-012.html.ini deleted file mode 100644 index 6f557d9e19..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-012.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[test-012.html] - type: testharness - [A_10_01_01_06_T01] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-013.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-013.html.ini deleted file mode 100644 index 2ebe02cf03..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-013.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[test-013.html] - type: testharness - [A_10_01_01_07_T01] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-methods/test-001.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-methods/test-001.html.ini deleted file mode 100644 index 20eebc9a82..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-methods/test-001.html.ini +++ /dev/null @@ -1,8 +0,0 @@ -[test-001.html] - type: testharness - [A_10_01_02_01_T01] - expected: FAIL - - [A_10_01_02_01_T02] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/events/event-dispatch/test-002.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/events/event-dispatch/test-002.html.ini deleted file mode 100644 index 3c43986e95..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/events/event-dispatch/test-002.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[test-002.html] - type: testharness - [A_05_05_02_T01] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/events/event-retargeting/test-001.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/events/event-retargeting/test-001.html.ini deleted file mode 100644 index 47d44deea5..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/events/event-retargeting/test-001.html.ini +++ /dev/null @@ -1,8 +0,0 @@ -[test-001.html] - type: testharness - [A_05_01_01_T1] - expected: FAIL - - [A_05_01_01_T2] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/events/event-retargeting/test-003.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/events/event-retargeting/test-003.html.ini deleted file mode 100644 index 8bbb1b71e2..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/events/event-retargeting/test-003.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[test-003.html] - type: testharness - [A_05_01_03_T01] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/events/retargeting-focus-events/test-001.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/events/retargeting-focus-events/test-001.html.ini deleted file mode 100644 index 891b6d3489..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/events/retargeting-focus-events/test-001.html.ini +++ /dev/null @@ -1,20 +0,0 @@ -[test-001.html] - type: testharness - [A_05_03_01_T01] - expected: FAIL - - [A_05_03_01_T02] - expected: FAIL - - [A_05_03_01_T03] - expected: FAIL - - [A_05_03_01_T04] - expected: FAIL - - [A_05_03_01_T05] - expected: FAIL - - [A_05_03_01_T06] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/events/retargeting-focus-events/test-002.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/events/retargeting-focus-events/test-002.html.ini deleted file mode 100644 index c55148ccc4..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/events/retargeting-focus-events/test-002.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[test-002.html] - type: testharness - [A_05_03_02_T01] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/events/retargeting-focus-events/test-003.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/events/retargeting-focus-events/test-003.html.ini deleted file mode 100644 index c6a45e93a8..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/events/retargeting-focus-events/test-003.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[test-003.html] - type: testharness - [A_05_03_03_T01] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/events/retargeting-relatedtarget/test-003.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/events/retargeting-relatedtarget/test-003.html.ini deleted file mode 100644 index 1c1377023b..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/events/retargeting-relatedtarget/test-003.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[test-003.html] - type: testharness - [A_05_02_03_T01] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/html-elements-in-shadow-trees/html-forms/test-001.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/html-elements-in-shadow-trees/html-forms/test-001.html.ini deleted file mode 100644 index fbdf5237ad..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/html-elements-in-shadow-trees/html-forms/test-001.html.ini +++ /dev/null @@ -1,8 +0,0 @@ -[test-001.html] - type: testharness - [A_08_02_01_T01] - expected: FAIL - - [A_08_02_01_T02] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/html-elements-in-shadow-trees/html-forms/test-002.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/html-elements-in-shadow-trees/html-forms/test-002.html.ini deleted file mode 100644 index 4eb48c20d4..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/html-elements-in-shadow-trees/html-forms/test-002.html.ini +++ /dev/null @@ -1,11 +0,0 @@ -[test-002.html] - type: testharness - [A_08_02_02_T01] - expected: FAIL - - [A_08_02_02_T02] - expected: FAIL - - [A_08_02_02_T03] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/nested-shadow-trees/nested_tree_reftest.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/nested-shadow-trees/nested_tree_reftest.html.ini deleted file mode 100644 index 5885c67284..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/nested-shadow-trees/nested_tree_reftest.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[nested_tree_reftest.html] - type: reftest - expected: FAIL diff --git a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/shadow-root-001.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/shadow-root-001.html.ini deleted file mode 100644 index dc0596145a..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/shadow-root-001.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[shadow-root-001.html] - type: reftest - expected: FAIL diff --git a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/dom-tree-accessors-001.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/dom-tree-accessors-001.html.ini deleted file mode 100644 index 9447e394a6..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/dom-tree-accessors-001.html.ini +++ /dev/null @@ -1,44 +0,0 @@ -[dom-tree-accessors-001.html] - type: testharness - [<head> and <body> in a shadow tree should not be accessible from owner document's "head" and "body" properties, respectively.] - expected: FAIL - - [The content of title element in a shadow tree should not be accessible from owner document's "title" attribute.] - expected: FAIL - - [Elements in a shadow tree should not be accessible from owner document's "images" attribute.] - expected: FAIL - - [Elements in a shadow tree should not be accessible from owner document's "embeds" attribute.] - expected: FAIL - - [Elements in a shadow tree should not be accessible from owner document's "plugins" attribute.] - expected: FAIL - - [Elements in a shadow tree should not be accessible from owner document's "links" attribute.] - expected: FAIL - - [Elements in a shadow tree should not be accessible from owner document's "forms" attribute.] - expected: FAIL - - [Elements in a shadow tree should not be accessible from owner document's "scripts" attribute.] - expected: FAIL - - [Elements in a shadow tree should not be accessible from owner document's getElementsByName() method.] - expected: FAIL - - [Elements in a shadow tree should not be accessible from owner document's "anchors" attribute.] - expected: FAIL - - [Elements in a shadow tree should not be accessible from owner document's "all" attribute.] - expected: FAIL - - [Elements in a shadow tree should not be accessible from owner document's getElementsByTagName() method.] - expected: FAIL - - [Elements in a shadow tree should not be accessible from owner document's getElementsByTagNameNS() method.] - expected: FAIL - - [Elements in a shadow tree should not be accessible from owner document's getElementById() method.] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/dom-tree-accessors-002.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/dom-tree-accessors-002.html.ini deleted file mode 100644 index 485b129a1f..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/dom-tree-accessors-002.html.ini +++ /dev/null @@ -1,11 +0,0 @@ -[dom-tree-accessors-002.html] - type: testharness - [Elements in a shadow tree should be accessible via shadow root's querySelectorAll() DOM tree accessor.] - expected: FAIL - - [Elements with a specific class in a shadow tree should be accessible viashadow root's querySelectorAll() DOM tree accessor.] - expected: FAIL - - [Elements in a shadow tree should be accessible via shadow root's getElementById() DOM tree accessor.] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/ownerdocument-001.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/ownerdocument-001.html.ini deleted file mode 100644 index f2caee337e..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/ownerdocument-001.html.ini +++ /dev/null @@ -1,20 +0,0 @@ -[ownerdocument-001.html] - type: testharness - [ownerDocument property of a shadow root should be the document of the shadow host, regardless of the location of the shadow host.] - expected: FAIL - - [ownerDocument property of elements in a shadow tree should match the document of the shadow host, regardless of the element's location in a shadow tree.] - expected: FAIL - - [Elements added to a shadow tree should automatically get a valid ownerDocument.] - expected: FAIL - - [ownerDocument property of an element in a shadow tree should be the document of the shadow host, even if the host element is created from another document.] - expected: FAIL - - [All children nodes of a shadow root get a valid ownerDocument when added to a shadow tree.] - expected: FAIL - - [ownerDocument property of a node should remain the same, even if its child is adopted into a shadow tree.] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/ownerdocument-002.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/ownerdocument-002.html.ini deleted file mode 100644 index 986aa9d030..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/ownerdocument-002.html.ini +++ /dev/null @@ -1,53 +0,0 @@ -[ownerdocument-002.html] - type: testharness - [ownerDocument property of any elements in a shadow tree should match the document of the shadow host, when the host is a "article" element.] - expected: FAIL - - [ownerDocument property of any elements in a shadow tree should match the document of the shadow host, when the host is a "aside" element.] - expected: FAIL - - [ownerDocument property of any elements in a shadow tree should match the document of the shadow host, when the host is a "blockquote" element.] - expected: FAIL - - [ownerDocument property of any elements in a shadow tree should match the document of the shadow host, when the host is a "body" element.] - expected: FAIL - - [ownerDocument property of any elements in a shadow tree should match the document of the shadow host, when the host is a "div" element.] - expected: FAIL - - [ownerDocument property of any elements in a shadow tree should match the document of the shadow host, when the host is a "footer" element.] - expected: FAIL - - [ownerDocument property of any elements in a shadow tree should match the document of the shadow host, when the host is a "h1" element.] - expected: FAIL - - [ownerDocument property of any elements in a shadow tree should match the document of the shadow host, when the host is a "h2" element.] - expected: FAIL - - [ownerDocument property of any elements in a shadow tree should match the document of the shadow host, when the host is a "h3" element.] - expected: FAIL - - [ownerDocument property of any elements in a shadow tree should match the document of the shadow host, when the host is a "h4" element.] - expected: FAIL - - [ownerDocument property of any elements in a shadow tree should match the document of the shadow host, when the host is a "h5" element.] - expected: FAIL - - [ownerDocument property of any elements in a shadow tree should match the document of the shadow host, when the host is a "h6" element.] - expected: FAIL - - [ownerDocument property of any elements in a shadow tree should match the document of the shadow host, when the host is a "header" element.] - expected: FAIL - - [ownerDocument property of any elements in a shadow tree should match the document of the shadow host, when the host is a "nav" element.] - expected: FAIL - - [ownerDocument property of any elements in a shadow tree should match the document of the shadow host, when the host is a "p" element.] - expected: FAIL - - [ownerDocument property of any elements in a shadow tree should match the document of the shadow host, when the host is a "section" element.] - expected: FAIL - - [ownerDocument property of any elements in a shadow tree should match the document of the shadow host, when the host is a "span" element.] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/selectors-api-001.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/selectors-api-001.html.ini deleted file mode 100644 index 6f69dcd117..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/selectors-api-001.html.ini +++ /dev/null @@ -1,8 +0,0 @@ -[selectors-api-001.html] - type: testharness - [Elements in a shadow tree should not be accessible from owner document's querySelector() method.] - expected: FAIL - - [Elements in a shadow tree should not be accessible from owner document's querySelectorAll() method.] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/selectors-api-002.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/selectors-api-002.html.ini deleted file mode 100644 index fbf20127e5..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/selectors-api-002.html.ini +++ /dev/null @@ -1,8 +0,0 @@ -[selectors-api-002.html] - type: testharness - [Elements in a shadow tree should be accessible from shadow root's querySelector() method.] - expected: FAIL - - [Elements in a shadow tree should be accessible from shadow root's querySelectorAll() method.] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/shadow-root-001.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/shadow-root-001.html.ini deleted file mode 100644 index e432d7ccf7..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/shadow-root-001.html.ini +++ /dev/null @@ -1,14 +0,0 @@ -[shadow-root-001.html] - type: testharness - [The parentNode attribute of a shadow root must always return null.] - expected: FAIL - - [The parentElement attribute of a shadow root must always return null.] - expected: FAIL - - [The parentNode attribute of a shadow root must always return null, even if the shadow root is nested inside another shadow root.] - expected: FAIL - - [The parentElement attribute of a shadow root must always return null, even if the shadow root is nested inside another shadow root.] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/test-005.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/test-005.html.ini deleted file mode 100644 index 7d0423c0d0..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/test-005.html.ini +++ /dev/null @@ -1,8 +0,0 @@ -[test-005.html] - type: testharness - [A_04_01_05_T02] - expected: FAIL - - [A_04_01_05_T01] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/test-007.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/test-007.html.ini index 53b7bd49b5..97b9fd544a 100644 --- a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/test-007.html.ini +++ b/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/test-007.html.ini @@ -1,8 +1,5 @@ [test-007.html] type: testharness - [A_04_01_07_T01] - expected: FAIL - [A_04_01_07_T02] expected: FAIL diff --git a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/test-009.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/test-009.html.ini deleted file mode 100644 index 9071f887fd..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/test-009.html.ini +++ /dev/null @@ -1,44 +0,0 @@ -[test-009.html] - type: testharness - [A_04_01_09_T12] - expected: FAIL - - [A_04_01_09_T01] - expected: FAIL - - [A_04_01_09_T03] - expected: FAIL - - [A_04_01_09_T05] - expected: FAIL - - [A_04_01_09_T06] - expected: FAIL - - [A_04_01_09_T07] - expected: FAIL - - [A_04_01_09_T08] - expected: FAIL - - [A_04_01_09_T09] - expected: FAIL - - [A_04_01_09_T10] - expected: FAIL - - [A_04_01_09_T11] - expected: FAIL - - [A_04_01_09_T13] - expected: FAIL - - [A_04_01_09_T14] - expected: FAIL - - [A_04_01_09_T15] - expected: FAIL - - [A_04_01_09_T16] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/test-011.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/test-011.html.ini deleted file mode 100644 index 97f503d6fe..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/test-011.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[test-011.html] - type: testharness - [A_04_01_11_T2] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/window-named-properties-001.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/window-named-properties-001.html.ini deleted file mode 100644 index fd7b39bbbd..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/window-named-properties-001.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[window-named-properties-001.html] - type: testharness - [An iframe element in a shadow tree should not be accessible from window's named properties with its "name" attribute value.] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/window-named-properties-002.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/window-named-properties-002.html.ini deleted file mode 100644 index fddab58d3d..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/window-named-properties-002.html.ini +++ /dev/null @@ -1,26 +0,0 @@ -[window-named-properties-002.html] - type: testharness - ["a" element with name attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["applet" element with name attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["area" element with name attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["embed" element with name attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["form" element with name attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["frameset" element with name attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["img" element with name attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["object" element with name attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/window-named-properties-003.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/window-named-properties-003.html.ini deleted file mode 100644 index b9ca5b161e..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/upper-boundary-encapsulation/window-named-properties-003.html.ini +++ /dev/null @@ -1,323 +0,0 @@ -[window-named-properties-003.html] - type: testharness - ["a" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["abbr" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["address" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["area" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["article" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["aside" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["audio" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["b" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["base" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["bdi" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["bdo" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["blockquote" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["body" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["br" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["button" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["canvas" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["caption" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["cite" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["code" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["col" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["colgroup" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["command" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["datalist" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["dd" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["del" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["details" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["dfn" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["dialog" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["div" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["dl" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["dt" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["em" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["embed" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["fieldset" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["figcaption" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["figure" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["footer" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["form" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["h1" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["h2" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["h3" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["h4" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["h5" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["h6" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["head" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["header" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["hgroup" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["hr" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["html" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["i" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["iframe" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["img" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["input" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["ins" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["kbd" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["keygen" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["label" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["legend" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["li" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["link" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["map" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["mark" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["menu" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["meta" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["meter" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["nav" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["noscript" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["object" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["ol" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["optgroup" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["option" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["output" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["p" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["param" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["pre" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["progress" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["q" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["rp" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["rt" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["ruby" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["s" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["samp" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["script" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["section" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["select" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["small" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["source" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["span" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["strong" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["style" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["sub" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["table" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["tbody" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["td" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["textarea" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["tfoot" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["th" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["thead" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["time" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["title" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["tr" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["track" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["u" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["ul" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["var" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["video" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - - ["wbr" element with id attribute in a shadow tree should not be accessible from window object's named property.] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/styles/test-001.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/styles/test-001.html.ini deleted file mode 100644 index a621f0950a..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/styles/test-001.html.ini +++ /dev/null @@ -1,14 +0,0 @@ -[test-001.html] - type: testharness - [A_06_00_01_T01] - expected: FAIL - - [A_06_00_01_T02] - expected: FAIL - - [A_06_00_01_T03] - expected: FAIL - - [A_06_00_01_T04] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/styles/test-003.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/styles/test-003.html.ini deleted file mode 100644 index e610bc9b3e..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/styles/test-003.html.ini +++ /dev/null @@ -1,11 +0,0 @@ -[test-003.html] - type: testharness - [A_06_00_03_T01] - expected: FAIL - - [A_06_00_03_T02] - expected: FAIL - - [A_06_00_03_T03] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/styles/test-008.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/styles/test-008.html.ini deleted file mode 100644 index 79d7bd1bae..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/styles/test-008.html.ini +++ /dev/null @@ -1,8 +0,0 @@ -[test-008.html] - type: testharness - [A_06_00_09_T01] - expected: FAIL - - [A_06_00_09_T02] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/user-interaction/editing/inheritance-of-content-editable-001.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/user-interaction/editing/inheritance-of-content-editable-001.html.ini deleted file mode 100644 index b43073a876..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/user-interaction/editing/inheritance-of-content-editable-001.html.ini +++ /dev/null @@ -1,11 +0,0 @@ -[inheritance-of-content-editable-001.html] - type: testharness - [contentEditable of shadow trees must be undefined when contentEditable attribute of shadow host is "true"] - expected: FAIL - - [contentEditable of shadow trees must be undefined when contentEditable of shadow host is "false"] - expected: FAIL - - [contentEditable of shadow trees must be undefined when contentEditable attribute of shadow host is "inherit"] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/user-interaction/ranges-and-selections/test-001.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/user-interaction/ranges-and-selections/test-001.html.ini deleted file mode 100644 index 67493fb912..0000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/user-interaction/ranges-and-selections/test-001.html.ini +++ /dev/null @@ -1,8 +0,0 @@ -[test-001.html] - type: testharness - [A_07_07_01_T01] - expected: FAIL - - [A_07_07_01_T02] - expected: FAIL - diff --git a/testing/web-platform/tests/css-display-3/display-contents-dynamic-pseudo-insertion-001-ref.html b/testing/web-platform/tests/css-display-3/display-contents-dynamic-pseudo-insertion-001-ref.html new file mode 100644 index 0000000000..9c59597695 --- /dev/null +++ b/testing/web-platform/tests/css-display-3/display-contents-dynamic-pseudo-insertion-001-ref.html @@ -0,0 +1,5 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSS Test Reference</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +PASS diff --git a/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp b/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp index 53b1ef66d7..4690383ab0 100644 --- a/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp +++ b/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp @@ -822,7 +822,7 @@ nsTypeAheadFind::GetSearchContainers(nsISupports *aContainer, getter_AddRefs(mStartPointRange), nullptr); } else { - int32_t startOffset; + uint32_t startOffset; nsCOMPtr<nsIDOMNode> startNode; if (aFindPrev) { currentSelectionRange->GetStartContainer(getter_AddRefs(startNode)); @@ -860,7 +860,7 @@ nsTypeAheadFind::RangeStartsInsideLink(nsIDOMRange *aRange, nsCOMPtr<nsIDOMNode> startNode; nsCOMPtr<nsIContent> startContent, origContent; aRange->GetStartContainer(getter_AddRefs(startNode)); - int32_t startOffset; + uint32_t startOffset; aRange->GetStartOffset(&startOffset); startContent = do_QueryInterface(startNode); @@ -880,9 +880,10 @@ nsTypeAheadFind::RangeStartsInsideLink(nsIDOMRange *aRange, const nsTextFragment *textFrag = startContent->GetText(); if (textFrag) { // look for non whitespace character before start offset - for (int32_t index = 0; index < startOffset; index++) { + for (uint32_t index = 0; index < startOffset; index++) { // FIXME: take content language into account when deciding whitespace. - if (!mozilla::dom::IsSpaceCharacter(textFrag->CharAt(index))) { + if (!mozilla::dom::IsSpaceCharacter( + textFrag->CharAt(static_cast<int32_t>(index)))) { *aIsStartingLink = false; // not at start of a node break; @@ -1227,12 +1228,14 @@ nsTypeAheadFind::IsRangeVisible(nsIPresShell *aPresShell, return true; // Don't need it to be on screen, just in rendering tree // Get the next in flow frame that contains the range start - int32_t startRangeOffset, startFrameOffset, endFrameOffset; + int32_t startFrameOffset, endFrameOffset; + uint32_t startRangeOffset; aRange->GetStartOffset(&startRangeOffset); while (true) { frame->GetOffsets(startFrameOffset, endFrameOffset); - if (startRangeOffset < endFrameOffset) + if (static_cast<int32_t>(startRangeOffset) < endFrameOffset) { break; + } nsIFrame *nextContinuationFrame = frame->GetNextContinuation(); if (nextContinuationFrame) diff --git a/widget/BasicEvents.h b/widget/BasicEvents.h index 960cb67c68..0ce0f587dd 100644 --- a/widget/BasicEvents.h +++ b/widget/BasicEvents.h @@ -25,6 +25,8 @@ struct ParamTraits; namespace mozilla { +class EventTargetChainItem; + /****************************************************************************** * mozilla::BaseEventFlags * @@ -350,6 +352,7 @@ protected: , mRefPoint(0, 0) , mLastRefPoint(0, 0) , mSpecifiedEventType(nullptr) + , mPath(nullptr) { MOZ_COUNT_CTOR(WidgetEvent); mFlags.Clear(); @@ -361,6 +364,7 @@ protected: WidgetEvent() : WidgetEventTime() + , mPath(nullptr) { MOZ_COUNT_CTOR(WidgetEvent); } @@ -419,6 +423,11 @@ public: nsCOMPtr<dom::EventTarget> mCurrentTarget; nsCOMPtr<dom::EventTarget> mOriginalTarget; + /// The possible related target + nsCOMPtr<dom::EventTarget> mRelatedTarget; + + nsTArray<EventTargetChainItem>* mPath; + dom::EventTarget* GetDOMEventTarget() const; dom::EventTarget* GetCurrentDOMEventTarget() const; dom::EventTarget* GetOriginalDOMEventTarget() const; @@ -436,6 +445,7 @@ public: mTarget = aCopyTargets ? aEvent.mTarget : nullptr; mCurrentTarget = aCopyTargets ? aEvent.mCurrentTarget : nullptr; mOriginalTarget = aCopyTargets ? aEvent.mOriginalTarget : nullptr; + mRelatedTarget = aCopyTargets ? aEvent.mRelatedTarget : nullptr; } /** diff --git a/widget/ContentEvents.h b/widget/ContentEvents.h index be64b7bebd..09d4d9928c 100644 --- a/widget/ContentEvents.h +++ b/widget/ContentEvents.h @@ -212,9 +212,6 @@ public: return result; } - /// The possible related target - nsCOMPtr<dom::EventTarget> mRelatedTarget; - bool mFromRaise; bool mIsRefocus; @@ -222,7 +219,6 @@ public: { AssignUIEventData(aEvent, aCopyTargets); - mRelatedTarget = aCopyTargets ? aEvent.mRelatedTarget : nullptr; mFromRaise = aEvent.mFromRaise; mIsRefocus = aEvent.mIsRefocus; } diff --git a/widget/MouseEvents.h b/widget/MouseEvents.h index 442ac41e82..4b8ff44ce6 100644 --- a/widget/MouseEvents.h +++ b/widget/MouseEvents.h @@ -110,9 +110,6 @@ public: MOZ_CRASH("WidgetMouseEventBase must not be most-subclass"); } - /// The possible related target - nsCOMPtr<nsISupports> relatedTarget; - enum buttonType { eLeftButton = 0, @@ -162,7 +159,6 @@ public: { AssignInputEventData(aEvent, aCopyTargets); - relatedTarget = aCopyTargets ? aEvent.relatedTarget : nullptr; button = aEvent.button; buttons = aEvent.buttons; pressure = aEvent.pressure; diff --git a/xpcom/base/nsIWeakReference.idl b/xpcom/base/nsIWeakReference.idl index 73390b15f7..7f6880fad8 100644 --- a/xpcom/base/nsIWeakReference.idl +++ b/xpcom/base/nsIWeakReference.idl @@ -37,6 +37,11 @@ interface nsIWeakReference : nsISupports %{C++ virtual size_t SizeOfOnlyThis(mozilla::MallocSizeOf aMallocSizeOf) const = 0; + + /** + * Returns true if the referring object is alive. Otherwise, false. + */ + virtual bool IsAlive() const = 0; %} }; diff --git a/xpcom/glue/nsWeakReference.cpp b/xpcom/glue/nsWeakReference.cpp index 57f372641e..be263e1c20 100644 --- a/xpcom/glue/nsWeakReference.cpp +++ b/xpcom/glue/nsWeakReference.cpp @@ -37,6 +37,7 @@ public: // nsIWeakReference... NS_DECL_NSIWEAKREFERENCE virtual size_t SizeOfOnlyThis(mozilla::MallocSizeOf aMallocSizeOf) const override; + bool IsAlive() const override { return mReferent != nullptr; } private: MOZ_WEAKREF_DECL_OWNINGTHREAD |