Bug 1533963 - Use a single RestyleHint representation. r=heycam
authorEmilio Cobos Álvarez <emilio@crisal.io>
Thu, 14 Mar 2019 11:47:50 +0000
changeset 521872 8995d452cf89
parent 521871 d8b7c29dcee0
child 521873 802aec0783a7
push id10870
push usernbeleuzu@mozilla.com
push dateFri, 15 Mar 2019 20:00:07 +0000
treeherdermozilla-beta@c594aee5b7a4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs1533963
milestone67.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 1533963 - Use a single RestyleHint representation. r=heycam Differential Revision: https://phabricator.services.mozilla.com/D22828
dom/animation/EffectCompositor.cpp
dom/base/Element.cpp
dom/base/Location.cpp
dom/base/nsDOMWindowUtils.cpp
dom/base/nsFrameLoader.cpp
dom/base/nsHistory.cpp
dom/html/HTMLInputElement.cpp
dom/html/HTMLTextAreaElement.cpp
dom/interfaces/base/nsIDOMWindowUtils.idl
dom/smil/SMILAnimationController.cpp
dom/svg/SVGElement.cpp
dom/svg/SVGSwitchElement.cpp
dom/svg/SVGViewportElement.cpp
layout/base/PresShell.cpp
layout/base/RestyleManager.cpp
layout/base/RestyleManager.h
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsChangeHint.h
layout/base/nsIPresShell.h
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
layout/mathml/nsMathMLmtableFrame.cpp
layout/style/MediaFeatureChange.h
layout/style/ServoBindings.h
layout/style/ServoBindings.toml
layout/style/ServoStyleSet.cpp
layout/style/ServoStyleSet.h
layout/style/nsHTMLStyleSheet.cpp
layout/svg/SVGGeometryFrame.cpp
layout/svg/SVGObserverUtils.cpp
layout/svg/SVGTextFrame.cpp
layout/svg/nsSVGContainerFrame.cpp
layout/svg/nsSVGForeignObjectFrame.cpp
layout/svg/nsSVGImageFrame.cpp
layout/svg/nsSVGUseFrame.cpp
layout/svg/nsSVGViewportFrame.cpp
layout/tables/nsTableCellFrame.cpp
servo/components/style/cbindgen.toml
servo/components/style/gecko/wrapper.rs
servo/components/style/invalidation/element/restyle_hints.rs
servo/ports/geckolib/glue.rs
widget/gtk/nsWindow.cpp
--- a/dom/animation/EffectCompositor.cpp
+++ b/dom/animation/EffectCompositor.cpp
@@ -291,26 +291,26 @@ void EffectCompositor::PostRestyleForAni
     return;
   }
 
   dom::Element* element = GetElementToRestyle(aElement, aPseudoType);
   if (!element) {
     return;
   }
 
