Bug 1394935: Add a special case for marking something as dirty from invalidation code. r?bholley draft
authorEmilio Cobos Álvarez <emilio@crisal.io>
Thu, 31 Aug 2017 09:42:26 +0200
changeset 656848 7ae5c4d81447c1a4fbdda758b309d5fdcded9199
parent 656818 3c220b14d8529b38d8ea4749e4fcd086bee3bae8
child 656877 605c5bc25e9c03579ba8941f2eb91820d368c7db
push id77339
push userbmo:emilio@crisal.io
push dateThu, 31 Aug 2017 19:18:11 +0000
reviewersbholley
bugs1394935
milestone57.0a1
Bug 1394935: Add a special case for marking something as dirty from invalidation code. r?bholley Not sure about the name, or whether making it a static or not (a static seemed slightly clearer, but nbd, feel free to bikeshed about it). MozReview-Commit-ID: FHGmcoprN2Q
dom/base/Element.cpp
dom/base/Element.h
layout/style/ServoBindings.cpp
layout/style/ServoBindings.h
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -4240,24 +4240,24 @@ Element::AddSizeOfExcludingThis(nsWindow
         }
       }
     }
   }
 }
 
 #ifdef DEBUG
 static bool
-BitIsPropagated(const Element* aElement, uint32_t aBit, nsINode* aRestyleRoot)
+BitsArePropagated(const Element* aElement, uint32_t aBits, nsINode* aRestyleRoot)
 {
   const Element* curr = aElement;
   while (curr) {
     if (curr == aRestyleRoot) {
       return true;
     }
-    if (!curr->HasFlag(aBit)) {
+    if (!curr->HasAllFlags(aBits)) {
       return false;
     }
     nsINode* parentNode = curr->GetParentNode();
     curr = curr->GetFlattenedTreeParentElementForStyle();
     MOZ_ASSERT_IF(!curr,
                   parentNode == aElement->OwnerDoc() ||
                   parentNode == parentNode->OwnerDoc()->GetRootElement());
   }
@@ -4322,34 +4322,38 @@ PropagateBits(Element* aElement, uint32_
 // * If we reach the document root, we then propagate the bits associated with
 //   the restyle root up the tree until we cross the path of the new root. Once
 //   we find this common ancestor, we record it as the restyle root, and then
 //   clear the bits between the new restyle root and the document root.
 // * If we have dirty content beneath multiple "document style traversal roots"
 //   (which are the main DOM + each piece of document-level native-anoymous
 //   content), we set the restyle root to the nsINode of the document itself.
 //   This is the bail-out case where we traverse everything.
+//
+// Note that, since we track a root, we try to optimize the case where an
+// element under the current root is dirtied, that's why we don't trivially use
+// `nsContentUtils::GetCommonFlattenedTreeAncestorForStyle`.
 static void
-NoteDirtyElement(Element* aElement, uint32_t aBit)
+NoteDirtyElement(Element* aElement, uint32_t aBits)
 {
   MOZ_ASSERT(aElement->IsInComposedDoc());
   MOZ_ASSERT(aElement->IsStyledByServo());
 
   Element* parent = aElement->GetFlattenedTreeParentElementForStyle();
   if (MOZ_LIKELY(parent)) {
     // If our parent is unstyled, we can inductively assume that it will be
     // traversed when the time is right, and that the traversal will reach us
     // when it happens. Nothing left to do.
     if (!parent->HasServoData()) {
       return;
     }
 
     // Similarly, if our parent already has the bit we're propagating, we can
     // assume everything is already set up.
-    if (parent->HasFlag(aBit)) {
+    if (parent->HasAllFlags(aBits)) {
       MOZ_ASSERT(aElement->GetComposedDoc()->GetServoRestyleRoot());
       return;
     }
 
     // If the parent is styled but is display:none, we're done.
     //
     // We check for a frame to reduce the cases where we need the FFI call.
     if (!parent->GetPrimaryFrame() && Servo_Element_IsDisplayNone(parent)) {
@@ -4367,59 +4371,84 @@ NoteDirtyElement(Element* aElement, uint
 
   // The bit checks below rely on this to arrive to useful conclusions about the
   // shape of the tree.
   AssertNoBitsPropagatedFrom(existingRoot);
 
   // If there's no existing restyle root, or if the root is already aElement,
   // just note the root+bits and return.
   if (!existingRoot || existingRoot == aElement) {
-    doc->SetServoRestyleRoot(aElement, existingBits | aBit);
+    doc->SetServoRestyleRoot(aElement, existingBits | aBits);
     return;
   }
 
   // There is an existing restyle root - walk up the tree from our element,
   // propagating bits as we go.
-  const bool reachedDocRoot = !parent || !PropagateBits(parent, aBit, existingRoot);
+  const bool reachedDocRoot = !parent || !PropagateBits(parent, aBits, existingRoot);
 
   if (!reachedDocRoot || existingRoot == doc) {
       // We're a descendant of the existing root. All that's left to do is to
       // make sure the bit we propagated is also registered on the root.
-      doc->SetServoRestyleRoot(existingRoot, existingBits | aBit);
+      doc->SetServoRestyleRoot(existingRoot, existingBits | aBits);
   } else {
     // We reached the root without crossing the pre-existing restyle root. We
     // now need to find the nearest common ancestor, so climb up from the
     // existing root, extending bits along the way.
     Element* rootParent = existingRoot->GetFlattenedTreeParentElementForStyle();
     if (Element* commonAncestor = PropagateBits(rootParent, existingBits, aElement)) {
       MOZ_ASSERT(commonAncestor == aElement ||
                  commonAncestor == nsContentUtils::GetCommonFlattenedTreeAncestorForStyle(aElement, rootParent));
 
       // We found a common ancestor. Make that the new style root, and clear the
       // bits between the new style root and the document root.
-      doc->SetServoRestyleRoot(commonAncestor, existingBits | aBit);
+      doc->SetServoRestyleRoot(commonAncestor, existingBits | aBits);
       Element* curr = commonAncestor;
       while ((curr = curr->GetFlattenedTreeParentElementForStyle())) {
-        MOZ_ASSERT(curr->HasFlag(aBit));
-        curr->UnsetFlags(aBit);
+        MOZ_ASSERT(curr->HasAllFlags(aBits));
+        curr->UnsetFlags(aBits);
       }
     } else {
       // We didn't find a common ancestor element. That means we're descended
       // from two different document style roots, so the common ancestor is the
       // document.
-      doc->SetServoRestyleRoot(doc, existingBits | aBit);
+      doc->SetServoRestyleRoot(doc, existingBits | aBits);
     }
   }
 
   MOZ_ASSERT(aElement == doc->GetServoRestyleRoot() ||
              nsContentUtils::ContentIsFlattenedTreeDescendantOfForStyle(
                aElement, doc->GetServoRestyleRoot()));
   MOZ_ASSERT(aElement == doc->GetServoRestyleRoot() ||
-             BitIsPropagated(parent, aBit, doc->GetServoRestyleRoot()));
-  MOZ_ASSERT(doc->GetServoRestyleRootDirtyBits() & aBit);
+             BitsArePropagated(parent, aBits, doc->GetServoRestyleRoot()));
+  MOZ_ASSERT(doc->GetServoRestyleRootDirtyBits() & aBits);
+}
+
+void
+Element::NoteDirtySubtreeForServo()
+{
+  MOZ_ASSERT(IsInComposedDoc());
+  MOZ_ASSERT(HasServoData());
+
+  nsIDocument* doc = GetComposedDoc();
+  nsINode* existingRoot = doc->GetServoRestyleRoot();
+  uint32_t existingBits = existingRoot ? doc->GetServoRestyleRootDirtyBits() : 0;
+
+  if (existingRoot &&
+      existingRoot->IsElement() &&
+      existingRoot != this &&
+      nsContentUtils::ContentIsFlattenedTreeDescendantOfForStyle(
+        existingRoot->AsElement(), this)) {
+    PropagateBits(existingRoot->AsElement()->GetFlattenedTreeParentElementForStyle(),
+                  existingBits,
+                  this);
+
+    doc->ClearServoRestyleRoot();
+  }
+
+  NoteDirtyElement(this, existingBits | ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
 }
 
 void
 Element::NoteDirtyForServo()
 {
   NoteDirtyElement(this, ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
 }
 
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -468,16 +468,26 @@ public:
 
   Directionality GetComputedDirectionality() const;
 
   static const uint32_t kAllServoDescendantBits =
     ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO |
     ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO |
     NODE_DESCENDANTS_NEED_FRAMES;
 
+  /**
+   * Notes that something in the given subtree of this element needs dirtying,
+   * and that all the relevant dirty bits have already been propagated up to the
+   * element.
+   *
+   * This is important because `NoteDirtyForServo` uses the dirty bits to reason
+   * about the shape of the tree, so we can't just call into there.
+   */
+  void NoteDirtySubtreeForServo();
+
   void NoteDirtyForServo();
   void NoteAnimationOnlyDirtyForServo();
   void NoteDescendantsNeedFramesForServo();
 
   bool HasDirtyDescendantsForServo() const
   {
     MOZ_ASSERT(IsStyledByServo());
     return HasFlag(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -373,16 +373,23 @@ Gecko_UnsetNodeFlags(RawGeckoNodeBorrowe
 void
 Gecko_NoteDirtyElement(RawGeckoElementBorrowed aElement)
 {
   MOZ_ASSERT(NS_IsMainThread());
   const_cast<Element*>(aElement)->NoteDirtyForServo();
 }
 
 void
+Gecko_NoteDirtySubtreeForInvalidation(RawGeckoElementBorrowed aElement)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  const_cast<Element*>(aElement)->NoteDirtySubtreeForServo();
+}
+
+void
 Gecko_NoteAnimationOnlyDirtyElement(RawGeckoElementBorrowed aElement)
 {
   MOZ_ASSERT(NS_IsMainThread());
   const_cast<Element*>(aElement)->NoteAnimationOnlyDirtyForServo();
 }
 
 CSSPseudoElementType
 Gecko_GetImplementedPseudo(RawGeckoElementBorrowed aElement)
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -376,16 +376,17 @@ void Gecko_SetContentDataImageValue(nsSt
                                     mozilla::css::ImageValue* aImageValue);
 nsStyleContentData::CounterFunction* Gecko_SetCounterFunction(
     nsStyleContentData* content_data, nsStyleContentType type);
 
 // Dirtiness tracking.
 void Gecko_SetNodeFlags(RawGeckoNodeBorrowed node, uint32_t flags);
 void Gecko_UnsetNodeFlags(RawGeckoNodeBorrowed node, uint32_t flags);
 void Gecko_NoteDirtyElement(RawGeckoElementBorrowed element);
+void Gecko_NoteDirtySubtreeForInvalidation(RawGeckoElementBorrowed element);
 void Gecko_NoteAnimationOnlyDirtyElement(RawGeckoElementBorrowed element);
 
 // Incremental restyle.
 mozilla::CSSPseudoElementType Gecko_GetImplementedPseudo(RawGeckoElementBorrowed element);
 // We'd like to return `nsChangeHint` here, but bindgen bitfield enums don't
 // work as return values with the Linux 32-bit ABI at the moment because
 // they wrap the value in a struct.
 uint32_t Gecko_CalcStyleDifference(ServoStyleContextBorrowed old_style,