Bug 1374752: Pack together the StyleSet, ChangeList and handled change hints, and use the latter with anonymous boxes while we're at it. r?heycam draft
authorEmilio Cobos Álvarez <emilio@crisal.io>
Tue, 20 Jun 2017 23:21:27 +0200
changeset 598899 079c8e9a99a2681a01c918c764ae2617223eb228
parent 598898 7d767b2755a2f538d1a3046ffcf5bff2be541a88
child 634605 ace06ca0e4d5e559c4bd712fe352af830cac12d1
push id65346
push userbmo:emilio+bugs@crisal.io
push dateThu, 22 Jun 2017 10:21:34 +0000
reviewersheycam
bugs1374752
milestone56.0a1
Bug 1374752: Pack together the StyleSet, ChangeList and handled change hints, and use the latter with anonymous boxes while we're at it. r?heycam MozReview-Commit-ID: DiWcUwD9po5
layout/base/ServoRestyleManager.cpp
layout/base/ServoRestyleManager.h
layout/generic/ViewportFrame.cpp
layout/generic/ViewportFrame.h
layout/generic/nsFrame.cpp
layout/generic/nsIFrame.h
layout/generic/nsInlineFrame.cpp
layout/generic/nsInlineFrame.h
layout/tables/nsTableFrame.cpp
layout/tables/nsTableFrame.h
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -176,50 +176,43 @@ ServoRestyleManager::ClearRestyleStateFr
  * need to track during the post-traversal.
  *
  * This is currently used to properly compute change hints when the parent
  * element of this node is a display: contents node, and also to avoid computing
  * the style for text children more than once per element.
  */
 struct ServoRestyleManager::TextPostTraversalState
 {
-  nsStyleContext& mParentContext;
-  ServoStyleSet& mStyleSet;
-  RefPtr<nsStyleContext> mStyle;
-  bool mShouldPostHints;
-  bool mShouldComputeHints;
-  nsChangeHint mComputedHint;
-  nsChangeHint mHintsHandled;
-
+public:
   TextPostTraversalState(nsStyleContext& aParentContext,
-                         ServoStyleSet& aStyleSet,
                          bool aDisplayContentsParentStyleChanged,
-                         nsChangeHint aHintsHandled)
+                         ServoRestyleState& aParentRestyleState)
     : mParentContext(aParentContext)
-    , mStyleSet(aStyleSet)
+    , mParentRestyleState(aParentRestyleState)
     , mStyle(nullptr)
     , mShouldPostHints(aDisplayContentsParentStyleChanged)
     , mShouldComputeHints(aDisplayContentsParentStyleChanged)
     , mComputedHint(nsChangeHint_Empty)
-    , mHintsHandled(aHintsHandled)
   {}
 
+  nsStyleChangeList& ChangeList() { return mParentRestyleState.ChangeList(); }
+
   nsStyleContext& ComputeStyle(nsIContent* aTextNode)
   {
     if (!mStyle) {
-      mStyle = mStyleSet.ResolveStyleForText(aTextNode, &mParentContext);
+      mStyle = mParentRestyleState.StyleSet().ResolveStyleForText(
+        aTextNode, &mParentContext);
     }
     MOZ_ASSERT(mStyle);
     return *mStyle;
   }
 
   void ComputeHintIfNeeded(nsIContent* aContent,
                            nsIFrame* aTextFrame,
-                           nsStyleContext& aNewContext,
-                           nsStyleChangeList& aChangeList)
+                           nsStyleContext& aNewContext)
   {
     MOZ_ASSERT(aTextFrame);
     MOZ_ASSERT(aNewContext.GetPseudo() == nsCSSAnonBoxes::mozText);
 
     if (MOZ_LIKELY(!mShouldPostHints)) {
       return;
     }
 
@@ -233,46 +226,53 @@ struct ServoRestyleManager::TextPostTrav
     // we'll cross that bridge when we support those in stylo.
     if (mShouldComputeHints) {
       mShouldComputeHints = false;
       uint32_t equalStructs, samePointerStructs;
       mComputedHint =
         oldContext->CalcStyleDifference(&aNewContext,
                                         &equalStructs,
                                         &samePointerStructs);
-      mComputedHint = NS_RemoveSubsumedHints(mComputedHint, mHintsHandled);
+      mComputedHint = NS_RemoveSubsumedHints(
+        mComputedHint, mParentRestyleState.ChangesHandled());
     }
 
     if (mComputedHint) {
-      aChangeList.AppendChange(aTextFrame, aContent, mComputedHint);
+      mParentRestyleState.ChangeList().AppendChange(
+        aTextFrame, aContent, mComputedHint);
     }
   }
+
+private:
+  nsStyleContext& mParentContext;
+  ServoRestyleState& mParentRestyleState;
+  RefPtr<nsStyleContext> mStyle;
+  bool mShouldPostHints;
+  bool mShouldComputeHints;
+  nsChangeHint mComputedHint;
 };
 
 static void
 UpdateBlockFramePseudoElements(nsBlockFrame* aFrame,
-                               ServoStyleSet& aStyleSet,
-                               nsStyleChangeList& aChangeList)
+                               ServoRestyleState& aRestyleState)
 {
   if (nsBulletFrame* bullet = aFrame->GetBullet()) {
     RefPtr<nsStyleContext> newContext =
-      aStyleSet.ResolvePseudoElementStyle(
-          aFrame->GetContent()->AsElement(),
-          bullet->StyleContext()->GetPseudoType(),
-          aFrame->StyleContext(),
-          /* aPseudoElement = */ nullptr);
+      aRestyleState.StyleSet().ResolvePseudoElementStyle(
+        aFrame->GetContent()->AsElement(),
+        bullet->StyleContext()->GetPseudoType(),
+        aFrame->StyleContext(),
+        /* aPseudoElement = */ nullptr);
 
-    aFrame->UpdateStyleOfOwnedChildFrame(bullet, newContext, aChangeList);
+    aFrame->UpdateStyleOfOwnedChildFrame(bullet, newContext, aRestyleState);
   }
 }
 
 static void
