Bug 1394935: Add a special case for marking something as dirty from invalidation code. r=bholley
authorEmilio Cobos Álvarez <emilio@crisal.io>
Thu, 31 Aug 2017 09:42:26 +0200
changeset 430034 8daf204f77fbf183e3660fd89e170f076967d245
parent 430033 c3f9a3e2ce9649289fd0e2e527e09206426f2730
child 430035 58f4ecda0d4ad434241d7dbe30bcf60ce261d8f7
push id1567
push userjlorenzo@mozilla.com
push dateThu, 02 Nov 2017 12:36:05 +0000
treeherdermozilla-release@e512c14a0406 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley
bugs1394935
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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,