summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt A. Tobin <email@mattatobin.com>2020-04-14 21:50:13 -0400
committerMatt A. Tobin <email@mattatobin.com>2020-04-14 21:50:13 -0400
commitbebec8fcb84dba6b684dfe1cc6c8a1e7741df374 (patch)
tree682329072ca4d617d06295c6576ba1c45c3db78e
parent5352b69a9286223272c0ed072900b4c78ba2ed7c (diff)
downloaduxp-bebec8fcb84dba6b684dfe1cc6c8a1e7741df374.tar.gz
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.cpp16
-rw-r--r--dom/base/ChildIterator.h13
-rw-r--r--dom/base/Element.h2
-rw-r--r--dom/base/ElementInlines.h11
-rw-r--r--dom/base/FragmentOrElement.cpp41
-rw-r--r--dom/base/nsContentUtils.cpp19
-rw-r--r--dom/base/nsContentUtils.h10
-rw-r--r--dom/base/nsIContent.h16
-rw-r--r--dom/base/nsIContentInlines.h41
-rw-r--r--dom/base/nsINode.h8
-rw-r--r--layout/generic/nsIAnonymousContentCreator.h4
-rw-r--r--layout/style/DocumentStyleRootIterator.cpp41
-rw-r--r--layout/style/DocumentStyleRootIterator.h37
-rw-r--r--layout/style/moz.build2
14 files changed, 226 insertions, 35 deletions
diff --git a/dom/base/ChildIterator.cpp b/dom/base/ChildIterator.cpp
index d8c454ae8f..fb07e9a210 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 ffff8dce5f..63425149af 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 0104d795c2..1b29f03462 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 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 ca00a49a5e..d7e7a78f49 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 4f7b71b193..6a819818c1 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 64f7485cbb..3f1a255043 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 e179d6ebce..52f2ba5b2f 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 368a0422b1..6a82f7f652 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 d82f5f8990..a0d972f80d 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 e7d4399b6b..844b3a0637 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 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/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',