-UpdateBackdropIfNeeded(nsIFrame* aFrame,
-                       ServoStyleSet& aStyleSet,
-                       nsStyleChangeList& aChangeList)
+UpdateBackdropIfNeeded(nsIFrame* aFrame, ServoRestyleState& aRestyleState)
 {
   const nsStyleDisplay* display = aFrame->StyleContext()->StyleDisplay();
   if (display->mTopLayer != NS_STYLE_TOP_LAYER_TOP) {
     return;
   }
 
   // Elements in the top layer are guaranteed to have absolute or fixed
   // position per https://fullscreen.spec.whatwg.org/#new-stacking-layer.
@@ -287,71 +287,66 @@ UpdateBackdropIfNeeded(nsIFrame* aFrame,
   MOZ_ASSERT(backdropPlaceholder->IsPlaceholderFrame());
   nsIFrame* backdropFrame =
     nsPlaceholderFrame::GetRealFrameForPlaceholder(backdropPlaceholder);
   MOZ_ASSERT(backdropFrame->IsBackdropFrame());
   MOZ_ASSERT(backdropFrame->StyleContext()->GetPseudoType() ==
              CSSPseudoElementType::backdrop);
 
   RefPtr<nsStyleContext> newContext =
-    aStyleSet.ResolvePseudoElementStyle(
-        aFrame->GetContent()->AsElement(),
-        CSSPseudoElementType::backdrop,
-        aFrame->StyleContext(),
-        /* aPseudoElement = */ nullptr);
+    aRestyleState.StyleSet().ResolvePseudoElementStyle(
+      aFrame->GetContent()->AsElement(),
+      CSSPseudoElementType::backdrop,
+      aFrame->StyleContext(),
+      /* aPseudoElement = */ nullptr);
 
-  aFrame->UpdateStyleOfOwnedChildFrame(backdropFrame,
-                                       newContext,
-                                       aChangeList);
+  aFrame->UpdateStyleOfOwnedChildFrame(
+    backdropFrame, newContext, aRestyleState);
 }
 
 static void
 UpdateFramePseudoElementStyles(nsIFrame* aFrame,
-                               ServoStyleSet& aStyleSet,
-                               nsStyleChangeList& aChangeList)
+                               ServoRestyleState& aRestyleState)
 {
   if (aFrame->IsFrameOfType(nsIFrame::eBlockFrame)) {
     UpdateBlockFramePseudoElements(static_cast<nsBlockFrame*>(aFrame),
-                                   aStyleSet,
-                                   aChangeList);
+                                   aRestyleState);
   }
 