-  nsRestyleHint hint = aCascadeLevel == CascadeLevel::Transitions
-                           ? eRestyle_CSSTransitions
-                           : eRestyle_CSSAnimations;
+  RestyleHint hint = aCascadeLevel == CascadeLevel::Transitions
+                         ? StyleRestyleHint_RESTYLE_CSS_TRANSITIONS
+                         : StyleRestyleHint_RESTYLE_CSS_ANIMATIONS;
 
   MOZ_ASSERT(NS_IsMainThread(),
              "Restyle request during restyling should be requested only on "
              "the main-thread. e.g. after the parallel traversal");
   if (ServoStyleSet::IsInServoTraversal() || mIsInPreTraverse) {
-    MOZ_ASSERT(hint == eRestyle_CSSAnimations ||
-               hint == eRestyle_CSSTransitions);
+    MOZ_ASSERT(hint == StyleRestyleHint_RESTYLE_CSS_ANIMATIONS ||
+               hint == StyleRestyleHint_RESTYLE_CSS_TRANSITIONS);
 
     // We can't call Servo_NoteExplicitHints here since AtomicRefCell does not
     // allow us mutate ElementData of the |aElement| in SequentialTask.
     // Instead we call Servo_NoteExplicitHints for the element in PreTraverse()
     // which will be called right before the second traversal that we do for
     // updating CSS animations.
     // In that case PreTraverse() will return true so that we know to do the
     // second traversal so we don't need to post any restyle requests to the
@@ -825,18 +825,19 @@ bool EffectCompositor::PreTraverseInSubt
       }
 
       // We need to post restyle hints even if the target is not in EffectSet to
       // ensure the final restyling for removed animations.
       // We can't call PostRestyleEvent directly here since we are still in the
       // middle of the servo traversal.
       mPresContext->RestyleManager()->PostRestyleEventForAnimations(
           target.mElement, target.mPseudoType,
-          cascadeLevel == CascadeLevel::Transitions ? eRestyle_CSSTransitions
-                                                    : eRestyle_CSSAnimations);
+          cascadeLevel == CascadeLevel::Transitions
+              ? StyleRestyleHint_RESTYLE_CSS_TRANSITIONS
+              : StyleRestyleHint_RESTYLE_CSS_ANIMATIONS);
 
       foundElementsNeedingRestyle = true;
 
       EffectSet* effects =
           EffectSet::GetEffectSet(target.mElement, target.mPseudoType);
       if (!effects) {
         // Drop EffectSets that have been destroyed.
         iter.Remove();
@@ -903,21 +904,21 @@ bool EffectCompositor::PreTraverse(dom::
     bool hasUnthrottledRestyle = false;
     if (!elementSet.Get(key, &hasUnthrottledRestyle) ||
         (!flushThrottledRestyles && !hasUnthrottledRestyle)) {
       continue;
     }
 
     mPresContext->RestyleManager()->PostRestyleEventForAnimations(
         aElement, aPseudoType,
-        cascadeLevel == CascadeLevel::Transitions ? eRestyle_CSSTransitions
-                                                  : eRestyle_CSSAnimations);
+        cascadeLevel == CascadeLevel::Transitions
+            ? StyleRestyleHint_RESTYLE_CSS_TRANSITIONS
+            : StyleRestyleHint_RESTYLE_CSS_ANIMATIONS);
 
-    EffectSet* effects = EffectSet::GetEffectSet(aElement, aPseudoType);
-    if (effects) {
+    if (EffectSet* effects = EffectSet::GetEffectSet(aElement, aPseudoType)) {
       MaybeUpdateCascadeResults(aElement, aPseudoType);
 
       for (KeyframeEffect* effect : *effects) {
         effect->GetAnimation()->WillComposeStyle();
       }
     }
 
     elementSet.Remove(key);
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -2028,24 +2028,22 @@ DeclarationBlock* Element::GetSMILOverri
 }
 
 nsresult Element::SetSMILOverrideStyleDeclaration(
     DeclarationBlock* aDeclaration) {
   Element::nsExtendedDOMSlots* slots = ExtendedDOMSlots();
 
   slots->mSMILOverrideStyleDeclaration = aDeclaration;
 
-  Document* doc = GetComposedDoc();
   // Only need to request a restyle if we're in a document.  (We might not
   // be in a document, if we're clearing animation effects on a target node
   // that's been detached since the previous animation sample.)
-  if (doc) {
-    nsCOMPtr<nsIPresShell> shell = doc->GetShell();
-    if (shell) {
-      shell->RestyleForAnimation(this, eRestyle_StyleAttribute_Animations);
+  if (Document* doc = GetComposedDoc()) {
+    if (nsIPresShell* shell = doc->GetShell()) {
+      shell->RestyleForAnimation(this, StyleRestyleHint_RESTYLE_SMIL);
     }
   }
 
   return NS_OK;
 }
 
 bool Element::IsLabelable() const { return false; }
 
--- a/dom/base/Location.cpp
+++ b/dom/base/Location.cpp
@@ -767,17 +767,18 @@ nsresult Location::Reload(bool aForceget
     // of reloading the page, just clear style data and reflow the
     // page since some sites may use this trick to work around gecko
     // reflow bugs, and this should have the same effect.
 
     nsCOMPtr<Document> doc = window->GetExtantDoc();
 
     nsPresContext* pcx;
     if (doc && (pcx = doc->GetPresContext())) {
-      pcx->RebuildAllStyleData(NS_STYLE_HINT_REFLOW, eRestyle_Subtree);
+      pcx->RebuildAllStyleData(NS_STYLE_HINT_REFLOW,
+                               RestyleHint::RestyleSubtree());
     }
 
     return NS_OK;
   }
 
   if (!webNav) {
     return NS_ERROR_FAILURE;
   }
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -3627,17 +3627,18 @@ nsDOMWindowUtils::GetCompositorAPZTestDa
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::PostRestyleSelfEvent(Element* aElement) {
   if (!aElement) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  nsLayoutUtils::PostRestyleEvent(aElement, eRestyle_Self, nsChangeHint(0));
+  nsLayoutUtils::PostRestyleEvent(aElement, StyleRestyleHint_RESTYLE_SELF,
+                                  nsChangeHint(0));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::GetMediaSuspend(uint32_t* aSuspend) {
   nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryReferent(mWindow);
   NS_ENSURE_STATE(window);
 
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -844,21 +844,22 @@ void nsFrameLoader::MarginsChanged(uint3
       if (cur->IsHTMLElement(nsGkAtoms::body)) {
         static_cast<HTMLBodyElement*>(cur)->ClearMappedServoStyle();
       }
     }
   }
 
   // Trigger a restyle if there's a prescontext
   // FIXME: This could do something much less expensive.
-  RefPtr<nsPresContext> presContext = mDocShell->GetPresContext();
-  if (presContext)
+  if (nsPresContext* presContext = mDocShell->GetPresContext()) {
     // rebuild, because now the same nsMappedAttributes* will produce
     // a different style
-    presContext->RebuildAllStyleData(nsChangeHint(0), eRestyle_Subtree);
+    presContext->RebuildAllStyleData(nsChangeHint(0),
+                                     RestyleHint::RestyleSubtree());
+  }
 }
 
 bool nsFrameLoader::ShowRemoteFrame(const ScreenIntSize& size,
                                     nsSubDocumentFrame* aFrame) {
   AUTO_PROFILER_LABEL("nsFrameLoader::ShowRemoteFrame", OTHER);
   NS_ASSERTION(IsRemoteFrame(),
                "ShowRemote only makes sense on remote frames.");
 
--- a/dom/base/nsHistory.cpp
+++ b/dom/base/nsHistory.cpp
@@ -161,17 +161,18 @@ void nsHistory::Go(int32_t aDelta, Error
       // style data and reflow the page since some sites may use this
       // trick to work around gecko reflow bugs, and this should have
       // the same effect.
 
       nsCOMPtr<Document> doc = window->GetExtantDoc();
 
       nsPresContext* pcx;
       if (doc && (pcx = doc->GetPresContext())) {
-        pcx->RebuildAllStyleData(NS_STYLE_HINT_REFLOW, eRestyle_Subtree);
+        pcx->RebuildAllStyleData(NS_STYLE_HINT_REFLOW,
+                                 RestyleHint::RestyleSubtree());
       }
 
       return;
     }
 
     // https://html.spec.whatwg.org/multipage/history.html#the-history-interface
     // "When the go(delta) method is invoked, if delta is zero, the user agent
     // must act as if the location.reload() method was called instead."
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -2300,17 +2300,17 @@ HTMLInputElement::GetPreviewValue(nsAStr
 NS_IMETHODIMP_(void)
 HTMLInputElement::EnablePreview() {
   if (mIsPreviewEnabled) {
     return;
   }
 
   mIsPreviewEnabled = true;
   // Reconstruct the frame to append an anonymous preview node
-  nsLayoutUtils::PostRestyleEvent(this, nsRestyleHint(0),
+  nsLayoutUtils::PostRestyleEvent(this, RestyleHint{0},
                                   nsChangeHint_ReconstructFrame);
 }
 
 NS_IMETHODIMP_(bool)
 HTMLInputElement::IsPreviewEnabled() { return mIsPreviewEnabled; }
 
 NS_IMETHODIMP_(bool)
 HTMLInputElement::GetPreviewVisibility() {
--- a/dom/html/HTMLTextAreaElement.cpp
+++ b/dom/html/HTMLTextAreaElement.cpp
@@ -253,17 +253,17 @@ HTMLTextAreaElement::GetPreviewValue(nsA
 NS_IMETHODIMP_(void)
 HTMLTextAreaElement::EnablePreview() {
   if (mIsPreviewEnabled) {
     return;
   }
 
   mIsPreviewEnabled = true;
   // Reconstruct the frame to append an anonymous preview node
-  nsLayoutUtils::PostRestyleEvent(this, nsRestyleHint(0),
+  nsLayoutUtils::PostRestyleEvent(this, RestyleHint{0},
                                   nsChangeHint_ReconstructFrame);
 }
 
 NS_IMETHODIMP_(bool)
 HTMLTextAreaElement::IsPreviewEnabled() { return mIsPreviewEnabled; }
 
 NS_IMETHODIMP_(bool)
 HTMLTextAreaElement::GetPreviewVisibility() {
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -1730,17 +1730,17 @@ interface nsIDOMWindowUtils : nsISupport
   /**
    * Get the content- and compositor-side APZ test data instances.
    * The return values are of type APZTestData (see APZTestData.webidl).
    */
   [implicit_jscontext] jsval getContentAPZTestData();
   [implicit_jscontext] jsval getCompositorAPZTestData();
 
   /**
-   * Posts an eRestyle_Self restyle event for the given element.
+   * Posts an RestyleHint::RESTYLE_SELF restyle event for the given element.
    */
   void postRestyleSelfEvent(in Element aElement);
 
   /**
    * Used to pause or resume all media in this window. Use-cases are audio
    * competing, remote media control and to prevent auto-playing media.
    */
   attribute uint32_t mediaSuspend;
--- a/dom/smil/SMILAnimationController.cpp
+++ b/dom/smil/SMILAnimationController.cpp
@@ -674,17 +674,17 @@ bool SMILAnimationController::PreTravers
     // aRoot.
     if (aRoot && !nsContentUtils::ContentIsFlattenedTreeDescendantOf(
                      key.mElement, aRoot)) {
       continue;
     }
 
     context->RestyleManager()->PostRestyleEventForAnimations(
         key.mElement, PseudoStyleType::NotPseudo,
-        eRestyle_StyleAttribute_Animations);
+        StyleRestyleHint_RESTYLE_SMIL);
 
     foundElementsNeedingRestyle = true;
   }
 
   // Only clear the mMightHavePendingStyleUpdates flag if we definitely posted
   // all restyles.
   if (!aRoot) {
     mMightHavePendingStyleUpdates = false;
--- a/dom/svg/SVGElement.cpp
+++ b/dom/svg/SVGElement.cpp
@@ -102,31 +102,32 @@ JSObject* SVGElement::WrapNode(JSContext
 
 //----------------------------------------------------------------------
 // SVGElement methods
 
 void SVGElement::DidAnimateClass() {
   // For Servo, snapshot the element before we change it.
   nsIPresShell* shell = OwnerDoc()->GetShell();
   if (shell) {
-    nsPresContext* presContext = shell->GetPresContext();
-    if (presContext) {
+    if (nsPresContext* presContext = shell->GetPresContext()) {
       presContext->RestyleManager()->ClassAttributeWillBeChangedBySMIL(this);
     }
   }
 
   nsAutoString src;
   mClassAttribute.GetAnimValue(src, this);
   if (!mClassAnimAttr) {
     mClassAnimAttr = new nsAttrValue();
   }
   mClassAnimAttr->ParseAtomArray(src);
 
+  // FIXME(emilio): This re-selector-matches, but we do the snapshot stuff right
+  // above... Is this needed anymore?
   if (shell) {
-    shell->RestyleForAnimation(this, eRestyle_Self);
+    shell->RestyleForAnimation(this, StyleRestyleHint_RESTYLE_SELF);
   }
 }
 
 nsresult SVGElement::Init() {
   // Set up length attributes - can't do this in the constructor
   // because we can't do a virtual call at that point
 
   LengthAttributesInfo lengthInfo = GetLengthInfo();
@@ -1999,17 +2000,17 @@ void SVGElement::DidAnimateTransformList
     // SVGTransformableElement::GetAttributeChangeHint will be called and an
     // appropriate change event posted to update our frame's overflow rects.
     // The SetAttrAndNotify doesn't happen for transform changes caused by
     // 'animateTransform' though (and sending out the mutation events that
     // nsNodeUtils::AttributeChanged dispatches would be inappropriate
     // anyway), so we need to post the change event ourself.
     nsChangeHint changeHint = GetAttributeChangeHint(transformAttr, aModType);
     if (changeHint) {
-      nsLayoutUtils::PostRestyleEvent(this, nsRestyleHint(0), changeHint);
+      nsLayoutUtils::PostRestyleEvent(this, RestyleHint{0}, changeHint);
     }
   }
 }
 
 SVGElement::StringAttributesInfo SVGElement::GetStringInfo() {
   return StringAttributesInfo(nullptr, nullptr, 0);
 }
 
--- a/dom/svg/SVGSwitchElement.cpp
+++ b/dom/svg/SVGSwitchElement.cpp
@@ -52,17 +52,17 @@ void SVGSwitchElement::MaybeInvalidate()
   nsIContent* newActiveChild = FindActiveChild();
 
   if (newActiveChild == mActiveChild) {
     return;
   }
 
   nsIFrame* frame = GetPrimaryFrame();
   if (frame) {
-    nsLayoutUtils::PostRestyleEvent(this, nsRestyleHint(0),
+    nsLayoutUtils::PostRestyleEvent(this, RestyleHint{0},
                                     nsChangeHint_InvalidateRenderingObservers);
     nsSVGUtils::ScheduleReflowSVG(frame);
   }
 
   mActiveChild = newActiveChild;
 }
 
 //----------------------------------------------------------------------
--- a/dom/svg/SVGViewportElement.cpp
+++ b/dom/svg/SVGViewportElement.cpp
@@ -162,17 +162,17 @@ void SVGViewportElement::ChildrenOnlyTra
 
   // If we're not reconstructing the frame tree, then we only call
   // PostRestyleEvent if we're not being called under reflow to avoid recursing
   // to death. See bug 767056 comments 10 and 12. Since our nsSVGOuterSVGFrame
   // is being reflowed we're going to invalidate and repaint its entire area
   // anyway (which will include our children).
   if ((changeHint & nsChangeHint_ReconstructFrame) ||
       !(aFlags & eDuringReflow)) {
-    nsLayoutUtils::PostRestyleEvent(this, nsRestyleHint(0), changeHint);
+    nsLayoutUtils::PostRestyleEvent(this, RestyleHint{0}, changeHint);
   }
 }
 
 gfx::Matrix SVGViewportElement::GetViewBoxTransform() const {
   float viewportWidth, viewportHeight;
   if (IsInner()) {
     SVGViewportElement* ctx = GetCtx();
     viewportWidth = mLengthAttributes[ATTR_WIDTH].GetAnimValue(ctx);
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -2917,37 +2917,34 @@ void nsIPresShell::DestroyFramesForAndRe
   // Clear the style data from all the flattened tree descendants, but _not_
   // from us, since otherwise we wouldn't see the reframe.
   RestyleManager::ClearServoDataFromSubtree(aElement,
                                             RestyleManager::IncludeRoot::No);
 
   auto changeHint =
       didReconstruct ? nsChangeHint(0) : nsChangeHint_ReconstructFrame;
 
-  // NOTE(emilio): eRestyle_Subtree is needed to force also a full subtree
-  // restyle for the content (in Stylo, where the existence of frames != the
-  // existence of styles).
-  mPresContext->RestyleManager()->PostRestyleEvent(aElement, eRestyle_Subtree,
-                                                   changeHint);
+  mPresContext->RestyleManager()->PostRestyleEvent(
+      aElement, RestyleHint::RestyleSubtree(), changeHint);
 
   --mChangeNestCount;
 }
 
 void nsIPresShell::PostRecreateFramesFor(Element* aElement) {
   if (MOZ_UNLIKELY(!mDidInitialize)) {
     // Nothing to do here. In fact, if we proceed and aElement is the root, we
     // will crash.
     return;
   }
 
   mPresContext->RestyleManager()->PostRestyleEvent(
-      aElement, nsRestyleHint(0), nsChangeHint_ReconstructFrame);
-}
-
-void nsIPresShell::RestyleForAnimation(Element* aElement, nsRestyleHint aHint) {
+      aElement, RestyleHint{0}, nsChangeHint_ReconstructFrame);
+}
+
+void nsIPresShell::RestyleForAnimation(Element* aElement, RestyleHint aHint) {
   // Now that we no longer have separate non-animation and animation
   // restyles, this method having a distinct identity is less important,
   // but it still seems useful to offer as a "more public" API and as a
   // chokepoint for these restyles to go through.
   mPresContext->RestyleManager()->PostRestyleEvent(aElement, aHint,
                                                    nsChangeHint(0));
 }
 
@@ -6523,17 +6520,18 @@ nsresult PresShell::EventHandler::Handle
 
   // Activation events need to be dispatched even if no frame was found, since
   // we don't want the focus to be out of sync.
   if (!aFrameForPresShell) {
     if (!NS_EVENT_NEEDS_FRAME(aGUIEvent)) {
       mPresShell->mCurrentEventFrame = nullptr;
       // XXX Shouldn't we create AutoCurrentEventInfoSetter instance for this
       //     call even if we set the target to nullptr.
-      return HandleEventWithCurrentEventInfo(aGUIEvent, aEventStatus, true, nullptr);
+      return HandleEventWithCurrentEventInfo(aGUIEvent, aEventStatus, true,
+                                             nullptr);
     }
 
     if (aGUIEvent->HasKeyEventMessage()) {
       // Keypress events in new blank tabs should not be completely thrown away.
       // Retarget them -- the parent chrome shell might make use of them.
       return RetargetEventToParent(aGUIEvent, aEventStatus);
     }
 
@@ -7447,17 +7445,18 @@ nsresult PresShell::EventHandler::Handle
   // with parent PresShell.
   mPresShell->mCurrentEventContent = eventTargetElement;
   if (!mPresShell->GetCurrentEventContent() ||
       !mPresShell->GetCurrentEventFrame() ||
       InZombieDocument(mPresShell->mCurrentEventContent)) {
     return RetargetEventToParent(aGUIEvent, aEventStatus);
   }
 
-  nsresult rv = HandleEventWithCurrentEventInfo(aGUIEvent, aEventStatus, true, nullptr);
+  nsresult rv =
+      HandleEventWithCurrentEventInfo(aGUIEvent, aEventStatus, true, nullptr);
 
 #ifdef DEBUG
   mPresShell->ShowEventTargetDebug();
 #endif
 
   return rv;
 }
 
@@ -7551,17 +7550,18 @@ nsresult PresShell::EventHandler::Handle
   MOZ_ASSERT(!aGUIEvent->IsTargetedAtFocusedContent());
   MOZ_ASSERT(aEventStatus);
 
   AutoCurrentEventInfoSetter eventInfoSetter(*this, aFrameForPresShell,
                                              nullptr);
 
   nsresult rv = NS_OK;
   if (mPresShell->GetCurrentEventFrame()) {
-    rv = HandleEventWithCurrentEventInfo(aGUIEvent, aEventStatus, true, nullptr);
+    rv =
+        HandleEventWithCurrentEventInfo(aGUIEvent, aEventStatus, true, nullptr);
   }
 
 #ifdef DEBUG
   mPresShell->ShowEventTargetDebug();
 #endif
 
   return rv;
 }
@@ -9402,17 +9402,17 @@ static bool ReResolveMenusAndTrees(nsIFr
   nsMenuFrame* menu = do_QueryFrame(aFrame);
   if (menu) menu->CloseMenu(true);
   return true;
 }
 
 static bool ReframeImageBoxes(nsIFrame* aFrame) {
   if (aFrame->IsImageBoxFrame()) {
     aFrame->PresContext()->RestyleManager()->PostRestyleEvent(
-        aFrame->GetContent()->AsElement(), nsRestyleHint(0),
+        aFrame->GetContent()->AsElement(), RestyleHint{0},
         nsChangeHint_ReconstructFrame);
     return false;  // don't walk descendants
   }
   return true;  // walk descendants
 }
 
 static void WalkFramesThroughPlaceholders(nsPresContext* aPresContext,
                                           nsIFrame* aFrame,
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -113,101 +113,96 @@ void RestyleManager::ContentAppended(nsI
     }
     if (wasEmpty) {
       RestyleForEmptyChange(container);
       return;
     }
   }
 
   if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
-    PostRestyleEvent(container, eRestyle_Subtree, nsChangeHint(0));
+    PostRestyleEvent(container, RestyleHint::RestyleSubtree(), nsChangeHint(0));
     // Restyling the container is the most we can do here, so we're done.
     return;
   }
 
   if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
     // restyle the last element child before this node
     for (nsIContent* cur = aFirstNewContent->GetPreviousSibling(); cur;
          cur = cur->GetPreviousSibling()) {
       if (cur->IsElement()) {
-        PostRestyleEvent(cur->AsElement(), eRestyle_Subtree, nsChangeHint(0));
+        PostRestyleEvent(cur->AsElement(), RestyleHint::RestyleSubtree(),
+                         nsChangeHint(0));
         break;
       }
     }
   }
 }
 
+static void RestyleSiblingsStartingWith(RestyleManager& aRM,
+                                        nsIContent* aStartingSibling) {
+  for (nsIContent* sibling = aStartingSibling; sibling;
+       sibling = sibling->GetNextSibling()) {
+    if (auto* element = Element::FromNode(sibling)) {
+      aRM.PostRestyleEvent(element, RestyleHint::RestyleSubtree(),
+                           nsChangeHint(0));
+    }
+  }
+}
+
 void RestyleManager::RestyleForEmptyChange(Element* aContainer) {
+  PostRestyleEvent(aContainer, RestyleHint::RestyleSubtree(), nsChangeHint(0));
+
   // In some cases (:empty + E, :empty ~ E), a change in the content of
   // an element requires restyling its parent's siblings.
-  nsRestyleHint hint = eRestyle_Subtree;
   nsIContent* grandparent = aContainer->GetParent();
-  if (grandparent &&
-      (grandparent->GetFlags() & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS)) {
-    hint = nsRestyleHint(hint | eRestyle_LaterSiblings);
+  if (!grandparent ||
+      !(grandparent->GetFlags() & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS)) {
+    return;
   }
-  PostRestyleEvent(aContainer, hint, nsChangeHint(0));
+  RestyleSiblingsStartingWith(*this, aContainer->GetNextSibling());
 }
 
 void RestyleManager::MaybeRestyleForEdgeChildChange(Element* aContainer,
                                                     nsIContent* aChangedChild) {
   MOZ_ASSERT(aContainer->GetFlags() & NODE_HAS_EDGE_CHILD_SELECTOR);
   MOZ_ASSERT(aChangedChild->GetParent() == aContainer);
   // restyle the previously-first element child if it is after this node
   bool passedChild = false;
   for (nsIContent* content = aContainer->GetFirstChild(); content;
        content = content->GetNextSibling()) {
     if (content == aChangedChild) {
       passedChild = true;
       continue;
     }
     if (content->IsElement()) {
       if (passedChild) {
-        PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
+        PostRestyleEvent(content->AsElement(), RestyleHint::RestyleSubtree(),
                          nsChangeHint(0));
       }
       break;
     }
   }
   // restyle the previously-last element child if it is before this node
   passedChild = false;
   for (nsIContent* content = aContainer->GetLastChild(); content;
        content = content->GetPreviousSibling()) {
     if (content == aChangedChild) {
       passedChild = true;
       continue;
     }
     if (content->IsElement()) {
       if (passedChild) {
-        PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
+        PostRestyleEvent(content->AsElement(), RestyleHint::RestyleSubtree(),
                          nsChangeHint(0));
       }
       break;
     }
   }
 }
 
-// Needed since we can't use PostRestyleEvent on non-elements (with
-// eRestyle_LaterSiblings or nsRestyleHint(eRestyle_Subtree |
-// eRestyle_LaterSiblings) as appropriate).
-static void RestyleSiblingsStartingWith(
-    RestyleManager* aRestyleManager,
-    nsIContent* aStartingSibling /* may be null */) {
-  for (nsIContent* sibling = aStartingSibling; sibling;
-       sibling = sibling->GetNextSibling()) {
-    if (sibling->IsElement()) {
-      aRestyleManager->PostRestyleEvent(
-          sibling->AsElement(),
-          nsRestyleHint(eRestyle_Subtree | eRestyle_LaterSiblings),
-          nsChangeHint(0));
-      break;
-    }
-  }
-}
-
 template <typename CharT>
 bool WhitespaceOnly(const CharT* aBuffer, size_t aUpTo) {
   for (auto index : IntegerRange(aUpTo)) {
     if (!dom::IsSpaceCharacter(aBuffer[index])) {
       return false;
     }
   }
   return true;
@@ -349,24 +344,24 @@ void RestyleManager::RestyleForInsertOrC
       // unnecessarily. Also can restyle unnecessarily if aChild is not
       // significant anyway, though that's more unlikely.
       RestyleForEmptyChange(container);
       return;
     }
   }
 
   if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
-    PostRestyleEvent(container, eRestyle_Subtree, nsChangeHint(0));
+    PostRestyleEvent(container, RestyleHint::RestyleSubtree(), nsChangeHint(0));
     // Restyling the container is the most we can do here, so we're done.
     return;
   }
 
   if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
     // Restyle all later siblings.
-    RestyleSiblingsStartingWith(this, aChild->GetNextSibling());
+    RestyleSiblingsStartingWith(*this, aChild->GetNextSibling());
   }
 
   if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
     MaybeRestyleForEdgeChildChange(container, aChild);
   }
 }
 
 void RestyleManager::ContentRemoved(nsIContent* aOldChild,
@@ -411,50 +406,50 @@ void RestyleManager::ContentRemoved(nsIC
     }
     if (isEmpty) {
       RestyleForEmptyChange(container);
       return;
     }
   }
 
   if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
-    PostRestyleEvent(container, eRestyle_Subtree, nsChangeHint(0));
+    PostRestyleEvent(container, RestyleHint::RestyleSubtree(), nsChangeHint(0));
     // Restyling the container is the most we can do here, so we're done.
     return;
   }
 
   if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
     // Restyle all later siblings.
