diff options
author | Matt A. Tobin <email@mattatobin.com> | 2020-04-14 21:50:13 -0400 |
---|---|---|
committer | Matt A. Tobin <email@mattatobin.com> | 2020-04-14 21:50:13 -0400 |
commit | bebec8fcb84dba6b684dfe1cc6c8a1e7741df374 (patch) | |
tree | 682329072ca4d617d06295c6576ba1c45c3db78e | |
parent | 5352b69a9286223272c0ed072900b4c78ba2ed7c (diff) | |
download | UXP-bebec8fcb84dba6b684dfe1cc6c8a1e7741df374.tar UXP-bebec8fcb84dba6b684dfe1cc6c8a1e7741df374.tar.gz UXP-bebec8fcb84dba6b684dfe1cc6c8a1e7741df374.tar.lz UXP-bebec8fcb84dba6b684dfe1cc6c8a1e7741df374.tar.xz UXP-bebec8fcb84dba6b684dfe1cc6c8a1e7741df374.zip |
Bug 1321284 - Crash in nsCSSFrameConstructor::GetInsertionPrevSibling when trying to reframe native anonymous content
* Make StyleChildrenIterator skip NAC generated by root element primary frame ancestors.
* Add nsINode::GetFlattenedTreeParentNodeForStyle.
* Add iterator class to find all restyle roots.
NOTE: Parts 1, 2, and "4.2"
Tag #1375
-rw-r--r-- | dom/base/ChildIterator.cpp | 16 | ||||
-rw-r--r-- | dom/base/ChildIterator.h | 13 | ||||
-rw-r--r-- | dom/base/Element.h | 2 | ||||
-rw-r--r-- | dom/base/ElementInlines.h | 11 | ||||
-rw-r--r-- | dom/base/FragmentOrElement.cpp | 41 | ||||
-rw-r--r-- | dom/base/nsContentUtils.cpp | 19 | ||||
-rw-r--r-- | dom/base/nsContentUtils.h | 10 | ||||
-rw-r--r-- | dom/base/nsIContent.h | 16 | ||||
-rw-r--r-- | dom/base/nsIContentInlines.h | 41 | ||||
-rw-r--r-- | dom/base/nsINode.h | 8 | ||||
-rw-r--r-- | layout/generic/nsIAnonymousContentCreator.h | 4 | ||||
-rw-r--r-- | layout/style/DocumentStyleRootIterator.cpp | 41 | ||||
-rw-r--r-- | layout/style/DocumentStyleRootIterator.h | 37 | ||||
-rw-r--r-- | layout/style/moz.build | 2 |
14 files changed, 226 insertions, 35 deletions
diff --git a/dom/base/ChildIterator.cpp b/dom/base/ChildIterator.cpp index d8c454ae8..fb07e9a21 100644 --- a/dom/base/ChildIterator.cpp +++ b/dom/base/ChildIterator.cpp @@ -383,12 +383,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); } } @@ -585,12 +583,6 @@ StyleChildrenIterator::IsNeeded(const Element* aElement) return true; } - // The root element has a scroll frame that is not the primary frame, so we - // need to do special checking for that case. - if (aElement == aElement->OwnerDoc()->GetRootElement()) { - return true; - } - return false; } diff --git a/dom/base/ChildIterator.h b/dom/base/ChildIterator.h index ffff8dce5..63425149a 100644 --- a/dom/base/ChildIterator.h +++ b/dom/base/ChildIterator.h @@ -256,10 +256,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 +268,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/Element.h b/dom/base/Element.h index 0104d795c..1b29f0346 100644 --- a/dom/base/Element.h +++ b/dom/base/Element.h @@ -390,6 +390,8 @@ public: 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. diff --git a/dom/base/ElementInlines.h b/dom/base/ElementInlines.h index c68bd012e..df2cc2e24 100644 --- a/dom/base/ElementInlines.h +++ b/dom/base/ElementInlines.h @@ -25,6 +25,17 @@ Element::UnregisterActivityObserver() OwnerDoc()->UnregisterActivityObserver(this); } +inline Element* +Element::GetFlattenedTreeParentElementForStyle() const +{ + nsINode* parentNode = GetFlattenedTreeParentNodeForStyle(); + if MOZ_LIKELY(parentNode && parentNode->IsElement()) { + return parentNode->AsElement(); + } + + return nullptr; +} + } // namespace dom } // namespace mozilla diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp index ca00a49a5..d7e7a78f4 100644 --- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -152,7 +152,7 @@ nsIContent::FindFirstNonChromeOnlyAccessContent() const } nsINode* -nsIContent::GetFlattenedTreeParentNodeInternal() const +nsIContent::GetFlattenedTreeParentNodeInternal(FlattenedParentType aType) const { nsINode* parentNode = GetParentNode(); if (!parentNode || !parentNode->IsContent()) { @@ -161,6 +161,45 @@ 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 diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index 4f7b71b19..6a819818c 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -103,6 +103,7 @@ #include "nsHtml5Module.h" #include "nsHtml5StringParser.h" #include "nsIAddonPolicyService.h" +#include "nsIAnonymousContentCreator.h" #include "nsIAsyncVerifyRedirectCallback.h" #include "nsICategoryManager.h" #include "nsIChannelEventSink.h" @@ -9837,6 +9838,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) { diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index 64f7485cb..3f1a25504 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -2743,6 +2743,16 @@ public: 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 */ diff --git a/dom/base/nsIContent.h b/dom/base/nsIContent.h index e179d6ebc..52f2ba5b2 100644 --- a/dom/base/nsIContent.h +++ b/dom/base/nsIContent.h @@ -144,7 +144,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, }; /** @@ -723,10 +730,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 diff --git a/dom/base/nsIContentInlines.h b/dom/base/nsIContentInlines.h index 368a0422b..6a82f7f65 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,10 @@ nsIContent::GetFlattenedTreeParent() const return (parent && parent->IsContent()) ? parent->AsContent() : nullptr; } +inline nsINode* +nsINode::GetFlattenedTreeParentNodeForStyle() const +{ + return ::GetFlattenedTreeParentNode<nsIContent::eForStyle>(this); +} #endif // nsIContentInlines_h diff --git a/dom/base/nsINode.h b/dom/base/nsINode.h index d82f5f899..a0d972f80 100644 --- a/dom/base/nsINode.h +++ b/dom/base/nsINode.h @@ -921,6 +921,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 */ diff --git a/layout/generic/nsIAnonymousContentCreator.h b/layout/generic/nsIAnonymousContentCreator.h index e7d4399b6..844b3a063 100644 --- a/layout/generic/nsIAnonymousContentCreator.h +++ b/layout/generic/nsIAnonymousContentCreator.h @@ -66,7 +66,9 @@ 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; diff --git a/layout/style/DocumentStyleRootIterator.cpp b/layout/style/DocumentStyleRootIterator.cpp new file mode 100644 index 000000000..af95c5c4f --- /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 000000000..7dcdc7fa1 --- /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/moz.build b/layout/style/moz.build index 3dc2a19af..40b9fd659 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', |