-  UpdateBackdropIfNeeded(aFrame, aStyleSet, aChangeList);
+  UpdateBackdropIfNeeded(aFrame, aRestyleState);
 }
 
 bool
 ServoRestyleManager::ProcessPostTraversal(Element* aElement,
                                           nsStyleContext* aParentContext,
-                                          ServoStyleSet* aStyleSet,
-                                          nsStyleChangeList& aChangeList,
-                                          nsChangeHint aChangesHandled)
+                                          ServoRestyleState& aRestyleState)
 {
   nsIFrame* styleFrame = nsLayoutUtils::GetStyleFrame(aElement);
 
   // Grab the change hint from Servo.
   nsChangeHint changeHint = Servo_TakeChangeHint(aElement);
-  changeHint = NS_RemoveSubsumedHints(changeHint, aChangesHandled);
-  aChangesHandled |= changeHint;
+  changeHint =
+    NS_RemoveSubsumedHints(changeHint, aRestyleState.ChangesHandled());
 
   // Handle lazy frame construction by posting a reconstruct for any lazily-
   // constructed roots.
   if (aElement->HasFlag(NODE_NEEDS_FRAME)) {
     changeHint |= nsChangeHint_ReconstructFrame;
     // The only time the primary frame is non-null is when image maps do hacky
     // SetPrimaryFrame calls.
     MOZ_ASSERT(!styleFrame || styleFrame->IsImageFrame());
     styleFrame = nullptr;
   }
 
   // Although we shouldn't generate non-ReconstructFrame hints for elements with
   // no frames, we can still get them here if they were explicitly posted by
   // PostRestyleEvent, such as a RepaintFrame hint when a :link changes to be
   // :visited.  Skip processing these hints if there is no frame.
   if ((styleFrame || (changeHint & nsChangeHint_ReconstructFrame)) && changeHint) {
-    aChangeList.AppendChange(styleFrame, aElement, changeHint);
+    aRestyleState.ChangeList().AppendChange(styleFrame, aElement, changeHint);
   }
 
   // If our change hint is reconstruct, we delegate to the frame constructor,
   // which consumes the new style and expects the old style to be on the frame.
   //
   // XXXbholley: We should teach the frame constructor how to clear the dirty
   // descendants bit to avoid the traversal here.
   if (changeHint & nsChangeHint_ReconstructFrame) {
@@ -376,45 +371,43 @@ ServoRestyleManager::ProcessPostTraversa
     displayContentsNode =
       PresContext()->FrameConstructor()->GetDisplayContentsNodeFor(aElement);
     if (displayContentsNode) {
       oldStyleContext = displayContentsNode->mStyle;
     }
   }
 
   RefPtr<ServoComputedValues> computedValues =
-    aStyleSet->ResolveServoStyle(aElement);
+    aRestyleState.StyleSet().ResolveServoStyle(aElement);
 
   // Note that we rely in the fact that we don't cascade pseudo-element styles
   // separately right now (that is, if a pseudo style changes, the normal style
   // changes too).
   //
   // Otherwise we should probably encode that information somehow to avoid
   // expensive checks in the common case.
   //
   // Also, we're going to need to check for pseudos of display: contents
   // elements, though that is buggy right now even in non-stylo mode, see
   // bug 1251799.
   const bool recreateContext = oldStyleContext &&
     oldStyleContext->ComputedValues() != computedValues;
 
+  ServoRestyleState childrenRestyleState(aRestyleState, changeHint);
+
   RefPtr<nsStyleContext> newContext = nullptr;
   if (recreateContext) {
     MOZ_ASSERT(styleFrame || displayContentsNode);
 
     auto pseudo = aElement->GetPseudoElementType();
     nsIAtom* pseudoTag = pseudo == CSSPseudoElementType::NotPseudo
       ? nullptr : nsCSSPseudoElements::GetPseudoAtom(pseudo);
 
-    newContext =
-      aStyleSet->GetContext(computedValues.forget(),
-                            aParentContext,
-                            pseudoTag,
-                            pseudo,
-                            aElement);
+    newContext = aRestyleState.StyleSet().GetContext(
+      computedValues.forget(), aParentContext, pseudoTag, pseudo, aElement);
 
     newContext->EnsureSameStructsCached(oldStyleContext);
 
     // XXX This could not always work as expected: there are kinds of content
     // with the first split and the last sharing style, but others not. We
     // should handle those properly.
     // XXXbz I think the UpdateStyleOfOwnedAnonBoxes call below handles _that_
     // right, but not other cases where we happen to have different styles on
@@ -425,99 +418,95 @@ ServoRestyleManager::ProcessPostTraversa
     }
 
     if (MOZ_UNLIKELY(displayContentsNode)) {
       MOZ_ASSERT(!styleFrame);
       displayContentsNode->mStyle = newContext;
     }
 
     if (styleFrame) {
-      styleFrame->UpdateStyleOfOwnedAnonBoxes(*aStyleSet, aChangeList, changeHint);
-      UpdateFramePseudoElementStyles(styleFrame, *aStyleSet, aChangeList);
+      styleFrame->UpdateStyleOfOwnedAnonBoxes(childrenRestyleState);
+      UpdateFramePseudoElementStyles(styleFrame, childrenRestyleState);
     }
 
     if (!aElement->GetParent()) {
       // This is the root.  Update styles on the viewport as needed.
       ViewportFrame* viewport =
         do_QueryFrame(mPresContext->PresShell()->GetRootFrame());
       if (viewport) {
-        viewport->UpdateStyle(*aStyleSet, aChangeList);
+        viewport->UpdateStyle(childrenRestyleState);
       }
     }
 
     // Some changes to animations don't affect the computed style and yet still
     // require the layer to be updated. For example, pausing an animation via
     // the Web Animations API won't affect an element's style but still
     // requires to update the animation on the layer.
     //
     // We can sometimes reach this when the animated style is being removed.
     // Since AddLayerChangesForAnimation checks if |styleFrame| has a transform
     // style or not, we need to call it *after* setting |newContext| to
     // |styleFrame| to ensure the animated transform has been removed first.
-    AddLayerChangesForAnimation(styleFrame, aElement, aChangeList);
+    AddLayerChangesForAnimation(
+      styleFrame, aElement, aRestyleState.ChangeList());
   }
 
   const bool descendantsNeedFrames =
     aElement->HasFlag(NODE_DESCENDANTS_NEED_FRAMES);
   const bool traverseElementChildren =
     aElement->HasDirtyDescendantsForServo() ||
     aElement->HasAnimationOnlyDirtyDescendantsForServo() ||
     descendantsNeedFrames;
   const bool traverseTextChildren = recreateContext || descendantsNeedFrames;
   bool recreatedAnyContext = recreateContext;
   if (traverseElementChildren || traverseTextChildren) {
     nsStyleContext* upToDateContext =
       recreateContext ? newContext : oldStyleContext;
 
     StyleChildrenIterator it(aElement);
-    TextPostTraversalState textState(
-        *upToDateContext,
-        *aStyleSet,
-        displayContentsNode && recreateContext,
-        aChangesHandled);
+    TextPostTraversalState textState(*upToDateContext,
+                                     displayContentsNode && recreateContext,
+                                     childrenRestyleState);
     for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
       if (traverseElementChildren && n->IsElement()) {
-        recreatedAnyContext |=
-          ProcessPostTraversal(n->AsElement(), upToDateContext,
-                               aStyleSet, aChangeList, aChangesHandled);
+        recreatedAnyContext |= ProcessPostTraversal(
+          n->AsElement(), upToDateContext, childrenRestyleState);
       } else if (traverseTextChildren && n->IsNodeOfType(nsINode::eTEXT)) {
-        recreatedAnyContext |=
-          ProcessPostTraversalForText(n, aChangeList, textState);
+        recreatedAnyContext |= ProcessPostTraversalForText(n, textState);
       }
     }
   }
 
   aElement->UnsetHasDirtyDescendantsForServo();
   aElement->UnsetHasAnimationOnlyDirtyDescendantsForServo();
   aElement->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES);
   return recreatedAnyContext;
 }
 
 bool
 ServoRestyleManager::ProcessPostTraversalForText(
     nsIContent* aTextNode,
-    nsStyleChangeList& aChangeList,
     TextPostTraversalState& aPostTraversalState)
 {
   // Handle lazy frame construction.
   if (aTextNode->HasFlag(NODE_NEEDS_FRAME)) {
-    aChangeList.AppendChange(nullptr, aTextNode, nsChangeHint_ReconstructFrame);
+    aPostTraversalState.ChangeList().AppendChange(
+      nullptr, aTextNode, nsChangeHint_ReconstructFrame);
     return true;
   }
 
   // Handle restyle.
   nsIFrame* primaryFrame = aTextNode->GetPrimaryFrame();
   if (!primaryFrame) {
     return false;
   }
 
   RefPtr<nsStyleContext> oldStyleContext = primaryFrame->StyleContext();
   nsStyleContext& newContext = aPostTraversalState.ComputeStyle(aTextNode);
-  aPostTraversalState.ComputeHintIfNeeded(
-      aTextNode, primaryFrame, newContext, aChangeList);
+  aPostTraversalState.ComputeHintIfNeeded(aTextNode, primaryFrame, newContext);
 
   for (nsIFrame* f = primaryFrame; f;
        f = GetNextContinuationWithSameStyle(f, oldStyleContext)) {
     f->SetStyleContext(&newContext);
   }
 
   return true;
 }