-    RestyleSiblingsStartingWith(this, aFollowingSibling);
+    RestyleSiblingsStartingWith(*this, aFollowingSibling);
   }
 
   if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
     // restyle the now-first element child if it was after aOldChild
     bool reachedFollowingSibling = false;
     for (nsIContent* content = container->GetFirstChild(); content;
          content = content->GetNextSibling()) {
       if (content == aFollowingSibling) {
         reachedFollowingSibling = true;
         // do NOT continue here; we might want to restyle this node
       }
       if (content->IsElement()) {
         if (reachedFollowingSibling) {
-          PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
+          PostRestyleEvent(content->AsElement(), RestyleHint::RestyleSubtree(),
                            nsChangeHint(0));
         }
         break;
       }
     }
     // restyle the now-last element child if it was before aOldChild
     reachedFollowingSibling = (aFollowingSibling == nullptr);
     for (nsIContent* content = container->GetLastChild(); content;
          content = content->GetPreviousSibling()) {
       if (content->IsElement()) {
         if (reachedFollowingSibling) {
-          PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
+          PostRestyleEvent(content->AsElement(), RestyleHint::RestyleSubtree(),
                            nsChangeHint(0));
         }
         break;
       }
       if (content == aFollowingSibling) {
         reachedFollowingSibling = true;
       }
     }
@@ -506,49 +501,16 @@ static nsChangeHint ChangeForContentStat
     // visited or not isn't really something we can worry about here.
     // FIXME: We could probably do this a bit better.
     changeHint |= nsChangeHint_RepaintFrame;
   }
 
   return changeHint;
 }
 
-/* static */
-nsCString RestyleManager::RestyleHintToString(nsRestyleHint aHint) {
-  nsCString result;
-  bool any = false;
-  const char* names[] = {"Self",           "SomeDescendants",
-                         "Subtree",        "LaterSiblings",
-                         "CSSTransitions", "CSSAnimations",
-                         "StyleAttribute", "StyleAttribute_Animations",
-                         "Force",          "ForceDescendants"};
-  uint32_t hint = aHint & ((1 << ArrayLength(names)) - 1);
-  uint32_t rest = aHint & ~((1 << ArrayLength(names)) - 1);
-  for (uint32_t i = 0; i < ArrayLength(names); i++) {
-    if (hint & (1 << i)) {
-      if (any) {
-        result.AppendLiteral(" | ");
-      }
-      result.AppendPrintf("eRestyle_%s", names[i]);
-      any = true;
-    }
-  }
-  if (rest) {
-    if (any) {
-      result.AppendLiteral(" | ");
-    }
-    result.AppendPrintf("0x%0x", rest);
-  } else {
-    if (!any) {
-      result.AppendLiteral("0");
-    }
-  }
-  return result;
-}
-
 #ifdef DEBUG
 /* static */
 nsCString RestyleManager::ChangeHintToString(nsChangeHint aHint) {
   nsCString result;
   bool any = false;
   const char* names[] = {"RepaintFrame",
                          "NeedReflow",
                          "ClearAncestorIntrinsics",
@@ -2288,95 +2250,85 @@ nsIFrame* ServoRestyleState::TableAwareP
     // Must be a caption.  In that case we want the table here.
     MOZ_ASSERT(aChild->StyleDisplay()->mDisplay == StyleDisplay::TableCaption);
     parent = parent->PrincipalChildList().FirstChild();
   }
   return parent;
 }
 
 void RestyleManager::PostRestyleEvent(Element* aElement,
-                                      nsRestyleHint aRestyleHint,
+                                      RestyleHint aRestyleHint,
                                       nsChangeHint aMinChangeHint) {
   MOZ_ASSERT(!(aMinChangeHint & nsChangeHint_NeutralChange),
              "Didn't expect explicit change hints to be neutral!");
   if (MOZ_UNLIKELY(IsDisconnected()) ||
       MOZ_UNLIKELY(PresContext()->PresShell()->IsDestroying())) {
     return;
   }
 
   // We allow posting restyles from within change hint handling, but not from
   // within the restyle algorithm itself.
   MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal());
 
-  if (aRestyleHint == 0 && !aMinChangeHint) {
+  if (!aRestyleHint && !aMinChangeHint) {
+    // FIXME(emilio): we should assert against this instead.
     return;  // Nothing to do.
   }
 
   // Assuming the restyle hints will invalidate cached style for
   // getComputedStyle, since we don't know if any of the restyling that we do
   // would affect undisplayed elements.
   if (aRestyleHint) {
+    if (!(aRestyleHint & RestyleHint::ForAnimations())) {
+      mHaveNonAnimationRestyles = true;
+    }
+
     IncrementUndisplayedRestyleGeneration();
   }
 
   // Processing change hints sometimes causes new change hints to be generated,
   // and very occasionally, additional restyle hints. We collect the change
   // hints manually to avoid re-traversing the DOM to find them.
   if (mReentrantChanges && !aRestyleHint) {
     mReentrantChanges->AppendElement(ReentrantChange{aElement, aMinChangeHint});
     return;
   }
 
-  if (aRestyleHint & ~eRestyle_AllHintsWithAnimations) {
-    mHaveNonAnimationRestyles = true;
-  }
-
-  if (aRestyleHint & eRestyle_LaterSiblings) {
-    aRestyleHint &= ~eRestyle_LaterSiblings;
-
-    nsRestyleHint siblingHint = eRestyle_Subtree;
-    Element* current = aElement->GetNextElementSibling();
-    while (current) {
-      Servo_NoteExplicitHints(current, siblingHint, nsChangeHint(0));
-      current = current->GetNextElementSibling();
-    }
-  }
-
   if (aRestyleHint || aMinChangeHint) {
     Servo_NoteExplicitHints(aElement, aRestyleHint, aMinChangeHint);
   }
 }
 
 void RestyleManager::PostRestyleEventForAnimations(Element* aElement,
                                                    PseudoStyleType aPseudoType,
-                                                   nsRestyleHint aRestyleHint) {
+                                                   RestyleHint aRestyleHint) {
   Element* elementToRestyle =
       EffectCompositor::GetElementToRestyle(aElement, aPseudoType);
 
   if (!elementToRestyle) {
     // FIXME: Bug 1371107: When reframing happens,
     // EffectCompositor::mElementsToRestyle still has unbound old pseudo
     // element. We should drop it.
     return;
   }
 
   AutoRestyleTimelineMarker marker(mPresContext->GetDocShell(),
                                    true /* animation-only */);
   Servo_NoteExplicitHints(elementToRestyle, aRestyleHint, nsChangeHint(0));
 }
 
 void RestyleManager::RebuildAllStyleData(nsChangeHint aExtraHint,
-                                         nsRestyleHint aRestyleHint) {
+                                         RestyleHint aRestyleHint) {
   // NOTE(emilio): GeckoRestlyeManager does a sync style flush, which seems not
   // to be needed in my testing.
   PostRebuildAllStyleDataEvent(aExtraHint, aRestyleHint);
 }
 
 void RestyleManager::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
