Bug 1321284 - Part 2: Add nsINode::GetFlattenedTreeParentNodeForStyle. r=bholley draft
authorCameron McCormack <cam@mcc.id.au>
Thu, 01 Dec 2016 14:55:09 +0800
changeset 447233 7e0d8f40ce513283f7262dca332fddc1e92ff810
parent 447232 7c5a533ada4eaa9839ca0497426f2c9c2a2d6c0b
child 447234 2315408fb39fc52f272731ce0e936ec6ba4ea195
push id38025
push userbmo:cam@mcc.id.au
push dateSat, 03 Dec 2016 06:57:28 +0000
reviewersbholley
bugs1321284
milestone53.0a1
Bug 1321284 - Part 2: Add nsINode::GetFlattenedTreeParentNodeForStyle. r=bholley MozReview-Commit-ID: AmDyeE21N8g
dom/base/Element.h
dom/base/ElementInlines.h
dom/base/FragmentOrElement.cpp
dom/base/nsIContent.h
dom/base/nsIContentInlines.h
dom/base/nsINode.h
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -396,16 +396,17 @@ public:
   inline bool HasDirAuto() const {
     return (!HasFixedDir() &&
             (HasValidDir() || IsHTMLElement(nsGkAtoms::bdi)));
   }
 
   Directionality GetComputedDirectionality() const;
 
   inline Element* GetFlattenedTreeParentElement() const;
+  inline Element* GetFlattenedTreeParentElementForStyle() const;
 
   bool HasDirtyDescendantsForServo() const
   {
     MOZ_ASSERT(IsStyledByServo());
     return HasFlag(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
   }
 
   void SetHasDirtyDescendantsForServo() {
--- a/dom/base/ElementInlines.h
+++ b/dom/base/ElementInlines.h
@@ -32,16 +32,27 @@ Element::GetFlattenedTreeParentElement()
   nsINode* parentNode = GetFlattenedTreeParentNode();
   if MOZ_LIKELY(parentNode && parentNode->IsElement()) {
     return parentNode->AsElement();
   }
 
   return nullptr;
 }
 
+inline Element*
+Element::GetFlattenedTreeParentElementForStyle() const
+{
+  nsINode* parentNode = GetFlattenedTreeParentNodeForStyle();
+  if MOZ_LIKELY(parentNode && parentNode->IsElement()) {
+    return parentNode->AsElement();
+  }
+
+  return nullptr;
+}
+
 inline void
 Element::NoteDirtyDescendantsForServo()
 {
   Element* curr = this;
   while (curr && !curr->HasDirtyDescendantsForServo()) {
     curr->SetHasDirtyDescendantsForServo();
     curr = curr->GetFlattenedTreeParentElement();
   }
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -147,25 +147,64 @@ nsIContent::FindFirstNonChromeOnlyAccess
       // non-const.  (Then again, so does GetChildAt(0)->GetParent().)
       return const_cast<nsIContent*>(content);
     }
   }
   return nullptr;
 }
 
 nsINode*
-nsIContent::GetFlattenedTreeParentNodeInternal() const
+nsIContent::GetFlattenedTreeParentNodeInternal(FlattenedParentType aType) const
 {
   nsINode* parentNode = GetParentNode();
   if (!parentNode || !parentNode->IsContent()) {
     MOZ_ASSERT(!parentNode || parentNode == OwnerDoc());
     return parentNode;
   }
   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.
--- a/dom/base/nsIContent.h
+++ b/dom/base/nsIContent.h
@@ -726,20 +726,19 @@ public:
   virtual void SetXBLInsertionParent(nsIContent* aContent) = 0;
 
   /**
    * Same as GetFlattenedTreeParentNode, but returns null if the parent is
    * non-nsIContent.
    */
   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;
 
   /**
    * Gets the custom element data used by web components custom element.
    * Custom element data is created at the first attempt to enqueue a callback.
    *
    * @return The custom element data or null if none.
    */
   virtual mozilla::dom::CustomElementData *GetCustomElementData() const = 0;
--- a/dom/base/nsIContentInlines.h
+++ b/dom/base/nsIContentInlines.h
@@ -28,44 +28,63 @@ inline mozilla::dom::ShadowRoot* nsICont
 {
   if (!IsElement()) {
     return nullptr;
   }
 
   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
   //       parent is the host element (as opposed to the actual parent which
   //       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;
+}
 
-  return parent;
+inline nsINode*
+nsINode::GetFlattenedTreeParentNode() const
+{
+  return ::GetFlattenedTreeParentNode<nsIContent::eNotForStyle>(this);
 }
 
 inline nsIContent*
 nsIContent::GetFlattenedTreeParent() const
 {
   nsINode* parent = GetFlattenedTreeParentNode();
   return (parent && parent->IsContent()) ? parent->AsContent() : nullptr;
 }
 
+inline nsINode*
+nsINode::GetFlattenedTreeParentNodeForStyle() const
+{
+  return ::GetFlattenedTreeParentNode<nsIContent::eForStyle>(this);
+}
 
 #endif // nsIContentInlines_h
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -914,16 +914,24 @@ public:
    * into an insertion point, or if the node is a direct child of a
    * shadow root.
    *
    * @return the flattened tree parent
    */
   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
    */
   mozilla::dom::Element* GetParentElement() const
   {
     return mParent && mParent->IsElement() ? mParent->AsElement() : nullptr;
   }