@@ -637,19 +626,18 @@ ServoRestyleManager::DoProcessPendingRes
     }
 
     // Recreate style contexts, and queue up change hints (which also handle
     // lazy frame construction).
     nsStyleChangeList currentChanges(StyleBackendType::Servo);
     DocumentStyleRootIterator iter(doc);
     bool anyStyleChanged = false;
     while (Element* root = iter.GetNextStyleRoot()) {
-      anyStyleChanged |=
-        ProcessPostTraversal(
-            root, nullptr, styleSet, currentChanges, nsChangeHint(0));
+      ServoRestyleState state(*styleSet, currentChanges);
+      anyStyleChanged |= ProcessPostTraversal(root, nullptr, state);
     }
 
     // Process the change hints.
     //
     // Unfortunately, the frame constructor can generate new change hints while
     // processing existing ones. We redirect those into a secondary queue and
     // iterate until there's nothing left.
     ReentrantChangeList newChanges;
--- a/layout/base/ServoRestyleManager.h
+++ b/layout/base/ServoRestyleManager.h
@@ -23,16 +23,46 @@ class nsAttrValue;
 class nsIAtom;
 class nsIContent;
 class nsIFrame;
 class nsStyleChangeList;
 
 namespace mozilla {
 
 /**
+ * A stack class used to pass some common restyle state in a slightly more
+ * comfortable way than a bunch of individual arguments.
+ */
+class ServoRestyleState
+{
+public:
+  ServoRestyleState(ServoStyleSet& aStyleSet, nsStyleChangeList& aChangeList)
+    : mStyleSet(aStyleSet)
+    , mChangeList(aChangeList)
+    , mChangesHandled(nsChangeHint(0))
+  {}
+
+  ServoRestyleState(ServoRestyleState& aParentState,
+                    nsChangeHint aHintForThisFrame)
+    : mStyleSet(aParentState.mStyleSet)
+    , mChangeList(aParentState.mChangeList)
+    , mChangesHandled(aParentState.mChangesHandled | aHintForThisFrame)
+  {}
+
+  nsChangeHint ChangesHandled() const { return mChangesHandled; }
+  nsStyleChangeList& ChangeList() { return mChangeList; }
+  ServoStyleSet& StyleSet() { return mStyleSet; }
+
+private:
+  ServoStyleSet& mStyleSet;
+  nsStyleChangeList& mChangeList;
+  const nsChangeHint mChangesHandled;
+};
+
+/**
  * Restyle manager for a Servo-backed style system.
  */
 class ServoRestyleManager : public RestyleManager
 {
   friend class ServoStyleSet;
 
 public:
   typedef ServoElementSnapshotTable SnapshotTable;
@@ -122,23 +152,20 @@ private:
    *
    * Returns whether any style did actually change. There may be cases where we
    * didn't need to change any style after all, for example, when a content
    * attribute changes that happens not to have any effect on the style of that
    * element or any descendant or sibling.
    */
   bool ProcessPostTraversal(Element* aElement,
                             nsStyleContext* aParentContext,
-                            ServoStyleSet* aStyleSet,
-                            nsStyleChangeList& aChangeList,
-                            nsChangeHint aChangesHandledForDescendants);
+                            ServoRestyleState& aRestyleState);
 
   struct TextPostTraversalState;
   bool ProcessPostTraversalForText(nsIContent* aTextNode,
-                                   nsStyleChangeList& aChangeList,
                                    TextPostTraversalState& aState);
 
   inline ServoStyleSet* StyleSet() const
   {
     MOZ_ASSERT(PresContext()->StyleSet()->IsServo(),
                "ServoRestyleManager should only be used with a Servo-flavored "
                "style backend");
     return PresContext()->StyleSet()->AsServo();
--- a/layout/generic/ViewportFrame.cpp
+++ b/layout/generic/ViewportFrame.cpp
@@ -5,16 +5,17 @@
 
 /*
  * rendering object that is the root of the frame tree, which contains
  * the document's scrollbars and contains fixed-positioned elements
  */
 
 #include "mozilla/ViewportFrame.h"
 
+#include "mozilla/ServoRestyleManager.h"
 #include "nsGkAtoms.h"
 #include "nsIScrollableFrame.h"
 #include "nsSubDocumentFrame.h"
 #include "nsCanvasFrame.h"
 #include "nsAbsoluteContainingBlock.h"
 #include "GeckoProfiler.h"
 #include "nsIMozBrowserFrame.h"
 #include "nsPlaceholderFrame.h"
@@ -411,34 +412,33 @@ ViewportFrame::ComputeCustomOverflow(nsO
   if (rootScrollFrame && !rootScrollFrame->IsIgnoringViewportClipping()) {
     return false;
   }
 
   return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas);
 }
 
 void
-ViewportFrame::UpdateStyle(ServoStyleSet& aStyleSet,
-                           nsStyleChangeList& aChangeList)
+ViewportFrame::UpdateStyle(ServoRestyleState& aRestyleState)
 {
   nsStyleContext* oldContext = StyleContext();
   nsIAtom* pseudo = oldContext->GetPseudo();
   RefPtr<nsStyleContext> newContext =
-    aStyleSet.ResolveInheritingAnonymousBoxStyle(pseudo, nullptr);
+    aRestyleState.StyleSet().ResolveInheritingAnonymousBoxStyle(pseudo, nullptr);
 
   // We're special because we have a null GetContent(), so don't call things
   // like UpdateStyleOfOwnedChildFrame that try to append changes for the
   // content to the change list.  Nor do we computed a changehint, since we have
   // no way to apply it anyway.
   newContext->EnsureSameStructsCached(oldContext);
 
   MOZ_ASSERT(!GetNextContinuation(), "Viewport has continuations?");
   SetStyleContext(newContext);
 
-  UpdateStyleOfOwnedAnonBoxes(aStyleSet, aChangeList, nsChangeHint_Empty);
+  UpdateStyleOfOwnedAnonBoxes(aRestyleState);
 }
 
 void
 ViewportFrame::AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult)
 {
   if (mFrames.NotEmpty()) {
     aResult.AppendElement(mFrames.FirstChild());
   }
--- a/layout/generic/ViewportFrame.h
+++ b/layout/generic/ViewportFrame.h
@@ -13,16 +13,18 @@
 
 #include "mozilla/Attributes.h"
 #include "nsContainerFrame.h"
 
 class nsPresContext;
 
 namespace mozilla {
 
+class ServoRestyleState;
+
 /**
   * ViewportFrame is the parent of a single child - the doc root frame or a scroll frame
   * containing the doc root frame. ViewportFrame stores this child in its primary child
   * list.
   */
 class ViewportFrame : public nsContainerFrame {
 public:
   NS_DECL_QUERYFRAME
@@ -71,17 +73,17 @@ public:
    * @return the rect to use as containing block rect
    */
   nsRect AdjustReflowInputAsContainingBlock(ReflowInput* aReflowInput) const;
 
   /**
    * Update our style (and recursively the styles of any anonymous boxes we
    * might own)
    */
-  void UpdateStyle(ServoStyleSet& aStyleSet, nsStyleChangeList& aChangeList);
+  void UpdateStyle(ServoRestyleState& aStyleSet);
 
   /**
    * Return our single anonymous box child.
    */
   void AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) override;
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -10197,19 +10197,17 @@ nsFrame::BoxMetrics() const
 {
   nsBoxLayoutMetrics* metrics = GetProperty(BoxMetricsProperty());
   NS_ASSERTION(metrics, "A box layout method was called but InitBoxMetrics was never called");
   return metrics;
 }
 
 void
 nsIFrame::UpdateStyleOfChildAnonBox(nsIFrame* aChildFrame,
-                                    ServoStyleSet& aStyleSet,
-                                    nsStyleChangeList& aChangeList,
-                                    nsChangeHint aHintForThisFrame)
+                                    ServoRestyleState& aRestyleState)
 {
   MOZ_ASSERT(aChildFrame->GetParent() == this,
              "This should only be used for children!");
   MOZ_ASSERT((!GetContent() && IsViewportFrame()) ||
              aChildFrame->GetContent() == GetContent(),
              "What content node is it a frame for?");
   MOZ_ASSERT(!aChildFrame->GetPrevContinuation(),
              "Only first continuations should end up here");
@@ -10218,53 +10216,58 @@ nsIFrame::UpdateStyleOfChildAnonBox(nsIF
   // statically...  But this API is a bit nicer.
   nsIAtom* pseudo = aChildFrame->StyleContext()->GetPseudo();
   MOZ_ASSERT(nsCSSAnonBoxes::IsAnonBox(pseudo), "Child is not an anon box?");
   MOZ_ASSERT(!nsCSSAnonBoxes::IsNonInheritingAnonBox(pseudo),
              "Why did the caller bother calling us?");
 
   // Anon boxes inherit from their parent; that's us.
   RefPtr<nsStyleContext> newContext =
-    aStyleSet.ResolveInheritingAnonymousBoxStyle(pseudo, StyleContext());
+    aRestyleState.StyleSet().ResolveInheritingAnonymousBoxStyle(pseudo,
+                                                                StyleContext());
 
   nsChangeHint childHint =
-    UpdateStyleOfOwnedChildFrame(aChildFrame, newContext, aChangeList);
+    UpdateStyleOfOwnedChildFrame(aChildFrame, newContext, aRestyleState);
 
   // Now that we've updated the style on aChildFrame, check whether it itself
   // has anon boxes to deal with.
-  aChildFrame->UpdateStyleOfOwnedAnonBoxes(aStyleSet, aChangeList, childHint);
+  ServoRestyleState childrenState(aRestyleState, childHint);
+  aChildFrame->UpdateStyleOfOwnedAnonBoxes(childrenState);
 }
 
 nsChangeHint
 nsIFrame::UpdateStyleOfOwnedChildFrame(nsIFrame* aChildFrame,
                                        nsStyleContext* aNewStyleContext,
-                                       nsStyleChangeList& aChangeList)
+                                       ServoRestyleState& aRestyleState)
 {
   // Figure out whether we have an actual change.  It's important that we do
   // this, for several reasons:
   //
   // 1) Even if all the child's changes are due to properties it inherits from
   //    us, it's possible that no one ever asked us for those style structs and
   //    hence changes to them aren't reflected in aHintForThisFrame at all.
   //
   // 2) Content can change stylesheets that change the styles of pseudos, and
   //    extensions can add/remove stylesheets that change the styles of
   //    anonymous boxes directly.
   uint32_t equalStructs, samePointerStructs; // Not used, actually.
   nsChangeHint childHint = aChildFrame->StyleContext()->CalcStyleDifference(
     aNewStyleContext,
     &equalStructs,
     &samePointerStructs);
+  childHint = NS_RemoveSubsumedHints(childHint, aRestyleState.ChangesHandled());
   if (childHint) {
     if (childHint & nsChangeHint_ReconstructFrame) {
       // If we generate a reconstruct here, remove any non-reconstruct hints we
       // may have already generated for this content.
-      aChangeList.PopChangesForContent(aChildFrame->GetContent());
-    }
-    aChangeList.AppendChange(aChildFrame, aChildFrame->GetContent(), childHint);
+      aRestyleState.ChangeList().PopChangesForContent(
+        aChildFrame->GetContent());
+    }
+    aRestyleState.ChangeList().AppendChange(
+      aChildFrame, aChildFrame->GetContent(), childHint);
   }
 
   for (nsIFrame* kid = aChildFrame; kid; kid = kid->GetNextContinuation()) {
     kid->SetStyleContext(aNewStyleContext);
   }
 
   return childHint;
 }