-                                                  nsRestyleHint aRestyleHint) {
+                                                  RestyleHint aRestyleHint) {
   // NOTE(emilio): The semantics of these methods are quite funny, in the sense
   // that we're not supposed to need to rebuild the actual stylist data.
   //
   // That's handled as part of the MediumFeaturesChanged stuff, if needed.
   StyleSet()->ClearCachedStyleData();
 
   DocumentStyleRootIterator iter(mPresContext->Document());
   while (Element* root = iter.GetNextStyleRoot()) {
@@ -3228,17 +3180,17 @@ void RestyleManager::ContentStateChanged
       !Gecko_VisitedStylesEnabled(element.OwnerDoc())) {
     aChangedBits &= ~kVisitedAndUnvisited;
     if (aChangedBits.IsEmpty()) {
       return;
     }
   }
 
   if (auto changeHint = ChangeForContentStateChange(element, aChangedBits)) {
-    Servo_NoteExplicitHints(&element, nsRestyleHint(0), changeHint);
+    Servo_NoteExplicitHints(&element, RestyleHint{0}, changeHint);
   }
 
   // Don't bother taking a snapshot if no rules depend on these state bits.
   //
   // We always take a snapshot for the LTR/RTL event states, since Servo doesn't
   // track those bits in the same way, and we know that :dir() rules are always
   // present in UA style sheets.
   if (!aChangedBits.HasAtLeastOneOfStates(DIRECTION_STATES) &&
@@ -3371,26 +3323,27 @@ static inline bool AttributeChangeRequir
 }
 
 void RestyleManager::AttributeChanged(Element* aElement, int32_t aNameSpaceID,
                                       nsAtom* aAttribute, int32_t aModType,
                                       const nsAttrValue* aOldValue) {
   MOZ_ASSERT(!mInStyleRefresh);
 
   auto changeHint = nsChangeHint(0);
-  auto restyleHint = nsRestyleHint(0);
+  auto restyleHint = RestyleHint{0};
 
   changeHint |= aElement->GetAttributeChangeHint(aAttribute, aModType);
 
   if (aAttribute == nsGkAtoms::style) {
-    restyleHint |= eRestyle_StyleAttribute;
+    restyleHint |= StyleRestyleHint_RESTYLE_STYLE_ATTRIBUTE;
   } else if (AttributeChangeRequiresSubtreeRestyle(*aElement, aAttribute)) {
-    restyleHint |= eRestyle_Subtree;
+    restyleHint |= RestyleHint::RestyleSubtree();
   } else if (aElement->IsAttributeMapped(aAttribute)) {
-    restyleHint |= eRestyle_Self;
+    // FIXME(emilio): Does this really need to re-selector-match?
+    restyleHint |= StyleRestyleHint_RESTYLE_SELF;
   }
 
   if (nsIFrame* primaryFrame = aElement->GetPrimaryFrame()) {
     // See if we have appearance information for a theme.
     const nsStyleDisplay* disp = primaryFrame->StyleDisplay();
     if (disp->HasAppearance()) {
       nsITheme* theme = PresContext()->GetTheme();
       if (theme && theme->ThemeSupportsWidget(PresContext(), primaryFrame,
--- a/layout/base/RestyleManager.h
+++ b/layout/base/RestyleManager.h
@@ -200,18 +200,16 @@ class RestyleManager {
   void Disconnect() { mPresContext = nullptr; }
 
   ~RestyleManager() {
     MOZ_ASSERT(!mAnimationsWithDestroyedFrame,
                "leaving dangling pointers from AnimationsWithDestroyedFrame");
     MOZ_ASSERT(!mReentrantChanges);
   }
 
-  static nsCString RestyleHintToString(nsRestyleHint aHint);
-
 #ifdef DEBUG
   static nsCString ChangeHintToString(nsChangeHint aHint);
 
   /**
    * DEBUG ONLY method to verify integrity of style tree versus frame tree
    */
   void DebugVerifyStyleTree(nsIFrame* aFrame);
 #endif
@@ -310,36 +308,35 @@ class RestyleManager {
   // Restyling for a ContentInserted (notification after insertion) or
   // for some CharacterDataChanged.
   void RestyleForInsertOrChange(nsIContent* aChild);
 
   // Restyle for a CharacterDataChanged notification. In practice this can only
   // affect :empty / :-moz-only-whitespace / :-moz-first-node / :-moz-last-node.
   void CharacterDataChanged(nsIContent*, const CharacterDataChangeInfo&);
 
-  void PostRestyleEvent(dom::Element*, nsRestyleHint,
+  void PostRestyleEvent(dom::Element*, RestyleHint,
                         nsChangeHint aMinChangeHint);
 
   /**
    * Posts restyle hints for animations.
    * This is only called for the second traversal for CSS animations during
    * updating CSS animations in a SequentialTask.
    * This function does neither register a refresh observer nor flag that a
    * style flush is needed since this function is supposed to be called during
    * restyling process and this restyle event will be processed in the second
    * traversal of the same restyling process.
    */
   void PostRestyleEventForAnimations(dom::Element*, PseudoStyleType,
-                                     nsRestyleHint);
+                                     RestyleHint);
 
   void NextRestyleIsForCSSRuleChanges() { mRestyleForCSSRuleChanges = true; }
 
-  void RebuildAllStyleData(nsChangeHint aExtraHint, nsRestyleHint aRestyleHint);
-  void PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
-                                    nsRestyleHint aRestyleHint);
+  void RebuildAllStyleData(nsChangeHint aExtraHint, RestyleHint);
+  void PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint, RestyleHint);
 
   void ProcessPendingRestyles();
   void ProcessAllPendingAttributeAndStateInvalidations();
 
   void ContentStateChanged(nsIContent* aContent, EventStates aStateMask);
   void AttributeWillChange(Element* aElement, int32_t aNameSpaceID,
                            nsAtom* aAttribute, int32_t aModType);
   void ClassAttributeWillBeChangedBySMIL(dom::Element* aElement);
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -3392,18 +3392,17 @@ nsCSSFrameConstructor::FindHTMLData(cons
       SIMPLE_TAG_CHAIN(canvas, nsCSSFrameConstructor::FindCanvasData),
       SIMPLE_TAG_CREATE(video, NS_NewHTMLVideoFrame),
       SIMPLE_TAG_CREATE(audio, NS_NewHTMLVideoFrame),
       SIMPLE_TAG_CREATE(progress, NS_NewProgressFrame),
       SIMPLE_TAG_CREATE(meter, NS_NewMeterFrame),
       COMPLEX_TAG_CREATE(details,
                          &nsCSSFrameConstructor::ConstructDetailsFrame)};
 
-  return FindDataByTag(aElement, aStyle, sHTMLData,
-                       ArrayLength(sHTMLData));
+  return FindDataByTag(aElement, aStyle, sHTMLData, ArrayLength(sHTMLData));
 }
 
 /* static */
 const nsCSSFrameConstructor::FrameConstructionData*
 nsCSSFrameConstructor::FindGeneratedImageData(const Element& aElement,
                                               ComputedStyle&) {
   if (!aElement.IsInNativeAnonymousSubtree()) {
     return nullptr;
@@ -4021,18 +4020,18 @@ const nsCSSFrameConstructor::FrameConstr
         SIMPLE_XUL_FCDATA(NS_NewTextBoxFrame);
 
 /* static */
 const nsCSSFrameConstructor::FrameConstructionData*
 nsCSSFrameConstructor::FindXULButtonData(const Element& aElement,
                                          ComputedStyle&) {
   static const FrameConstructionData sXULMenuData =
       SIMPLE_XUL_FCDATA(NS_NewMenuFrame);
-  if (aElement.AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
-                           nsGkAtoms::menu, eCaseMatters)) {
+  if (aElement.AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, nsGkAtoms::menu,
+                           eCaseMatters)) {
     return &sXULMenuData;
   }
 
 #  ifdef MOZ_THUNDERBIRD
   if (aElement.AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
                            NS_LITERAL_STRING("menu-button"), eCaseMatters)) {
     return &sXULMenuData;
   }
@@ -5292,25 +5291,23 @@ nsCSSFrameConstructor::FindElementTagDat
     default:
       return nullptr;
   }
 }
 
 nsCSSFrameConstructor::XBLBindingLoadInfo::XBLBindingLoadInfo(
     already_AddRefed<ComputedStyle>&& aStyle,
     UniquePtr<PendingBinding> aPendingBinding)
-    : mStyle(std::move(aStyle)),
-      mPendingBinding(std::move(aPendingBinding)) {
+    : mStyle(std::move(aStyle)), mPendingBinding(std::move(aPendingBinding)) {
   MOZ_ASSERT(mStyle);
 }
 
 nsCSSFrameConstructor::XBLBindingLoadInfo::XBLBindingLoadInfo(
     nsIContent& aContent, ComputedStyle& aStyle)
-    : mStyle(&aStyle),
-      mPendingBinding(nullptr) {}
+    : mStyle(&aStyle), mPendingBinding(nullptr) {}
 
 nsCSSFrameConstructor::XBLBindingLoadInfo::XBLBindingLoadInfo() = default;
 
 nsCSSFrameConstructor::XBLBindingLoadInfo
 nsCSSFrameConstructor::LoadXBLBindingIfNeeded(nsIContent& aContent,
                                               ComputedStyle& aStyle,
                                               uint32_t aFlags) {
   if (!(aFlags & ITEM_ALLOW_XBL_BASE)) {
@@ -7762,17 +7759,17 @@ bool nsCSSFrameConstructor::EnsureFrameF
   // pages that repeatedly query metrics for collapsed-whitespace text nodes
   // don't trigger pathological behavior.
   mAlwaysCreateFramesForIgnorableWhitespace = true;
   Element* root = mDocument->GetRootElement();
   if (!root) {
     return false;
   }
 
-  RestyleManager()->PostRestyleEvent(root, nsRestyleHint(0),
+  RestyleManager()->PostRestyleEvent(root, RestyleHint{0},
                                      nsChangeHint_ReconstructFrame);
   return true;
 }
 
 void nsCSSFrameConstructor::CharacterDataChanged(
     nsIContent* aContent, const CharacterDataChangeInfo& aInfo) {
   AUTO_PROFILER_LABEL("nsCSSFrameConstructor::CharacterDataChanged",
                       LAYOUT_FrameConstruction);
@@ -8680,18 +8677,17 @@ void nsCSSFrameConstructor::RecreateFram
     if (aInsertionKind == InsertionKind::Async && aContent->IsElement()) {
       // FIXME(emilio, bug 1397239): There's nothing removing the frame state
       // for elements that go away before we come back to the frame
       // constructor.
       //
       // Also, it'd be nice to just use the `ContentRangeInserted` path for
       // both elements and non-elements, but we need to make lazy frame
       // construction to apply to all elements first.
-      RestyleManager()->PostRestyleEvent(aContent->AsElement(),
-                                         nsRestyleHint(0),
+      RestyleManager()->PostRestyleEvent(aContent->AsElement(), RestyleHint{0},
                                          nsChangeHint_ReconstructFrame);
     } else {
       // Now, recreate the frames associated with this content object. If
       // ContentRemoved triggered reconstruction, then we don't need to do this
       // because the frames will already have been built.
       ContentRangeInserted(aContent, aContent->GetNextSibling(),
                            mTempFrameTreeState, aInsertionKind);
     }
--- a/layout/base/nsChangeHint.h
+++ b/layout/base/nsChangeHint.h
@@ -7,16 +7,17 @@
 /* constants for what needs to be recomputed in response to style changes */
 
 #ifndef nsChangeHint_h___
 #define nsChangeHint_h___
 
 #include "mozilla/Types.h"
 #include "nsDebug.h"
 #include "nsTArray.h"
+#include "mozilla/ServoStyleConsts.h"
 
 // Defines for various style related constants
 
 enum nsChangeHint : uint32_t {
   nsChangeHint_Empty = 0,
 
   // change was visual only (e.g., COLOR=)
   // Invalidates all descendant frames (including following
@@ -513,132 +514,30 @@ inline nsChangeHint NS_RemoveSubsumedHin
     MOZ_ASSERT(result & nsChangeHint_ClearAncestorIntrinsics);
     result |=  // nsChangeHint_ClearAncestorIntrinsics |
         nsChangeHint_NeedDirtyReflow;
   }
 
   return result;
 }
 
-/**
- * |nsRestyleHint| is a bitfield for the result of
- * |HasStateDependentStyle| and |HasAttributeDependentStyle|.  When no
- * restyling is necessary, use |nsRestyleHint(0)|.
- *
- * Without eRestyle_Force or eRestyle_ForceDescendants, the restyling process
- * can stop processing at a frame when it detects no style changes and it is
- * known that the styles of the subtree beneath it will not change, leaving
- * the old ComputedStyle on the frame.  eRestyle_Force can be used to skip this
- * optimization on a frame, and to force its new ComputedStyle to be used.
- *
- * Similarly, eRestyle_ForceDescendants will cause the frame and all of its
- * descendants to be traversed and for the new ComputedStyles that are created
- * to be set on the frames.
- *
- * NOTE: When adding new restyle hints, please also add them to
- * RestyleManager::RestyleHintToString.
- */
-enum nsRestyleHint : uint32_t {
-  // Rerun selector matching on the element.  If a new ComputedStyle
-  // results, update the ComputedStyles of descendants.  (Irrelevant if
-  // eRestyle_Subtree is also set, since that implies a superset of the
-  // work.)
-  eRestyle_Self = 1 << 0,
-
-  // Rerun selector matching on descendants of the element that match
-  // a given selector.
-  eRestyle_SomeDescendants = 1 << 1,
-
-  // Rerun selector matching on the element and all of its descendants.
-  // (Implies eRestyle_ForceDescendants, which ensures that we continue
-  // the restyling process for all descendants, but doesn't cause
-  // selector matching.)
-  eRestyle_Subtree = 1 << 2,
-
-  // Rerun selector matching on all later siblings of the element and
-  // all of their descendants.
-  eRestyle_LaterSiblings = 1 << 3,
-
-  // Replace the style data coming from CSS transitions without updating
-  // any other style data.  If a new ComputedStyle results, update style
-  // contexts on the descendants.  (Irrelevant if eRestyle_Self or
-  // eRestyle_Subtree is also set, since those imply a superset of the
-  // work.)
-  eRestyle_CSSTransitions = 1 << 4,
+namespace mozilla {
 
-  // Replace the style data coming from CSS animations without updating
-  // any other style data.  If a new ComputedStyle results, update style
-  // contexts on the descendants.  (Irrelevant if eRestyle_Self or
-  // eRestyle_Subtree is also set, since those imply a superset of the
-  // work.)
-  eRestyle_CSSAnimations = 1 << 5,
-
-  // Replace the style data coming from inline style without updating
-  // any other style data.  If a new ComputedStyle results, update style
-  // contexts on the descendants.  (Irrelevant if eRestyle_Self or
-  // eRestyle_Subtree is also set, since those imply a superset of the
-  // work.)  Supported only for element ComputedStyles and not for
-  // pseudo-elements or anonymous boxes, on which it converts to
-  // eRestyle_Self.
-  // If the change is for the advance of a declarative animation, use
-  // the value below instead.
-  eRestyle_StyleAttribute = 1 << 6,
-
-  // Same as eRestyle_StyleAttribute, but for when the change results
-  // from the advance of a declarative animation.
-  eRestyle_StyleAttribute_Animations = 1 << 7,
+using RestyleHint = StyleRestyleHint;
 
-  // Continue the restyling process to the current frame's children even
-  // if this frame's restyling resulted in no style changes.
-  eRestyle_Force = 1 << 8,
-
-  // Continue the restyling process to all of the current frame's
-  // descendants, even if any frame's restyling resulted in no style
-  // changes.  (Implies eRestyle_Force.)  Note that this is weaker than
-  // eRestyle_Subtree, which makes us rerun selector matching on all
-  // descendants rather than just continuing the restyling process.
-  eRestyle_ForceDescendants = 1 << 9,
-
-  // Useful unions:
-  eRestyle_AllHintsWithAnimations = eRestyle_CSSTransitions |
-                                    eRestyle_CSSAnimations |
-                                    eRestyle_StyleAttribute_Animations,
-};
-
-// The functions below need an integral type to cast to to avoid
-// infinite recursion.
-typedef decltype(nsRestyleHint(0) + nsRestyleHint(0)) nsRestyleHint_size_t;
-
-inline constexpr nsRestyleHint operator|(nsRestyleHint aLeft,
-                                         nsRestyleHint aRight) {
-  return nsRestyleHint(nsRestyleHint_size_t(aLeft) |
-                       nsRestyleHint_size_t(aRight));
+inline RestyleHint RestyleHint::RestyleSubtree() {
+  return StyleRestyleHint_RESTYLE_SELF | StyleRestyleHint_RESTYLE_DESCENDANTS;
 }
 
-inline constexpr nsRestyleHint operator&(nsRestyleHint aLeft,
-                                         nsRestyleHint aRight) {
-  return nsRestyleHint(nsRestyleHint_size_t(aLeft) &
-                       nsRestyleHint_size_t(aRight));
-}
-
-inline nsRestyleHint& operator|=(nsRestyleHint& aLeft, nsRestyleHint aRight) {
-  return aLeft = aLeft | aRight;
-}
-
-inline nsRestyleHint& operator&=(nsRestyleHint& aLeft, nsRestyleHint aRight) {
-  return aLeft = aLeft & aRight;
+inline RestyleHint RestyleHint::RecascadeSubtree() {
+  return StyleRestyleHint_RECASCADE_SELF |
+         StyleRestyleHint_RECASCADE_DESCENDANTS;
 }
 
-inline constexpr nsRestyleHint operator~(nsRestyleHint aArg) {
-  return nsRestyleHint(~nsRestyleHint_size_t(aArg));
+inline RestyleHint RestyleHint::ForAnimations() {
+  return StyleRestyleHint_RESTYLE_CSS_TRANSITIONS |
+         StyleRestyleHint_RESTYLE_CSS_ANIMATIONS |
+         StyleRestyleHint_RESTYLE_SMIL;
 }
 
-inline constexpr nsRestyleHint operator^(nsRestyleHint aLeft,
-                                         nsRestyleHint aRight) {
-  return nsRestyleHint(nsRestyleHint_size_t(aLeft) ^
-                       nsRestyleHint_size_t(aRight));
-}
-
-inline nsRestyleHint operator^=(nsRestyleHint& aLeft, nsRestyleHint aRight) {
-  return aLeft = aLeft ^ aRight;
-}
+}  // namespace mozilla
 
 #endif /* nsChangeHint_h___ */
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -526,19 +526,18 @@ class nsIPresShell : public nsStubDocume
    *
    * Only called when the slot attribute of the element changes, the rest of
    * the changes should be handled in ShadowRoot.
    */
   void SlotAssignmentWillChange(mozilla::dom::Element& aElement,
                                 mozilla::dom::HTMLSlotElement* aOldSlot,
                                 mozilla::dom::HTMLSlotElement* aNewSlot);
 
-  void PostRecreateFramesFor(mozilla::dom::Element* aElement);
-  void RestyleForAnimation(mozilla::dom::Element* aElement,
-                           nsRestyleHint aHint);
+  void PostRecreateFramesFor(mozilla::dom::Element*);
+  void RestyleForAnimation(mozilla::dom::Element*, mozilla::RestyleHint);
 
   // ShadowRoot has APIs that can change styles. This notifies the shell that
   // stlyes applicable in the shadow tree have potentially changed.
   void RecordShadowStyleChange(mozilla::dom::ShadowRoot& aShadowRoot);
 
   /**
    * Determine if it is safe to flush all pending notifications.
    */
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -7929,22 +7929,20 @@ void nsLayoutUtils::DeregisterImageReque
         *aRequestRegistered = false;
       }
     }
   }
 }
 
 /* static */
 void nsLayoutUtils::PostRestyleEvent(Element* aElement,
-                                     nsRestyleHint aRestyleHint,
+                                     RestyleHint aRestyleHint,
                                      nsChangeHint aMinChangeHint) {
-  Document* doc = aElement->GetComposedDoc();
-  if (doc) {
-    RefPtr<nsPresContext> presContext = doc->GetPresContext();
-    if (presContext) {
+  if (Document* doc = aElement->GetComposedDoc()) {
+    if (nsPresContext* presContext = doc->GetPresContext()) {
       presContext->RestyleManager()->PostRestyleEvent(aElement, aRestyleHint,
                                                       aMinChangeHint);
     }
   }
 }
 
 nsSetAttrRunnable::nsSetAttrRunnable(Element* aElement, nsAtom* aAttrName,
                                      const nsAString& aValue)
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -2560,18 +2560,17 @@ class nsLayoutUtils {
                                      imgIRequest* aRequest,
                                      bool* aRequestRegistered);
 
   /**
    * Shim to nsCSSFrameConstructor::PostRestyleEvent. Exists so that we
    * can avoid including nsCSSFrameConstructor.h and all its dependencies
    * in content files.
    */
-  static void PostRestyleEvent(mozilla::dom::Element* aElement,
-                               nsRestyleHint aRestyleHint,
+  static void PostRestyleEvent(mozilla::dom::Element*, mozilla::RestyleHint,
                                nsChangeHint aMinChangeHint);
 
   /**
    * Updates a pair of x and y distances if a given point is closer to a given
    * rectangle than the original distance values.  If aPoint is closer to
    * aRect than aClosestXDistance and aClosestYDistance indicate, then those
    * two variables are updated with the distance between aPoint and aRect,
    * and true is returned.  If aPoint is not closer, then aClosestXDistance
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -409,17 +409,18 @@ void nsPresContext::InvalidatePaintedLay
 
 void nsPresContext::AppUnitsPerDevPixelChanged() {
   InvalidatePaintedLayers();
 
   if (mDeviceContext) {
     mDeviceContext->FlushFontCache();
   }
 
-  MediaFeatureValuesChanged({eRestyle_ForceDescendants, NS_STYLE_HINT_REFLOW,
+  MediaFeatureValuesChanged({RestyleHint::RecascadeSubtree(),
+                             NS_STYLE_HINT_REFLOW,
                              MediaFeatureChangeReason::ResolutionChange});
 
   mCurAppUnitsPerDevPixel = mDeviceContext->AppUnitsPerDevPixel();
 }
 
 void nsPresContext::PreferenceChanged(const char* aPrefName) {
   nsDependentCString prefName(aPrefName);
   if (prefName.EqualsLiteral("layout.css.dpi") ||
@@ -538,17 +539,17 @@ void nsPresContext::UpdateAfterPreferenc
   nsChangeHint hint = nsChangeHint(0);
 
   if (mPrefChangePendingNeedsReflow) {
     hint |= NS_STYLE_HINT_REFLOW;
   }
 
   // Preferences require rerunning selector matching because we rebuild
   // the pref style sheet for some preference changes.
-  RebuildAllStyleData(hint, eRestyle_Subtree);
+  RebuildAllStyleData(hint, RestyleHint::RestyleSubtree());
 }
 
 nsresult nsPresContext::Init(nsDeviceContext* aDeviceContext) {
   NS_ASSERTION(!mInitialized, "attempt to reinit pres context");
   NS_ENSURE_ARG(aDeviceContext);
 
   mDeviceContext = aDeviceContext;
 
@@ -724,17 +725,17 @@ void nsPresContext::DoChangeCharSet(NotN
   UpdateCharSet(aCharSet);
   mDeviceContext->FlushFontCache();
 
   // If a document contains one or more <script> elements, frame construction
   // might happen earlier than the UpdateCharSet(), so we need to restyle
   // descendants to make their style data up-to-date.
   //
   // FIXME(emilio): Revisit whether this is true after bug 1438911.
-  RebuildAllStyleData(NS_STYLE_HINT_REFLOW, eRestyle_ForceDescendants);
+  RebuildAllStyleData(NS_STYLE_HINT_REFLOW, RestyleHint::RecascadeSubtree());
 }
 
 void nsPresContext::UpdateCharSet(NotNull<const Encoding*> aCharSet) {
   switch (GET_BIDI_OPTION_TEXTTYPE(GetBidi())) {
     case IBMBIDI_TEXTTYPE_LOGICAL:
       SetVisualMode(false);
       break;
 
@@ -941,17 +942,18 @@ void nsPresContext::UpdateEffectiveTextZ
   } else if (newZoom > maxZoom) {
     newZoom = maxZoom;
   }
 
   mEffectiveTextZoom = newZoom;
 
   // Media queries could have changed, since we changed the meaning
   // of 'em' units in them.
-  MediaFeatureValuesChanged({eRestyle_ForceDescendants, NS_STYLE_HINT_REFLOW,
+  MediaFeatureValuesChanged({RestyleHint::RecascadeSubtree(),
+                             NS_STYLE_HINT_REFLOW,
                              MediaFeatureChangeReason::ZoomChange});
 }
 
 float nsPresContext::GetDeviceFullZoom() {
   return mDeviceContext->GetFullZoom();
 }
 
 void nsPresContext::SetFullZoom(float aZoom) {
@@ -1336,32 +1338,32 @@ void nsPresContext::SysColorChangedInter
   PreferenceSheet::Refresh();
 
   // Reset default background and foreground colors for the document since
   // they may be using system colors
   GetDocumentColorPreferences();
 
   // The system color values are computed to colors in the style data,
   // so normal style data comparison is sufficient here.
-  RebuildAllStyleData(nsChangeHint(0), nsRestyleHint(0));
+  RebuildAllStyleData(nsChangeHint(0), RestyleHint{0});
 }
 
 void nsPresContext::RefreshSystemMetrics() {
   // This will force the system metrics to be generated the next time they're
   // used.
   nsMediaFeatures::FreeSystemMetrics();
 
   // Changes to system metrics can change media queries on them.
   //
   // Changes in theme can change system colors (whose changes are
   // properly reflected in computed style data), system fonts (whose
   // changes are not), and -moz-appearance (whose changes likewise are
   // not), so we need to recascade for the first, and reflow for the rest.
   MediaFeatureValuesChangedAllDocuments({
-      eRestyle_ForceDescendants,
+      RestyleHint::RecascadeSubtree(),
       NS_STYLE_HINT_REFLOW,
       MediaFeatureChangeReason::SystemMetricsChange,
   });
 }
 
 void nsPresContext::UIResolutionChanged() {
   if (!mPendingUIResolutionChanged) {
     nsCOMPtr<nsIRunnable> ev =
@@ -1447,21 +1449,22 @@ void nsPresContext::StopEmulatingMedium(
   nsAtom* previousMedium = Medium();
   mIsEmulatingMedia = false;
   if (Medium() != previousMedium) {
     MediaFeatureValuesChanged({MediaFeatureChangeReason::MediumChange});
   }
 }
 
 void nsPresContext::ContentLanguageChanged() {
-  PostRebuildAllStyleDataEvent(nsChangeHint(0), eRestyle_ForceDescendants);
+  PostRebuildAllStyleDataEvent(nsChangeHint(0),
+                               RestyleHint::RecascadeSubtree());
 }
 
 void nsPresContext::RebuildAllStyleData(nsChangeHint aExtraHint,
-                                        nsRestyleHint aRestyleHint) {
+                                        RestyleHint aRestyleHint) {
   if (!mShell) {
     // We must have been torn down. Nothing to do here.
     return;
   }
 
   // FIXME(emilio): Why is it safe to reset mUsesRootEMUnits / mUsesEXChUnits
   // here if there's no restyle hint? That looks pretty bogus.
   mUsesRootEMUnits = false;
@@ -1476,17 +1479,17 @@ void nsPresContext::RebuildAllStyleData(
   mDocument->MarkUserFontSetDirty();
   MarkCounterStylesDirty();
   MarkFontFeatureValuesDirty();
 
   RestyleManager()->RebuildAllStyleData(aExtraHint, aRestyleHint);
 }
 
 void nsPresContext::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
-                                                 nsRestyleHint aRestyleHint) {
+                                                 RestyleHint aRestyleHint) {
   if (!mShell) {
     // We must have been torn down. Nothing to do here.
     return;
   }
   RestyleManager()->PostRebuildAllStyleDataEvent(aExtraHint, aRestyleHint);
 }
 
 static bool MediaFeatureValuesChangedAllDocumentsCallback(Document* aDocument,
@@ -1652,26 +1655,27 @@ void nsPresContext::UserFontSetUpdated(g
 
   // Note: this method is called without a font when rules in the userfont set
   // are updated, which may occur during reflow as a result of the lazy
   // initialization of the userfont set. It would be better to avoid a full
   // restyle but until this method is only called outside of reflow, schedule a
   // full restyle in these cases.
   if (!aUpdatedFont) {
     PostRebuildAllStyleDataEvent(NS_STYLE_HINT_REFLOW,
-                                 eRestyle_ForceDescendants);
+                                 RestyleHint::RecascadeSubtree());
     return;
   }
 
   // Special case - if either the 'ex' or 'ch' units are used, these
   // depend upon font metrics. Updating this information requires
   // rebuilding the rule tree from the top, avoiding the reuse of cached
   // data even when no style rules have changed.
   if (UsesExChUnits()) {
-    PostRebuildAllStyleDataEvent(nsChangeHint(0), eRestyle_ForceDescendants);
+    PostRebuildAllStyleDataEvent(nsChangeHint(0),
+                                 RestyleHint::RecascadeSubtree());
   }
 
   // Iterate over the frame tree looking for frames associated with the
   // downloadable font family in question. If a frame's nsStyleFont has
   // the name, check the font group associated with the metrics to see if
   // it contains that specific font (i.e. the one chosen within the family
   // given the weight, width, and slant from the nsStyleFont). If it does,
   // mark that frame dirty and skip inspecting its descendants.
@@ -1708,17 +1712,17 @@ void nsPresContext::FlushCounterStyles()
     // Still in its initial state, no need to clean.
     return;
   }
 
   if (mCounterStylesDirty) {
     bool changed = mCounterStyleManager->NotifyRuleChanged();
     if (changed) {
       PresShell()->NotifyCounterStylesAreDirty();
-      PostRebuildAllStyleDataEvent(NS_STYLE_HINT_REFLOW, nsRestyleHint(0));
+      PostRebuildAllStyleDataEvent(NS_STYLE_HINT_REFLOW, RestyleHint{0});
       RefreshDriver()->AddPostRefreshObserver(
           new CounterStyleCleaner(RefreshDriver(), mCounterStyleManager));
     }
     mCounterStylesDirty = false;
   }
 }
 
 void nsPresContext::MarkCounterStylesDirty() {
@@ -1737,17 +1741,17 @@ void nsPresContext::NotifyMissingFonts()
 }
 
 void nsPresContext::EnsureSafeToHandOutCSSRules() {
   if (!mShell->StyleSet()->EnsureUniqueInnerOnCSSSheets()) {
     // Nothing to do.
     return;
   }
 
-  RebuildAllStyleData(nsChangeHint(0), eRestyle_Subtree);
+  RebuildAllStyleData(nsChangeHint(0), RestyleHint::RestyleSubtree());
 }
 
 void nsPresContext::FireDOMPaintEvent(
     nsTArray<nsRect>* aList, TransactionId aTransactionId,
     mozilla::TimeStamp aTimeStamp /* = mozilla::TimeStamp() */) {
   nsPIDOMWindowInner* ourWindow = mDocument->GetInnerWindow();
   if (!ourWindow) return;
 
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -253,45 +253,28 @@ class nsPresContext : public nsISupports
 
   /**
    * Rebuilds all style data by throwing out the old rule tree and
    * building a new one, and additionally applying aExtraHint (which
    * must not contain nsChangeHint_ReconstructFrame) to the root frame.
    * For aRestyleHint, see RestyleManager::RebuildAllStyleData.
    * Also rebuild the user font set and counter style manager.
    */
-  void RebuildAllStyleData(nsChangeHint aExtraHint, nsRestyleHint aRestyleHint);
+  void RebuildAllStyleData(nsChangeHint aExtraHint, mozilla::RestyleHint);
   /**
    * Just like RebuildAllStyleData, except (1) asynchronous and (2) it
    * doesn't rebuild the user font set.
    */
   void PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
-                                    nsRestyleHint aRestyleHint);
+                                    mozilla::RestyleHint);
 
   void ContentLanguageChanged();
 
   /**
-   * Handle changes in the values of media features (used in media
-   * queries).
-   *
-   * There are three sensible values to use for aRestyleHint:
-   *  * nsRestyleHint(0) to rebuild style data, with rerunning of
-   *    selector matching, only if media features have changed
-   *  * eRestyle_ForceDescendants to force rebuilding of style data (but
-   *    still only rerun selector matching if media query results have
-   *    changed).  (RebuildAllStyleData always adds
-   *    eRestyle_ForceDescendants internally, so here we're only using
-   *    it to distinguish from nsRestyleHint(0) whether we need to call
-   *    RebuildAllStyleData at all.)
-   *  * eRestyle_Subtree to force rebuilding of style data with
-   *    rerunning of selector matching
-   *
-   * For aChangeHint, see RestyleManager::RebuildAllStyleData.  (Passing
-   * a nonzero aChangeHint forces rebuilding style data even if
-   * nsRestyleHint(0) is passed.)
+   * Handle changes in the values of media features (used in media queries).
    */
   void MediaFeatureValuesChanged(const mozilla::MediaFeatureChange& aChange) {
     if (mShell) {
       mShell->EnsureStyleFlush();
     }
 
     if (!mPendingMediaFeatureValuesChange) {
       mPendingMediaFeatureValuesChange.emplace(aChange);
--- a/layout/mathml/nsMathMLmtableFrame.cpp
+++ b/layout/mathml/nsMathMLmtableFrame.cpp
@@ -895,17 +895,18 @@ void nsMathMLmtableFrame::SetInitialChil
 
 void nsMathMLmtableFrame::RestyleTable() {
   // re-sync MathML specific style data that may have changed
   MapAllAttributesIntoCSS(this);
 
   // Explicitly request a re-resolve and reflow in our subtree to pick up any
   // changes
   PresContext()->RestyleManager()->PostRestyleEvent(
-      mContent->AsElement(), eRestyle_Subtree, nsChangeHint_AllReflowHints);
+      mContent->AsElement(), RestyleHint::RestyleSubtree(),
+      nsChangeHint_AllReflowHints);
 }
 
 nscoord nsMathMLmtableFrame::GetColSpacing(int32_t aColIndex) {
   if (mUseCSSSpacing) {
     return nsTableFrame::GetColSpacing(aColIndex);
   }
   if (!mColSpacing.Length()) {
     NS_ERROR("mColSpacing should not be empty");
--- a/layout/style/MediaFeatureChange.h
+++ b/layout/style/MediaFeatureChange.h
@@ -39,24 +39,24 @@ enum class MediaFeatureChangeReason {
   // display-mode changed on the document, thus the display-mode media queries
   // may have changed.
   DisplayModeChange = 1 << 7,
 };
 
 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(MediaFeatureChangeReason)
 
 struct MediaFeatureChange {
-  nsRestyleHint mRestyleHint;
+  RestyleHint mRestyleHint;
   nsChangeHint mChangeHint;
   MediaFeatureChangeReason mReason;
 
   MOZ_IMPLICIT MediaFeatureChange(MediaFeatureChangeReason aReason)
-      : MediaFeatureChange(nsRestyleHint(0), nsChangeHint(0), aReason) {}
+      : MediaFeatureChange(RestyleHint{0}, nsChangeHint(0), aReason) {}
 
-  MediaFeatureChange(nsRestyleHint aRestyleHint, nsChangeHint aChangeHint,
+  MediaFeatureChange(RestyleHint aRestyleHint, nsChangeHint aChangeHint,
                      MediaFeatureChangeReason aReason)
       : mRestyleHint(aRestyleHint),
         mChangeHint(aChangeHint),
         mReason(aReason) {}
 
   inline MediaFeatureChange& operator|=(const MediaFeatureChange& aOther) {
     mRestyleHint |= aOther.mRestyleHint;
     mChangeHint |= aOther.mChangeHint;
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -805,19 +805,18 @@ void Servo_Initialize(RawGeckoURLExtraDa
 
 // Initialize Servo on a cooperative Quantum DOM thread.
 void Servo_InitializeCooperativeThread();
 
 // Shut down Servo components. Should be called exactly once at shutdown.
 void Servo_Shutdown();
 
 // Restyle and change hints.
-void Servo_NoteExplicitHints(RawGeckoElementBorrowed element,
-                             nsRestyleHint restyle_hint,
-                             nsChangeHint change_hint);
+void Servo_NoteExplicitHints(RawGeckoElementBorrowed, mozilla::RestyleHint,
+                             nsChangeHint);
 
 // 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 Servo_TakeChangeHint(RawGeckoElementBorrowed element,
                               bool* was_restyled);
 
 ComputedStyleStrong Servo_ResolveStyle(RawGeckoElementBorrowed element,
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -56,17 +56,16 @@ hide-types = [
     "nsINode_ErrorResult",
     "nsDOMAttributeMap_ErrorResult",
     # End of JS::Value related types
     # https://github.com/rust-lang/rust-bindgen/issues/1503
     "mozilla::StyleTimingFunction.*",
 ]
 bitfield-enums = [
     "nsChangeHint",
-    "nsRestyleHint",
     "mozilla::OriginFlags",
 ]
 rusty-enums = [
     "nsCompatibility",
     "mozilla::EffectCompositor_CascadeLevel",
     "mozilla::SheetType",
     "mozilla::dom::CallerType",
     "mozilla::dom::IterationCompositeOperation",
@@ -255,17 +254,16 @@ whitelist-types = [
     "nsCursorImage",
     "nsFont",
     "nsAtom",
     "nsDynamicAtom",
     "nsMargin",
     "nsMediaFeature",
     "nsMediaFeatures",
     "nsRect",
-    "nsRestyleHint",
     "nsresult",
     "nsSimpleContentList",
     "nsSize",
     "nsStyleBackground",
     "nsStyleBorder",
     "nsStyleColor",
     "nsStyleColumn",
     "nsStyleContent",
@@ -445,16 +443,17 @@ cbindgen-types = [
     { gecko = "StyleGenericPerspective", servo = "values::generics::box_::Perspective" },
     { gecko = "StyleZIndex", servo = "values::computed::ZIndex" },
     { gecko = "StyleGenericZIndex", servo = "values::generics::position::ZIndex" },
     { gecko = "StyleTransformOrigin", servo = "values::computed::TransformOrigin" },
     { gecko = "StyleGenericBorderRadius", servo = "values::generics::border::BorderRadius" },
     { gecko = "StyleLetterSpacing", servo = "values::computed::text::LetterSpacing" },
     { gecko = "StyleGenericLineHeight", servo = "values::generics::text::LineHeight" },
     { gecko = "StyleContain", servo = "values::computed::Contain" },
+    { gecko = "StyleRestyleHint", servo = "invalidation::element::restyle_hints::RestyleHint" },
 ]
 
 mapped-generic-types = [
     { generic = true, gecko = "mozilla::RustCell", servo = "::std::cell::Cell" },
     { generic = false, gecko = "ServoNodeData", servo = "AtomicRefCell<ElementData>" },
     { generic = false, gecko = "mozilla::ServoWritingMode", servo = "::logical_geometry::WritingMode" },
     { generic = false, gecko = "mozilla::ServoCustomPropertiesMap", servo = "Option<::servo_arc::Arc<::custom_properties::CustomPropertiesMap>>" },
     { generic = false, gecko = "mozilla::ServoRuleNode", servo = "Option<::rule_tree::StrongRuleNode>" },
@@ -514,16 +513,17 @@ structs-types = [
     "mozilla::StyleIntersectionObserverRootMargin",
     "mozilla::StyleComputedFontStretchRange",
     "mozilla::StyleComputedFontStyleDescriptor",
     "mozilla::StyleComputedFontWeightRange",
     "mozilla::StyleFontDisplay",
     "mozilla::StyleUnicodeRange",
     "mozilla::StyleFontLanguageOverride",
     "mozilla::StyleFontFaceSourceListComponent",
+    "mozilla::RestyleHint",
     "gfxFontFeature",
     "mozilla::gfx::FontVariation",
     "mozilla::DeclarationBlockMutationClosure",
     "nsAttrValue",
     "nsIContent",
     "nsINode",
     "Document",
     "Document_DocumentTheme",
@@ -587,17 +587,16 @@ structs-types = [
     "nsCSSValue",
     "nsCSSValueSharedList",
     "nsChangeHint",
     "nsCursorImage",
     "nsFont",
     "nsAtom",
     "nsIURI",
     "nsCompatibility",
-    "nsRestyleHint",
     "nsStyleBackground",
     "nsStyleBorder",
     "nsStyleColor",
     "nsStyleColumn",
     "nsStyleContent",
     "nsStyleContentData",
     "nsStyleContentData_CounterFunction",
     "mozilla::StyleContentType",
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -174,18 +174,18 @@ void ServoStyleSet::Shutdown() {
 
 void ServoStyleSet::RecordShadowStyleChange(ShadowRoot& aShadowRoot) {
   // TODO(emilio): We could keep track of the actual shadow roots that need
   // their styles recomputed.
   SetStylistXBLStyleSheetsDirty();
 
   // FIXME(emilio): This should be done using stylesheet invalidation instead.
   if (nsPresContext* pc = GetPresContext()) {
-    pc->RestyleManager()->PostRestyleEvent(aShadowRoot.Host(), eRestyle_Subtree,
-                                           nsChangeHint(0));
+    pc->RestyleManager()->PostRestyleEvent(
+        aShadowRoot.Host(), RestyleHint::RestyleSubtree(), nsChangeHint(0));
   }
 }
 
 void ServoStyleSet::InvalidateStyleForDocumentStateChanges(
     EventStates aStatesChanged) {
   MOZ_ASSERT(mDocument);
   MOZ_ASSERT(!aStatesChanged.IsEmpty());
 
@@ -227,17 +227,17 @@ static const MediaFeatureChangeReason kM
     // Zoom changes change the meaning of em units.
     MediaFeatureChangeReason::ZoomChange |
     // A resolution change changes the app-units-per-dev-pixels ratio, which
     // some structs (Border, Outline, Column) store for clamping. We should
     // arguably not do that, maybe doing it on layout directly, to try to avoid
     // relying on the pres context (bug 1418159).
     MediaFeatureChangeReason::ResolutionChange;
 
-nsRestyleHint ServoStyleSet::MediumFeaturesChanged(
+RestyleHint ServoStyleSet::MediumFeaturesChanged(
     MediaFeatureChangeReason aReason) {
   AutoTArray<RawServoAuthorStylesBorrowedMut, 20> nonDocumentStyles;
 
   EnumerateShadowRoots(*mDocument, [&](ShadowRoot& aShadowRoot) {
     if (auto* authorStyles = aShadowRoot.GetServoStyles()) {
       nonDocumentStyles.AppendElement(authorStyles);
     }
   });
@@ -265,26 +265,26 @@ nsRestyleHint ServoStyleSet::MediumFeatu
     SetStylistStyleSheetsDirty();
   }
 
   if (result.mAffectsNonDocumentRules) {
     SetStylistXBLStyleSheetsDirty();
   }
 
   if (rulesChanged) {
-    return eRestyle_Subtree;
+    return RestyleHint::RestyleSubtree();
   }
 
   const bool viewportChanged =
       bool(aReason & MediaFeatureChangeReason::ViewportChange);
   if (result.mUsesViewportUnits && viewportChanged) {
-    return eRestyle_ForceDescendants;
+    return RestyleHint::RecascadeSubtree();
   }
 
-  return nsRestyleHint(0);
+  return RestyleHint{0};
 }
 
 MOZ_DEFINE_MALLOC_SIZE_OF(ServoStyleSetMallocSizeOf)
 MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoStyleSetMallocEnclosingSizeOf)
 
 void ServoStyleSet::AddSizeOfIncludingThis(nsWindowSizes& aSizes) const {
   MallocSizeOf mallocSizeOf = aSizes.mState.mMallocSizeOf;
 
@@ -330,18 +330,18 @@ void ServoStyleSet::AddSizeOfIncludingTh
 void ServoStyleSet::SetAuthorStyleDisabled(bool aStyleDisabled) {
   if (mAuthorStyleDisabled == aStyleDisabled) {
     return;
   }
 
   mAuthorStyleDisabled = aStyleDisabled;
   if (Element* root = mDocument->GetRootElement()) {
     if (nsPresContext* pc = GetPresContext()) {
-      pc->RestyleManager()->PostRestyleEvent(root, eRestyle_Subtree,
-                                             nsChangeHint(0));
+      pc->RestyleManager()->PostRestyleEvent(
+          root, RestyleHint::RestyleSubtree(), nsChangeHint(0));
     }
   }
   Servo_StyleSet_SetAuthorStyleDisabled(mRawSet.get(), mAuthorStyleDisabled);
   // XXX Workaround for the assertion in InvalidateStyleForDocumentStateChanges
   // which is called by nsIPresShell::SetAuthorStyleDisabled via nsIPresShell::
   // RestyleForCSSRuleChanges. It is not really necessary because we don't need
   // to rebuild stylist for this change. But we have bug around this, and we
   // may want to rethink how things should work. See bug 1437785.
--- a/layout/style/ServoStyleSet.h
+++ b/layout/style/ServoStyleSet.h
@@ -111,17 +111,17 @@ class ServoStyleSet {
 
   // Runs style invalidation due to document state changes.
   void InvalidateStyleForDocumentStateChanges(EventStates aStatesChanged);
 
   void RecordShadowStyleChange(dom::ShadowRoot&);
 
   bool StyleSheetsHaveChanged() const { return StylistNeedsUpdate(); }
 
-  nsRestyleHint MediumFeaturesChanged(MediaFeatureChangeReason);
+  RestyleHint MediumFeaturesChanged(MediaFeatureChangeReason);
 
   // Evaluates a given SourceSizeList, returning the optimal viewport width in
   // app units.
   //
   // The SourceSizeList parameter can be null, in which case it will return
   // 100vw.
   inline nscoord EvaluateSourceSizeList(
       const RawServoSourceSizeList* aSourceSizeList) const;
--- a/layout/style/nsHTMLStyleSheet.cpp
+++ b/layout/style/nsHTMLStyleSheet.cpp
@@ -89,27 +89,25 @@ void nsHTMLStyleSheet::Reset() {
 }
 
 nsresult nsHTMLStyleSheet::ImplLinkColorSetter(
     RefPtr<RawServoDeclarationBlock> &aDecl, nscolor aColor) {
   if (!mDocument || !mDocument->GetShell()) {
     return NS_OK;
   }
 
-  RestyleManager *restyle = mDocument->GetPresContext()->RestyleManager();
-
   MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal());
   aDecl = Servo_DeclarationBlock_CreateEmpty().Consume();
   Servo_DeclarationBlock_SetColorValue(aDecl.get(), eCSSProperty_color, aColor);
 
   // Now make sure we restyle any links that might need it.  This
   // shouldn't happen often, so just rebuilding everything is ok.
-  Element *root = mDocument->GetRootElement();
-  if (root) {
-    restyle->PostRestyleEvent(root, eRestyle_Subtree, nsChangeHint(0));
+  if (Element *root = mDocument->GetRootElement()) {
+    RestyleManager *rm = mDocument->GetPresContext()->RestyleManager();
+    rm->PostRestyleEvent(root, RestyleHint::RestyleSubtree(), nsChangeHint(0));
   }
   return NS_OK;
 }
 
 nsresult nsHTMLStyleSheet::SetLinkColor(nscolor aColor) {
   return ImplLinkColorSetter(mServoUnvisitedLinkDecl, aColor);
 }
 
--- a/layout/svg/SVGGeometryFrame.cpp
+++ b/layout/svg/SVGGeometryFrame.cpp
@@ -157,17 +157,17 @@ nsresult SVGGeometryFrame::AttributeChan
   // We don't invalidate for transform changes (the layers code does that).
   // Also note that SVGTransformableElement::GetAttributeChangeHint will
   // return nsChangeHint_UpdateOverflow for "transform" attribute changes
   // and cause DoApplyRenderingChangeToTree to make the SchedulePaint call.
 
   if (aNameSpaceID == kNameSpaceID_None &&
       (static_cast<SVGGeometryElement*>(GetContent())
            ->AttributeDefinesGeometry(aAttribute))) {
-    nsLayoutUtils::PostRestyleEvent(mContent->AsElement(), nsRestyleHint(0),
+    nsLayoutUtils::PostRestyleEvent(mContent->AsElement(), RestyleHint{0},
                                     nsChangeHint_InvalidateRenderingObservers);
     nsSVGUtils::ScheduleReflowSVG(this);
   }
   return NS_OK;
 }
 
 /* virtual */
 void SVGGeometryFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
--- a/layout/svg/SVGObserverUtils.cpp
+++ b/layout/svg/SVGObserverUtils.cpp
@@ -317,17 +317,17 @@ void nsSVGRenderingObserverProperty::OnR
 
   if (frame && frame->HasAllStateBits(NS_FRAME_SVG_LAYOUT)) {
     // We need to notify anything that is observing the referencing frame or
     // any of its ancestors that the referencing frame has been invalidated.
     // Since walking the parent chain checking for observers is expensive we
     // do that using a change hint (multiple change hints of the same type are
     // coalesced).
     nsLayoutUtils::PostRestyleEvent(frame->GetContent()->AsElement(),
-                                    nsRestyleHint(0),
+                                    RestyleHint{0},
                                     nsChangeHint_InvalidateRenderingObservers);
   }
 }
 
 class SVGTextPathObserver final : public nsSVGRenderingObserverProperty {
  public:
   SVGTextPathObserver(URLAndReferrerInfo* aURI, nsIFrame* aFrame,
                       bool aReferenceImage)
@@ -378,17 +378,17 @@ void SVGTextPathObserver::OnRenderingCha
     return;
   }
   mValid = nowValid;
 
   // Repaint asynchronously in case the path frame is being torn down
   nsChangeHint changeHint =
       nsChangeHint(nsChangeHint_RepaintFrame | nsChangeHint_UpdateTextPath);
   frame->PresContext()->RestyleManager()->PostRestyleEvent(
-      frame->GetContent()->AsElement(), nsRestyleHint(0), changeHint);
+      frame->GetContent()->AsElement(), RestyleHint{0}, changeHint);
 }
 
 class SVGMarkerObserver final : public nsSVGRenderingObserverProperty {
  public:
   SVGMarkerObserver(URLAndReferrerInfo* aURI, nsIFrame* aFrame,
                     bool aReferenceImage)
       : nsSVGRenderingObserverProperty(aURI, aFrame, aReferenceImage) {}
 
@@ -409,17 +409,17 @@ void SVGMarkerObserver::OnRenderingChang
   // Don't need to request ReflowFrame if we're being reflowed.
   if (!(frame->GetStateBits() & NS_FRAME_IN_REFLOW)) {
     // XXXjwatt: We need to unify SVG into standard reflow so we can just use
     // nsChangeHint_NeedReflow | nsChangeHint_NeedDirtyReflow here.
     // XXXSDL KILL THIS!!!
     nsSVGUtils::ScheduleReflowSVG(frame);
   }
   frame->PresContext()->RestyleManager()->PostRestyleEvent(
-      frame->GetContent()->AsElement(), nsRestyleHint(0),
+      frame->GetContent()->AsElement(), RestyleHint{0},
       nsChangeHint_RepaintFrame);
 }
 
 class nsSVGPaintingProperty : public nsSVGRenderingObserverProperty {
  public:
   nsSVGPaintingProperty(URLAndReferrerInfo* aURI, nsIFrame* aFrame,
                         bool aReferenceImage)
       : nsSVGRenderingObserverProperty(aURI, aFrame, aReferenceImage) {}
@@ -670,17 +670,17 @@ void SVGFilterObserverListForCSSProp::On
     changeHint |= nsChangeHint_InvalidateRenderingObservers;
   }
 
   // Don't need to request UpdateOverflow if we're being reflowed.
   if (!(frame->GetStateBits() & NS_FRAME_IN_REFLOW)) {
     changeHint |= nsChangeHint_UpdateOverflow;
   }
   frame->PresContext()->RestyleManager()->PostRestyleEvent(
-      frame->GetContent()->AsElement(), nsRestyleHint(0), changeHint);
+      frame->GetContent()->AsElement(), RestyleHint{0}, changeHint);
 }
 
 class SVGFilterObserverListForCanvasContext final
     : public SVGFilterObserverList {
  public:
   SVGFilterObserverListForCanvasContext(CanvasRenderingContext2D* aContext,
                                         Element* aCanvasElement,
                                         nsTArray<nsStyleFilter>& aFilters)
--- a/layout/svg/SVGTextFrame.cpp
+++ b/layout/svg/SVGTextFrame.cpp
@@ -5021,17 +5021,17 @@ void SVGTextFrame::ScheduleReflowSVG() {
 }
 
 void SVGTextFrame::NotifyGlyphMetricsChange() {
   // TODO: perf - adding NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY is overly
   // aggressive here.  Ideally we would only set that bit when our descendant
   // frame tree changes (i.e. after frame construction).
   AddStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY |
                NS_STATE_SVG_POSITIONING_DIRTY);
-  nsLayoutUtils::PostRestyleEvent(mContent->AsElement(), nsRestyleHint(0),
+  nsLayoutUtils::PostRestyleEvent(mContent->AsElement(), RestyleHint{0},
                                   nsChangeHint_InvalidateRenderingObservers);
   ScheduleReflowSVG();
 }
 
 void SVGTextFrame::UpdateGlyphPositioning() {
   nsIFrame* kid = PrincipalChildList().FirstChild();
   if (!kid) {
     return;
--- a/layout/svg/nsSVGContainerFrame.cpp
+++ b/layout/svg/nsSVGContainerFrame.cpp
@@ -183,17 +183,17 @@ void nsSVGDisplayContainerFrame::RemoveF
                                              nsIFrame* aOldFrame) {
   SVGObserverUtils::InvalidateRenderingObservers(aOldFrame);
 
   // nsSVGContainerFrame::RemoveFrame doesn't call down into
   // nsContainerFrame::RemoveFrame, so it doesn't call FrameNeedsReflow. We
   // need to schedule a repaint and schedule an update to our overflow rects.
   SchedulePaint();
   PresContext()->RestyleManager()->PostRestyleEvent(
-      mContent->AsElement(), nsRestyleHint(0), nsChangeHint_UpdateOverflow);
+      mContent->AsElement(), RestyleHint{0}, nsChangeHint_UpdateOverflow);
 
   nsSVGContainerFrame::RemoveFrame(aListID, aOldFrame);
 }
 
 bool nsSVGDisplayContainerFrame::IsSVGTransformed(
     gfx::Matrix* aOwnTransform, gfx::Matrix* aFromParentTransform) const {
   bool foundTransform = false;
 
--- a/layout/svg/nsSVGForeignObjectFrame.cpp
+++ b/layout/svg/nsSVGForeignObjectFrame.cpp
@@ -77,38 +77,38 @@ void nsSVGForeignObjectFrame::DestroyFro
 }
 
 nsresult nsSVGForeignObjectFrame::AttributeChanged(int32_t aNameSpaceID,
                                                    nsAtom* aAttribute,
                                                    int32_t aModType) {
   if (aNameSpaceID == kNameSpaceID_None) {
     if (aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height) {
       nsLayoutUtils::PostRestyleEvent(
-          mContent->AsElement(), nsRestyleHint(0),
+          mContent->AsElement(), RestyleHint{0},
           nsChangeHint_InvalidateRenderingObservers);
       nsSVGUtils::ScheduleReflowSVG(this);
       // XXXjwatt: why mark intrinsic widths dirty? can't we just use eResize?
       RequestReflow(nsIPresShell::eStyleChange);
     } else if (aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y) {
       // make sure our cached transform matrix gets (lazily) updated
       mCanvasTM = nullptr;
       nsLayoutUtils::PostRestyleEvent(
-          mContent->AsElement(), nsRestyleHint(0),
+          mContent->AsElement(), RestyleHint{0},
           nsChangeHint_InvalidateRenderingObservers);
       nsSVGUtils::ScheduleReflowSVG(this);
     } else if (aAttribute == nsGkAtoms::transform) {
       // We don't invalidate for transform changes (the layers code does that).
       // Also note that SVGTransformableElement::GetAttributeChangeHint will
       // return nsChangeHint_UpdateOverflow for "transform" attribute changes
       // and cause DoApplyRenderingChangeToTree to make the SchedulePaint call.
       mCanvasTM = nullptr;
     } else if (aAttribute == nsGkAtoms::viewBox ||
                aAttribute == nsGkAtoms::preserveAspectRatio) {
       nsLayoutUtils::PostRestyleEvent(
-          mContent->AsElement(), nsRestyleHint(0),
+          mContent->AsElement(), RestyleHint{0},
           nsChangeHint_InvalidateRenderingObservers);
     }
   }
 
   return NS_OK;
 }
 
 void nsSVGForeignObjectFrame::Reflow(nsPresContext* aPresContext,
--- a/layout/svg/nsSVGImageFrame.cpp
+++ b/layout/svg/nsSVGImageFrame.cpp
@@ -115,17 +115,17 @@ void nsSVGImageFrame::DestroyFrom(nsIFra
 
 nsresult nsSVGImageFrame::AttributeChanged(int32_t aNameSpaceID,
                                            nsAtom* aAttribute,
                                            int32_t aModType) {
   if (aNameSpaceID == kNameSpaceID_None) {
     if (aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y ||
         aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height) {
       nsLayoutUtils::PostRestyleEvent(
-          mContent->AsElement(), nsRestyleHint(0),
+          mContent->AsElement(), RestyleHint{0},
           nsChangeHint_InvalidateRenderingObservers);
       nsSVGUtils::ScheduleReflowSVG(this);
       return NS_OK;
     } else if (aAttribute == nsGkAtoms::preserveAspectRatio) {
       // We don't paint the content of the image using display lists, therefore
       // we have to invalidate for this children-only transform changes since
       // there is no layer tree to notice that the transform changed and
       // recomposite.
@@ -508,39 +508,39 @@ nsSVGImageListener::Notify(imgIRequest* 
                            const nsIntRect* aData) {
   if (!mFrame) {
     return NS_ERROR_FAILURE;
   }
 
   if (aType == imgINotificationObserver::LOAD_COMPLETE) {
     mFrame->InvalidateFrame();
     nsLayoutUtils::PostRestyleEvent(mFrame->GetContent()->AsElement(),
-                                    nsRestyleHint(0),
+                                    RestyleHint{0},
                                     nsChangeHint_InvalidateRenderingObservers);
     nsSVGUtils::ScheduleReflowSVG(mFrame);
   }
 
   if (aType == imgINotificationObserver::FRAME_UPDATE) {
     // No new dimensions, so we don't need to call
     // nsSVGUtils::InvalidateAndScheduleBoundsUpdate.
     nsLayoutUtils::PostRestyleEvent(mFrame->GetContent()->AsElement(),
-                                    nsRestyleHint(0),
+                                    RestyleHint{0},
                                     nsChangeHint_InvalidateRenderingObservers);
     mFrame->InvalidateFrame();
   }
 
   if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
     // Called once the resource's dimensions have been obtained.
     nsCOMPtr<imgIContainer> image;
     aRequest->GetImage(getter_AddRefs(image));
     if (image) {
       image->SetAnimationMode(mFrame->PresContext()->ImageAnimationMode());
       mFrame->mImageContainer = image.forget();
     }
     mFrame->InvalidateFrame();
     nsLayoutUtils::PostRestyleEvent(mFrame->GetContent()->AsElement(),
-                                    nsRestyleHint(0),
+                                    RestyleHint{0},
                                     nsChangeHint_InvalidateRenderingObservers);
     nsSVGUtils::ScheduleReflowSVG(mFrame);
   }
 
   return NS_OK;
 }
--- a/layout/svg/nsSVGUseFrame.cpp
+++ b/layout/svg/nsSVGUseFrame.cpp
@@ -47,39 +47,39 @@ nsresult nsSVGUseFrame::AttributeChanged
   }
 
   return nsSVGGFrame::AttributeChanged(aNamespaceID, aAttribute, aModType);
 }
 
 void nsSVGUseFrame::PositionAttributeChanged() {
   // make sure our cached transform matrix gets (lazily) updated
   mCanvasTM = nullptr;
-  nsLayoutUtils::PostRestyleEvent(GetContent()->AsElement(), nsRestyleHint(0),
+  nsLayoutUtils::PostRestyleEvent(GetContent()->AsElement(), RestyleHint{0},
                                   nsChangeHint_InvalidateRenderingObservers);
   nsSVGUtils::ScheduleReflowSVG(this);
   nsSVGUtils::NotifyChildrenOfSVGChange(this, TRANSFORM_CHANGED);
 }
 
 void nsSVGUseFrame::DimensionAttributeChanged(bool aHadValidDimensions,
                                               bool aAttributeIsUsed) {
   bool invalidate = aAttributeIsUsed;
   if (mHasValidDimensions != aHadValidDimensions) {
     mHasValidDimensions = !mHasValidDimensions;
     invalidate = true;
   }
 
   if (invalidate) {
-    nsLayoutUtils::PostRestyleEvent(GetContent()->AsElement(), nsRestyleHint(0),
+    nsLayoutUtils::PostRestyleEvent(GetContent()->AsElement(), RestyleHint{0},
                                     nsChangeHint_InvalidateRenderingObservers);
     nsSVGUtils::ScheduleReflowSVG(this);
   }
 }
 
 void nsSVGUseFrame::HrefChanged() {
-  nsLayoutUtils::PostRestyleEvent(GetContent()->AsElement(), nsRestyleHint(0),
+  nsLayoutUtils::PostRestyleEvent(GetContent()->AsElement(), RestyleHint{0},
                                   nsChangeHint_InvalidateRenderingObservers);
   nsSVGUtils::ScheduleReflowSVG(this);
 }
 
 //----------------------------------------------------------------------
 // nsSVGDisplayableFrame methods
 
 void nsSVGUseFrame::ReflowSVG() {
--- a/layout/svg/nsSVGViewportFrame.cpp
+++ b/layout/svg/nsSVGViewportFrame.cpp
@@ -163,17 +163,17 @@ nsresult nsSVGViewportFrame::AttributeCh
                                               int32_t aModType) {
   if (aNameSpaceID == kNameSpaceID_None &&
       !(GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
     SVGViewportElement* content =
         static_cast<SVGViewportElement*>(GetContent());
 
     if (aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height) {
       nsLayoutUtils::PostRestyleEvent(
-          mContent->AsElement(), nsRestyleHint(0),
+          mContent->AsElement(), RestyleHint{0},
           nsChangeHint_InvalidateRenderingObservers);
       nsSVGUtils::ScheduleReflowSVG(this);
 
       if (content->HasViewBoxOrSyntheticViewBox()) {
         // make sure our cached transform matrix gets (lazily) updated
         mCanvasTM = nullptr;
         content->ChildrenOnlyTransformChanged();
         nsSVGUtils::NotifyChildrenOfSVGChange(this, TRANSFORM_CHANGED);
@@ -200,17 +200,17 @@ nsresult nsSVGViewportFrame::AttributeCh
 
       // We don't invalidate for transform changes (the layers code does that).
       // Also note that SVGTransformableElement::GetAttributeChangeHint will
       // return nsChangeHint_UpdateOverflow for "transform" attribute changes
       // and cause DoApplyRenderingChangeToTree to make the SchedulePaint call.
 
       if (aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y) {
         nsLayoutUtils::PostRestyleEvent(
-            mContent->AsElement(), nsRestyleHint(0),
+            mContent->AsElement(), RestyleHint{0},
             nsChangeHint_InvalidateRenderingObservers);
         nsSVGUtils::ScheduleReflowSVG(this);
       } else if (aAttribute == nsGkAtoms::viewBox ||
                  (aAttribute == nsGkAtoms::preserveAspectRatio &&
                   content->HasViewBoxOrSyntheticViewBox())) {
         content->ChildrenOnlyTransformChanged();
         // SchedulePaint sets a global state flag so we only need to call it
         // once (on ourself is fine), not once on each child (despite bug
--- a/layout/tables/nsTableCellFrame.cpp
+++ b/layout/tables/nsTableCellFrame.cpp
@@ -199,17 +199,17 @@ nsresult nsTableCellFrame::AttributeChan
   // BasicTableLayoutStrategy
   if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::nowrap &&
       PresContext()->CompatibilityMode() == eCompatibility_NavQuirks) {
     PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
                                   NS_FRAME_IS_DIRTY);
   }
 
   if (aAttribute == nsGkAtoms::rowspan || aAttribute == nsGkAtoms::colspan) {
-    nsLayoutUtils::PostRestyleEvent(mContent->AsElement(), nsRestyleHint(0),
+    nsLayoutUtils::PostRestyleEvent(mContent->AsElement(), RestyleHint{0},
                                     nsChangeHint_UpdateTableCellSpans);
   }
   return NS_OK;
 }
 
 /* virtual */
 void nsTableCellFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
   nsContainerFrame::DidSetComputedStyle(aOldComputedStyle);
--- a/servo/components/style/cbindgen.toml
+++ b/servo/components/style/cbindgen.toml
@@ -99,16 +99,17 @@ include = [
   "BorderSpacing",
   "BorderRadius",
   "NonNegativeLengthOrNumberRect",
   "Perspective",
   "ZIndex",
   "TransformOrigin",
   "WordBreak",
   "Contain",
+  "RestyleHint",
 ]
 item_types = ["enums", "structs", "typedefs"]
 
 [export.body]
 "CSSPixelLength" = """
   inline nscoord ToAppUnits() const;
   inline bool IsZero() const;
 """
@@ -192,8 +193,14 @@ item_types = ["enums", "structs", "typed
   inline const T& GetBStart(mozilla::WritingMode) const;
   inline const T& GetIEnd(mozilla::WritingMode) const;
   inline const T& GetBEnd(mozilla::WritingMode) const;
 """
 
 "GenericBorderRadius" = """
   inline const StyleLengthPercentage& Get(mozilla::HalfCorner) const;
 """
+
+"RestyleHint" = """
+  static inline StyleRestyleHint RestyleSubtree();
+  static inline StyleRestyleHint RecascadeSubtree();
+  static inline StyleRestyleHint ForAnimations();
+"""
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -39,30 +39,30 @@ use crate::gecko_bindings::bindings::Gec
 use crate::gecko_bindings::bindings::Gecko_IsSignificantChild;
 use crate::gecko_bindings::bindings::Gecko_MatchLang;
 use crate::gecko_bindings::bindings::Gecko_UnsetDirtyStyleAttr;
 use crate::gecko_bindings::bindings::Gecko_UpdateAnimations;
 use crate::gecko_bindings::bindings::{Gecko_ElementState, Gecko_GetDocumentLWTheme};
 use crate::gecko_bindings::bindings::{Gecko_SetNodeFlags, Gecko_UnsetNodeFlags};
 use crate::gecko_bindings::structs;
 use crate::gecko_bindings::structs::nsChangeHint;
-use crate::gecko_bindings::structs::nsRestyleHint;
 use crate::gecko_bindings::structs::Document_DocumentTheme as DocumentTheme;
 use crate::gecko_bindings::structs::EffectCompositor_CascadeLevel as CascadeLevel;
 use crate::gecko_bindings::structs::ELEMENT_HANDLED_SNAPSHOT;
 use crate::gecko_bindings::structs::ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO;
 use crate::gecko_bindings::structs::ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO;
 use crate::gecko_bindings::structs::ELEMENT_HAS_SNAPSHOT;
 use crate::gecko_bindings::structs::NODE_DESCENDANTS_NEED_FRAMES;
 use crate::gecko_bindings::structs::NODE_NEEDS_FRAME;
 use crate::gecko_bindings::structs::{nsAtom, nsIContent, nsINode_BooleanFlag};
 use crate::gecko_bindings::structs::{RawGeckoElement, RawGeckoNode, RawGeckoXBLBinding};
 use crate::gecko_bindings::sugar::ownership::{HasArcFFI, HasSimpleFFI};
 use crate::global_style_data::GLOBAL_STYLE_DATA;
 use crate::hash::FxHashMap;
+use crate::invalidation::element::restyle_hints::RestyleHint;
 use crate::logical_geometry::WritingMode;
 use crate::media_queries::Device;
 use crate::properties::animated_properties::{AnimationValue, AnimationValueMap};
 use crate::properties::style_structs::Font;
 use crate::properties::{ComputedValues, LonghandId};
 use crate::properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock};
 use crate::rule_tree::CascadeLevel as ServoCascadeLevel;
 use crate::selector_parser::{AttrValue, HorizontalDirection, Lang};
@@ -797,29 +797,27 @@ impl<'le> GeckoElement<'le> {
     /// element and its ancestors.
     ///
     /// This function is also called after display property changed for SMIL
     /// animation.
     ///
     /// Also this function schedules style flush.
     pub unsafe fn note_explicit_hints(
         &self,
-        restyle_hint: nsRestyleHint,
+        restyle_hint: RestyleHint,
         change_hint: nsChangeHint,
     ) {
         use crate::gecko::restyle_damage::GeckoRestyleDamage;
-        use crate::invalidation::element::restyle_hints::RestyleHint;
 
         let damage = GeckoRestyleDamage::new(change_hint);
         debug!(
             "note_explicit_hints: {:?}, restyle_hint={:?}, change_hint={:?}",
             self, restyle_hint, change_hint
         );
 
-        let restyle_hint: RestyleHint = restyle_hint.into();
         debug_assert!(
             !(restyle_hint.has_animation_hint() && restyle_hint.has_non_animation_hint()),
             "Animation restyle hints should not appear with non-animation restyle hints"
         );
 
         let mut data = match self.mutate_data() {
             Some(d) => d,
             None => {
@@ -1511,17 +1509,17 @@ impl<'le> TElement for GeckoElement<'le>
             debug_assert!(
                 self.implemented_pseudo_element()
                     .map_or(true, |p| !p.is_before_or_after()),
                 "display property animation shouldn't run on pseudo elements \
                  since it's only for SMIL"
             );
             unsafe {
                 self.note_explicit_hints(
-                    nsRestyleHint::eRestyle_Subtree,
+                    RestyleHint::restyle_subtree(),
                     nsChangeHint::nsChangeHint_Empty,
                 );
             }
         }
     }
 
     /// Update various animation-related state on a given (pseudo-)element as
     /// results of normal restyle.
--- a/servo/components/style/invalidation/element/restyle_hints.rs
+++ b/servo/components/style/invalidation/element/restyle_hints.rs
@@ -1,20 +1,19 @@
 /* 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 https://mozilla.org/MPL/2.0/. */
 
 //! Restyle hints: an optimization to avoid unnecessarily matching selectors.
 
-#[cfg(feature = "gecko")]
-use crate::gecko_bindings::structs::nsRestyleHint;
 use crate::traversal_flags::TraversalFlags;
 
 bitflags! {
     /// The kind of restyle we need to do for a given element.
+    #[repr(C)]
     pub struct RestyleHint: u8 {
         /// Do a selector match of the element.
         const RESTYLE_SELF = 1 << 0;
 
         /// Do a selector match of the element's descendants.
         const RESTYLE_DESCENDANTS = 1 << 1;
 
         /// Recascade the current element.
@@ -185,80 +184,10 @@ impl RestyleHint {
 }
 
 impl Default for RestyleHint {
     fn default() -> Self {
         Self::empty()
     }
 }
 
-#[cfg(feature = "gecko")]
-impl From<nsRestyleHint> for RestyleHint {
-    fn from(mut raw: nsRestyleHint) -> Self {
-        let mut hint = RestyleHint::empty();
-
-        debug_assert!(
-            raw.0 & nsRestyleHint::eRestyle_LaterSiblings.0 == 0,
-            "Handle later siblings manually if necessary plz."
-        );
-
-        if (raw.0 & (nsRestyleHint::eRestyle_Self.0 | nsRestyleHint::eRestyle_Subtree.0)) != 0 {
-            raw.0 &= !nsRestyleHint::eRestyle_Self.0;
-            hint.insert(RestyleHint::RESTYLE_SELF);
-        }
-
-        if (raw.0 & (nsRestyleHint::eRestyle_Subtree.0 | nsRestyleHint::eRestyle_SomeDescendants.0)) !=
-            0
-        {
-            raw.0 &= !nsRestyleHint::eRestyle_Subtree.0;
-            raw.0 &= !nsRestyleHint::eRestyle_SomeDescendants.0;
-            hint.insert(RestyleHint::RESTYLE_DESCENDANTS);
-        }
-
-        if (raw.0 & (nsRestyleHint::eRestyle_ForceDescendants.0 | nsRestyleHint::eRestyle_Force.0)) !=
-            0
-        {
-            raw.0 &= !nsRestyleHint::eRestyle_Force.0;
-            hint.insert(RestyleHint::RECASCADE_SELF);
-        }
-
-        if (raw.0 & nsRestyleHint::eRestyle_ForceDescendants.0) != 0 {
-            raw.0 &= !nsRestyleHint::eRestyle_ForceDescendants.0;
-            hint.insert(RestyleHint::RECASCADE_DESCENDANTS);
-        }
-
-        hint.insert(RestyleHint::from_bits_truncate(raw.0 as u8));
-
-        hint
-    }
-}
-
 #[cfg(feature = "servo")]
 malloc_size_of_is_0!(RestyleHint);
-
-/// Asserts that all replacement hints have a matching nsRestyleHint value.
-#[cfg(feature = "gecko")]
-#[inline]
-pub fn assert_restyle_hints_match() {
-    use crate::gecko_bindings::structs;
-
-    macro_rules! check_restyle_hints {
-        ( $( $a:ident => $b:path),*, ) => {
-            if cfg!(debug_assertions) {
-                let mut replacements = RestyleHint::replacements();
-                $(
-                    assert_eq!(structs::nsRestyleHint::$a.0 as usize, $b.bits() as usize, stringify!($b));
-                    replacements.remove($b);
-                )*
-                assert_eq!(replacements, RestyleHint::empty(),
-                           "all RestyleHint replacement bits should have an \
-                            assertion");
-            }
-        }
-    }
-
-    check_restyle_hints! {
-        eRestyle_CSSTransitions => RestyleHint::RESTYLE_CSS_TRANSITIONS,
-        eRestyle_CSSAnimations => RestyleHint::RESTYLE_CSS_ANIMATIONS,
-        eRestyle_StyleAttribute => RestyleHint::RESTYLE_STYLE_ATTRIBUTE,
-        eRestyle_StyleAttribute_Animations => RestyleHint::RESTYLE_SMIL,
-    }
-}
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -125,17 +125,16 @@ use style::gecko_bindings::structs;
 use style::gecko_bindings::structs::gfxFontFeatureValueSet;
 use style::gecko_bindings::structs::nsAtom;
 use style::gecko_bindings::structs::nsCSSCounterDesc;
 use style::gecko_bindings::structs::nsCSSFontDesc;
 use style::gecko_bindings::structs::nsCSSPropertyID;
 use style::gecko_bindings::structs::nsCSSValueSharedList;
 use style::gecko_bindings::structs::nsChangeHint;
 use style::gecko_bindings::structs::nsCompatibility;
-use style::gecko_bindings::structs::nsRestyleHint;
 use style::gecko_bindings::structs::nsStyleTransformMatrix::MatrixTransformOperator;
 use style::gecko_bindings::structs::nsTArray;
 use style::gecko_bindings::structs::nsTimingFunction;
 use style::gecko_bindings::structs::nsresult;
 use style::gecko_bindings::structs::AtomArray;
 use style::gecko_bindings::structs::PseudoStyleType;
 use style::gecko_bindings::structs::CallerType;
 use style::gecko_bindings::structs::CompositeOperation;
@@ -163,17 +162,17 @@ use style::gecko_bindings::structs::Styl
 use style::gecko_bindings::structs::StyleRuleInclusion;
 use style::gecko_bindings::structs::StyleSheet as DomStyleSheet;
 use style::gecko_bindings::structs::URLExtraData;
 use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasArcFFI, HasFFI};
 use style::gecko_bindings::sugar::ownership::{HasSimpleFFI, Strong};
 use style::gecko_bindings::sugar::refptr::RefPtr;
 use style::gecko_properties;
 use style::global_style_data::{GlobalStyleData, GLOBAL_STYLE_DATA, STYLE_THREAD_POOL};
-use style::invalidation::element::restyle_hints;
+use style::invalidation::element::restyle_hints::RestyleHint;
 use style::media_queries::MediaList;
 use style::parser::{self, Parse, ParserContext};
 use style::properties::animated_properties::AnimationValue;
 use style::properties::{parse_one_declaration_into, parse_style_attribute};
 use style::properties::{ComputedValues, Importance, NonCustomPropertyId};
 use style::properties::{LonghandId, LonghandIdSet, PropertyDeclarationBlock, PropertyId};
 use style::properties::{PropertyDeclarationId, ShorthandId};
 use style::properties::{SourcePropertyDeclaration, StyleBuilder};
@@ -235,17 +234,16 @@ static mut DUMMY_URL_DATA: *mut URLExtra
 #[no_mangle]
 pub unsafe extern "C" fn Servo_Initialize(dummy_url_data: *mut URLExtraData) {
     use style::gecko_bindings::sugar::origin_flags;
 
     // Pretend that we're a Servo Layout thread, to make some assertions happy.
     thread_state::initialize(thread_state::ThreadState::LAYOUT);
 
     // Perform some debug-only runtime assertions.
-    restyle_hints::assert_restyle_hints_match();
     origin_flags::assert_flags_match();
     parser::assert_parsing_mode_match();
     traversal_flags::assert_traversal_flags_match();
     specified::font::assert_variant_east_asian_matches();
     specified::font::assert_variant_ligatures_matches();
     specified::box_::assert_touch_action_matches();
 
     DUMMY_URL_DATA = dummy_url_data;
@@ -4807,17 +4805,17 @@ pub extern "C" fn Servo_CSSSupports(cond
 
     let namespaces = Default::default();
     cond.eval(&context, &namespaces)
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn Servo_NoteExplicitHints(
     element: RawGeckoElementBorrowed,
-    restyle_hint: nsRestyleHint,
+    restyle_hint: RestyleHint,
     change_hint: nsChangeHint,
 ) {
     GeckoElement(element).note_explicit_hints(restyle_hint, change_hint);
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_TakeChangeHint(
     element: RawGeckoElementBorrowed,
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -6809,12 +6809,11 @@ void nsWindow::ForceTitlebarRedraw(void)
   nsIFrame *frame = view->GetFrame();
   if (!frame) {
     return;
   }
 
   frame = FindTitlebarFrame(frame);
   if (frame) {
     nsLayoutUtils::PostRestyleEvent(frame->GetContent()->AsElement(),
-                                    nsRestyleHint(0),
-                                    nsChangeHint_RepaintFrame);
-  }
-}
+                                    RestyleHint{0}, nsChangeHint_RepaintFrame);
+  }
+}