@@ -10556,49 +10559,44 @@ nsIFrame::UpdateWidgetProperties()
   }
   if (nsCOMPtr<nsIWidget> widget = GetWindowWidget(presContext)) {
     widget->SetWindowOpacity(StyleUIReset()->mWindowOpacity);
     widget->SetWindowTransform(ComputeWidgetTransform());
   }
 }
 
 void
-nsIFrame::DoUpdateStyleOfOwnedAnonBoxes(ServoStyleSet& aStyleSet,
-                                        nsStyleChangeList& aChangeList,
-                                        nsChangeHint aHintForThisFrame)
+nsIFrame::DoUpdateStyleOfOwnedAnonBoxes(ServoRestyleState& aRestyleState)
 {
   // As a special case, we check for {ib}-split block frames here, rather
   // than have an nsInlineFrame::AppendDirectlyOwnedAnonBoxes implementation
   // that returns them.
   //
   // (If we did handle them in AppendDirectlyOwnedAnonBoxes, we would have to
   // return *all* of the in-flow {ib}-split block frames, not just the first
   // one.  For restyling, we really just need the first in flow, and the other
   // user of the AppendOwnedAnonBoxes API, AllChildIterator, doesn't need to
   // know about them at all, since these block frames never create NAC.  So we
   // avoid any unncessary hashtable lookups for the {ib}-split frames by calling
   // UpdateStyleOfOwnedAnonBoxesForIBSplit directly here.)
   if (IsInlineFrame()) {
     if ((GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
-      static_cast<nsInlineFrame*>(this)->
-        UpdateStyleOfOwnedAnonBoxesForIBSplit(aStyleSet, aChangeList,
-                                              aHintForThisFrame);
+      static_cast<nsInlineFrame*>(this)->UpdateStyleOfOwnedAnonBoxesForIBSplit(
+        aRestyleState);
     }
     return;
   }
 
   AutoTArray<OwnedAnonBox,4> frames;
   AppendDirectlyOwnedAnonBoxes(frames);
   for (OwnedAnonBox& box : frames) {
     if (box.mUpdateStyleFn) {
-      box.mUpdateStyleFn(this, box.mAnonBoxFrame,
-                         aStyleSet, aChangeList, aHintForThisFrame);
+      box.mUpdateStyleFn(this, box.mAnonBoxFrame, aRestyleState);
     } else {
-      UpdateStyleOfChildAnonBox(box.mAnonBoxFrame,
-                                aStyleSet, aChangeList, aHintForThisFrame);
+      UpdateStyleOfChildAnonBox(box.mAnonBoxFrame, aRestyleState);
     }
   }
 }
 
 /* virtual */ void
 nsIFrame::AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult)
 {
   MOZ_ASSERT(!(GetStateBits() & NS_FRAME_OWNS_ANON_BOXES));
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -94,17 +94,17 @@ struct nsMargin;
 struct CharacterDataChangeInfo;
 
 namespace mozilla {
 
 enum class CSSPseudoElementType : uint8_t;
 class EventStates;
 struct ReflowInput;
 class ReflowOutput;
-class ServoStyleSet;
+class ServoRestyleState;
 class DisplayItemData;
 class EffectSet;
 
 namespace layers {
 class Layer;
 } // namespace layers
 
 namespace gfx {
@@ -3286,69 +3286,65 @@ public:
    * @return The style context that should be the parent of this frame's
    *         style context.  Null is permitted, and means that this frame's
    *         style context should be the root of the style context tree.
    */
   virtual nsStyleContext* GetParentStyleContext(nsIFrame** aProviderFrame) const = 0;
 
   /**
    * Called by ServoRestyleManager to update the style contexts of anonymous
-   * boxes directly associtated with this frame.  The passed-in ServoStyleSet
-   * can be used to create new style contexts as needed.
+   * boxes directly associtated with this frame.
+   *
+   * The passed-in ServoRestyleState can be used to create new style contexts
+   * as needed, as well as posting changes to the change list.
+   *
+   * It's guaranteed to already have a change in it for this frame and this
+   * frame's content.
    *
    * This function will be called after this frame's style context has already
    * been updated.  This function will only be called on frames which have the
    * NS_FRAME_OWNS_ANON_BOXES bit set.
-   *
-   * The nsStyleChangeList can be used to append additional changes.  It's
-   * guaranteed to already have a change in it for this frame and this frame's
-   * content and a change hint of aHintForThisFrame.
-   */
-  void UpdateStyleOfOwnedAnonBoxes(mozilla::ServoStyleSet& aStyleSet,
-                                   nsStyleChangeList& aChangeList,
-                                   nsChangeHint aHintForThisFrame) {
+   */
+  void UpdateStyleOfOwnedAnonBoxes(mozilla::ServoRestyleState& aRestyleState)
+  {
     if (GetStateBits() & NS_FRAME_OWNS_ANON_BOXES) {
-      DoUpdateStyleOfOwnedAnonBoxes(aStyleSet, aChangeList, aHintForThisFrame);
+      DoUpdateStyleOfOwnedAnonBoxes(aRestyleState);
     }
   }
 
 protected:
   // This does the actual work of UpdateStyleOfOwnedAnonBoxes.  It calls
   // AppendDirectlyOwnedAnonBoxes to find all of the anonymous boxes
   // owned by this frame, and then updates styles on each of them.
-  void DoUpdateStyleOfOwnedAnonBoxes(mozilla::ServoStyleSet& aStyleSet,
-                                     nsStyleChangeList& aChangeList,
-                                     nsChangeHint aHintForThisFrame);
+  void DoUpdateStyleOfOwnedAnonBoxes(mozilla::ServoRestyleState& aRestyleState);
 
   // A helper for DoUpdateStyleOfOwnedAnonBoxes for the specific case
   // of the owned anon box being a child of this frame.
   void UpdateStyleOfChildAnonBox(nsIFrame* aChildFrame,
-                                 mozilla::ServoStyleSet& aStyleSet,
-                                 nsStyleChangeList& aChangeList,
-                                 nsChangeHint aHintForThisFrame);
+                                 mozilla::ServoRestyleState& aRestyleState);
 
 public:
   // A helper both for UpdateStyleOfChildAnonBox, and to update frame-backed
   // pseudo-elements in ServoRestyleManager.
   //
   // This gets a style context that will be the new style context for
   // `aChildFrame`, and takes care of updating it, calling CalcStyleDifference,
   // and adding to the change list as appropriate.
   //
   // Returns the generated change hint for the frame.
   nsChangeHint UpdateStyleOfOwnedChildFrame(
-      nsIFrame* aChildFrame,
-      nsStyleContext* aNewStyleContext,
-      nsStyleChangeList& aChangeList);
+    nsIFrame* aChildFrame,
+    nsStyleContext* aNewStyleContext,
+    mozilla::ServoRestyleState& aRestyleState);
 
   struct OwnedAnonBox
   {
-    typedef void (*UpdateStyleFn)(nsIFrame* aOwningFrame, nsIFrame* aAnonBox,
-                                  mozilla::ServoStyleSet&,
-                                  nsStyleChangeList&, nsChangeHint);
+    typedef void (*UpdateStyleFn)(nsIFrame* aOwningFrame,
+                                  nsIFrame* aAnonBox,
+                                  mozilla::ServoRestyleState& aRestyleState);
 
     explicit OwnedAnonBox(nsIFrame* aAnonBoxFrame,
                           UpdateStyleFn aUpdateStyleFn = nullptr)
       : mAnonBoxFrame(aAnonBoxFrame)
       , mUpdateStyleFn(aUpdateStyleFn)
     {}
 
     nsIFrame* mAnonBoxFrame;
--- a/layout/generic/nsInlineFrame.cpp
+++ b/layout/generic/nsInlineFrame.cpp
@@ -1010,19 +1010,17 @@ nsInlineFrame::AccessibleType()
     return a11y::eHyperTextType;
 
   return a11y::eNoType;
 }
 #endif
 
 void
 nsInlineFrame::UpdateStyleOfOwnedAnonBoxesForIBSplit(
-    ServoStyleSet& aStyleSet,
-    nsStyleChangeList& aChangeList,
-    nsChangeHint aHintForThisFrame)
+  ServoRestyleState& aRestyleState)
 {
   MOZ_ASSERT(GetStateBits() & NS_FRAME_OWNS_ANON_BOXES,
              "Why did we get called?");
   MOZ_ASSERT(GetStateBits() & NS_FRAME_PART_OF_IBSPLIT,
              "Why did we have the NS_FRAME_OWNS_ANON_BOXES bit set?");
   // Note: this assert _looks_ expensive, but it's cheap in all the cases when
   // it passes!
   MOZ_ASSERT(nsLayoutUtils::FirstContinuationOrIBSplitSibling(this) == this,
@@ -1032,23 +1030,23 @@ nsInlineFrame::UpdateStyleOfOwnedAnonBox
              "We should be the primary frame for our element");
 
   nsIFrame* blockFrame = GetProperty(nsIFrame::IBSplitSibling());
   MOZ_ASSERT(blockFrame, "Why did we have an IB split?");
 
   // The anonymous block's style inherits from ours, and we already have our new
   // style context.
   RefPtr<nsStyleContext> newContext =
-    aStyleSet.ResolveInheritingAnonymousBoxStyle(
+    aRestyleState.StyleSet().ResolveInheritingAnonymousBoxStyle(
       nsCSSAnonBoxes::mozBlockInsideInlineWrapper, StyleContext());
 
   // We're guaranteed that newContext only differs from the old style context on
   // the block in things they might inherit from us.  And changehint processing
   // guarantees walking the continuation and ib-sibling chains, so our existing
-  // changehint beign in aChangeList is good enough.  So we don't need to touch
+  // changehint being in aChangeList is good enough.  So we don't need to touch
   // aChangeList at all here.
 
   while (blockFrame) {
     MOZ_ASSERT(!blockFrame->GetPrevContinuation(),
                "Must be first continuation");
 
     MOZ_ASSERT(blockFrame->StyleContext()->GetPseudo() ==
                nsCSSAnonBoxes::mozBlockInsideInlineWrapper,
--- a/layout/generic/nsInlineFrame.h
+++ b/layout/generic/nsInlineFrame.h
@@ -112,19 +112,17 @@ public:
     return (GetStateBits() & NS_INLINE_FRAME_BIDI_VISUAL_STATE_IS_SET)
              ? !!(GetStateBits() & NS_INLINE_FRAME_BIDI_VISUAL_IS_LAST)
              : (!GetNextInFlow());
   }
 
   // Restyles the block wrappers around our non-inline-outside kids.
   // This will only be called when such wrappers in fact exist.
   void UpdateStyleOfOwnedAnonBoxesForIBSplit(
-      mozilla::ServoStyleSet& aStyleSet,
-      nsStyleChangeList& aChangeList,
-      nsChangeHint aHintForThisFrame);
+    mozilla::ServoRestyleState& aRestyleState);
 
 protected:
   // Additional reflow state used during our reflow methods
   struct InlineReflowInput {
     nsIFrame* mPrevFrame;
     nsInlineFrame* mNextInFlow;
     nsIFrame*      mLineContainer;
     nsLineLayout*  mLineLayout;
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -34,16 +34,17 @@
 #include "nsCSSAnonBoxes.h"
 #include "nsIPresShell.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMHTMLElement.h"
 #include "nsIScriptError.h"
 #include "nsFrameManager.h"
 #include "nsError.h"
 #include "nsCSSFrameConstructor.h"
+#include "mozilla/ServoRestyleManager.h"
 #include "mozilla/ServoStyleSet.h"
 #include "mozilla/StyleSetHandle.h"
 #include "mozilla/StyleSetHandleInlines.h"
 #include "nsDisplayList.h"
 #include "nsIScrollableFrame.h"
 #include "nsCSSProps.h"
 #include "RestyleTracker.h"
 #include "nsStyleChangeList.h"
@@ -8008,43 +8009,43 @@ nsTableFrame::AppendDirectlyOwnedAnonBox
                nsCSSAnonBoxes::tableWrapper,
              "What happened to our parent?");
   aResult.AppendElement(
     OwnedAnonBox(wrapper, &UpdateStyleOfOwnedAnonBoxesForTableWrapper));
 }
 
 /* static */ void
 nsTableFrame::UpdateStyleOfOwnedAnonBoxesForTableWrapper(
-    nsIFrame* aOwningFrame,
-    nsIFrame* aWrapperFrame,
-    ServoStyleSet& aStyleSet,
-    nsStyleChangeList& aChangeList,
-    nsChangeHint aHintForThisFrame)
+  nsIFrame* aOwningFrame,
+  nsIFrame* aWrapperFrame,
+  ServoRestyleState& aRestyleState)
 {
   MOZ_ASSERT(aWrapperFrame->StyleContext()->GetPseudo() ==
                nsCSSAnonBoxes::tableWrapper,
              "What happened to our parent?");
 
   RefPtr<nsStyleContext> newContext =
-    aStyleSet.ResolveInheritingAnonymousBoxStyle(nsCSSAnonBoxes::tableWrapper,
-                                                 aOwningFrame->StyleContext());
+    aRestyleState.StyleSet().ResolveInheritingAnonymousBoxStyle(
+      nsCSSAnonBoxes::tableWrapper, aOwningFrame->StyleContext());
 
   // Figure out whether we have an actual change.  It's important that we do
   // this, even though all the wrapper's changes are due to properties it
   // inherits from us, because it's possible that no one ever asked us for those
   // style structs and hence changes to them aren't reflected in
   // aHintForThisFrame at all.
   uint32_t equalStructs, samePointerStructs; // Not used, actually.
   nsChangeHint wrapperHint = aWrapperFrame->StyleContext()->CalcStyleDifference(
     newContext,
     &equalStructs,
     &samePointerStructs);
+  wrapperHint =
+    NS_RemoveSubsumedHints(wrapperHint, aRestyleState.ChangesHandled());
   if (wrapperHint) {
-    aChangeList.AppendChange(aWrapperFrame, aWrapperFrame->GetContent(),
-                             wrapperHint);
+    aRestyleState.ChangeList().AppendChange(
+      aWrapperFrame, aWrapperFrame->GetContent(), wrapperHint);
   }
 
   for (nsIFrame* cur = aWrapperFrame; cur; cur = cur->GetNextContinuation()) {
     cur->SetStyleContext(newContext);
   }
 
   MOZ_ASSERT(!(aWrapperFrame->GetStateBits() & NS_FRAME_OWNS_ANON_BOXES),
              "Wrapper frame doesn't have any anon boxes of its own!");
--- a/layout/tables/nsTableFrame.h
+++ b/layout/tables/nsTableFrame.h
@@ -593,21 +593,19 @@ public:
 
   virtual bool ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) override;
 
   // Return our wrapper frame.
   void AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) override;
 
 protected:
   static void UpdateStyleOfOwnedAnonBoxesForTableWrapper(
-      nsIFrame* aOwningFrame,
-      nsIFrame* aWrapperFrame,
-      mozilla::ServoStyleSet& aStyleSet,
-      nsStyleChangeList& aChangeList,
-      nsChangeHint aHintForThisFrame);
+    nsIFrame* aOwningFrame,
+    nsIFrame* aWrapperFrame,
+    mozilla::ServoRestyleState& aRestyleState);
 
   /** protected constructor.
     * @see NewFrame
     */
   explicit nsTableFrame(nsStyleContext* aContext, ClassID aID = kClassID);
 
   /** destructor, responsible for mColumnLayoutData */
   virtual ~nsTableFrame();