Bug 1285474: Add mozilla::RestyleManagerBase to share logic between RestyleManager and ServoRestyleManager. r=bholley
authorEmilio Cobos Álvarez <ecoal95@gmail.com>
Fri, 08 Jul 2016 00:08:46 -0700
changeset 346724 769a86b5787dfd7d2bbd10584a54979320c260d9
parent 346723 f25ce46d4eac84012375576d5474106948acccb8
child 346725 da8948e3c26d39932ce8f133a423187262676006
push id1230
push userjlund@mozilla.com
push dateMon, 31 Oct 2016 18:13:35 +0000
treeherdermozilla-release@5e06e3766db2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley
bugs1285474
milestone50.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 1285474: Add mozilla::RestyleManagerBase to share logic between RestyleManager and ServoRestyleManager. r=bholley Partially implement some restyling APIs to take rid of some gecko-only code paths. MozReview-Commit-ID: L5i6Kr2Qars
layout/base/RestyleManager.cpp
layout/base/RestyleManager.h
layout/base/RestyleManagerBase.cpp
layout/base/RestyleManagerBase.h
layout/base/RestyleManagerHandle.h
layout/base/RestyleManagerHandleInlines.h
layout/base/ServoRestyleManager.cpp
layout/base/ServoRestyleManager.h
layout/base/moz.build
layout/base/nsCSSRenderingBorders.cpp
layout/base/nsPresShell.cpp
layout/base/nsRefreshDriver.cpp
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -78,25 +78,22 @@ ElementTagToString(dom::Element* aElemen
   nsCString result;
   nsDependentAtomString buf(aElement->NodeInfo()->NameAtom());
   result.AppendPrintf("(%s@%p)", NS_ConvertUTF16toUTF8(buf).get(), aElement);
   return result;
 }
 #endif
 
 RestyleManager::RestyleManager(nsPresContext* aPresContext)
-  : mPresContext(aPresContext)
+  : RestyleManagerBase(aPresContext)
   , mDoRebuildAllStyleData(false)
   , mInRebuildAllStyleData(false)
-  , mObservingRefreshDriver(false)
   , mInStyleRefresh(false)
   , mSkipAnimationRules(false)
   , mHavePendingNonAnimationRestyles(false)
-  , mRestyleGeneration(1)
-  , mHoverGeneration(0)
   , mRebuildAllExtraHint(nsChangeHint(0))
   , mRebuildAllRestyleHint(nsRestyleHint(0))
   , mAnimationGeneration(0)
   , mReframingStyleContexts(nullptr)
   , mAnimationsWithDestroyedFrame(nullptr)
   , mPendingRestyles(ELEMENT_HAS_PENDING_RESTYLE |
                      ELEMENT_IS_POTENTIAL_RESTYLE_ROOT |
                      ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR)
@@ -215,17 +212,17 @@ SyncViewsAndInvalidateDescendants(nsIFra
  * the restyled element's principle frame to one of its ancestor frames based
  * on what nsCSSRendering::FindBackground returns, since the background style
  * may have been propagated up to an ancestor frame. Processing hints using an
  * ancestor frame is fine in general, but nsChangeHint_ChildrenOnlyTransform is
  * a special case since it is intended to update the children of a specific
  * frame.
  */
 static nsIFrame*
-GetFrameForChildrenOnlyTransformHint(nsIFrame *aFrame)
+GetFrameForChildrenOnlyTransformHint(nsIFrame* aFrame)
 {
   if (aFrame->GetType() == nsGkAtoms::viewportFrame) {
     // This happens if the root-<svg> is fixed positioned, in which case we
     // can't use aFrame->GetContent() to find the primary frame, since
     // GetContent() returns nullptr for ViewportFrame.
     aFrame = aFrame->PrincipalChildList().FirstChild();
   }
   // For an nsHTMLScrollFrame, this will get the SVG frame that has the
@@ -352,34 +349,34 @@ ApplyRenderingChangeToTree(nsPresContext
   // We check StyleDisplay()->HasTransformStyle() in addition to checking
   // IsTransformed() since we can get here for some frames that don't support
   // CSS transforms.
   NS_ASSERTION(!(aChange & nsChangeHint_UpdateTransformLayer) ||
                aFrame->IsTransformed() ||
                aFrame->StyleDisplay()->HasTransformStyle(),
                "Unexpected UpdateTransformLayer hint");
 
-  nsIPresShell *shell = aPresContext->PresShell();
+  nsIPresShell* shell = aPresContext->PresShell();
   if (shell->IsPaintingSuppressed()) {
     // Don't allow synchronous rendering changes when painting is turned off.
     aChange &= ~nsChangeHint_RepaintFrame;
     if (!aChange) {
       return;
     }
   }
 
   // Trigger rendering updates by damaging this frame and any
   // continuations of this frame.
 #ifdef DEBUG
   gInApplyRenderingChangeToTree = true;
 #endif
   if (aChange & nsChangeHint_RepaintFrame) {
     // If the frame's background is propagated to an ancestor, walk up to
     // that ancestor and apply the RepaintFrame change hint to it.
-    nsStyleContext *bgSC;
+    nsStyleContext* bgSC;
     nsIFrame* propagatedFrame = aFrame;
     while (!nsCSSRendering::FindBackground(propagatedFrame, &bgSC)) {
       propagatedFrame = propagatedFrame->GetParent();
       NS_ASSERTION(aFrame, "root frame must paint");
     }
 
     if (propagatedFrame != aFrame) {
       DoApplyRenderingChangeToTree(propagatedFrame, nsChangeHint_RepaintFrame);
@@ -437,29 +434,29 @@ RestyleManager::RecomputePosition(nsIFra
       }
 
       // Update sticky positioning for an entire element at once, starting with
       // the first continuation or ib-split sibling.
       // It's rare that the frame we already have isn't already the first
       // continuation or ib-split sibling, but it can happen when styles differ
       // across continuations such as ::first-line or ::first-letter, and in
       // those cases we will generally (but maybe not always) do the work twice.
-      nsIFrame *firstContinuation =
+      nsIFrame* firstContinuation =
         nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
 
       StickyScrollContainer::ComputeStickyOffsets(firstContinuation);
       StickyScrollContainer* ssc =
         StickyScrollContainer::GetStickyScrollContainerForFrame(firstContinuation);
       if (ssc) {
         ssc->PositionContinuations(firstContinuation);
       }
     } else {
       MOZ_ASSERT(NS_STYLE_POSITION_RELATIVE == display->mPosition,
                  "Unexpected type of positioning");
-      for (nsIFrame *cont = aFrame; cont;
+      for (nsIFrame* cont = aFrame; cont;
            cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
         nsIFrame* cb = cont->GetContainingBlock();
         nsMargin newOffsets;
         WritingMode wm = cb->GetWritingMode();
         const LogicalSize size(wm, cb->GetContentRectRelativeToSelf().Size());
 
         nsHTMLReflowState::ComputeRelativeOffsets(wm, cont, size, newOffsets);
         NS_ASSERTION(newOffsets.left == -newOffsets.right &&
@@ -637,17 +634,17 @@ RestyleManager::StyleChangeReflow(nsIFra
   nsIPresShell::ReflowRootHandling rootHandling;
   if (aHint & nsChangeHint_ReflowChangesSizeOrPosition) {
     rootHandling = nsIPresShell::ePositionOrSizeChange;
   } else {
     rootHandling = nsIPresShell::eNoPositionOrSizeChange;
   }
 
   do {
-    mPresContext->PresShell()->FrameNeedsReflow(aFrame, dirtyType, dirtyBits,
+    PresContext()->PresShell()->FrameNeedsReflow(aFrame, dirtyType, dirtyBits,
                                                 rootHandling);
     aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);
   } while (aFrame);
 }
 
 void
 RestyleManager::AddSubtreeToOverflowTracker(nsIFrame* aFrame)
 {
@@ -740,17 +737,17 @@ RestyleManager::ProcessRestyledFrames(ns
 
   PROFILER_LABEL("RestyleManager", "ProcessRestyledFrames",
     js::ProfileEntry::Category::CSS);
 
   // Make sure to not rebuild quote or counter lists while we're
   // processing restyles
   FrameConstructor()->BeginUpdate();
 
-  FramePropertyTable* propTable = mPresContext->PropertyTable();
+  FramePropertyTable* propTable = PresContext()->PropertyTable();
 
   // Mark frames so that we skip frames that die along the way, bug 123049.
   // A frame can be in the list multiple times with different hints. Further
   // optmization is possible if nsStyleChangeList::AppendChange could coalesce
   int32_t index = count;
 
   while (0 <= --index) {
     const nsStyleChangeData* changeData;
@@ -793,17 +790,17 @@ RestyleManager::ProcessRestyledFrames(ns
         !(hint & nsChangeHint_ReconstructFrame)) {
       if (NeedToReframeForAddingOrRemovingTransform(frame) ||
           frame->GetType() == nsGkAtoms::fieldSetFrame ||
           frame->GetContentInsertionFrame() != frame) {
         // The frame has positioned children that need to be reparented, or
         // it can't easily be converted to/from being an abs-pos container correctly.
         hint |= nsChangeHint_ReconstructFrame;
       } else {
-        for (nsIFrame *cont = frame; cont;
+        for (nsIFrame* cont = frame; cont;
              cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
           // Normally frame construction would set state bits as needed,
           // but we're not going to reconstruct the frame so we need to set them.
           // It's because we need to set this state on each affected frame
           // that we can't coalesce nsChangeHint_UpdateContainingBlock hints up
           // to ancestors (i.e. it can't be an inherited change hint).
           if (cont->IsAbsPosContaininingBlock()) {
             if (cont->StyleDisplay()->HasTransform(cont)) {
@@ -851,17 +848,17 @@ RestyleManager::ProcessRestyledFrames(ns
       if (!(frame->GetStateBits() & NS_FRAME_MAY_BE_TRANSFORMED)) {
         // Frame can not be transformed, and thus a change in transform will
         // have no effect and we should not use the
         // nsChangeHint_UpdatePostTransformOverflow hint.
         hint &= ~nsChangeHint_UpdatePostTransformOverflow;
       }
 
       if (hint & nsChangeHint_UpdateEffects) {
-        for (nsIFrame *cont = frame; cont;
+        for (nsIFrame* cont = frame; cont;
              cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
           nsSVGEffects::UpdateEffects(cont);
         }
       }
       if ((hint & nsChangeHint_InvalidateRenderingObservers) ||
           ((hint & nsChangeHint_UpdateOpacityLayer) &&
            frame->IsFrameOfType(nsIFrame::eSVG) &&
            !(frame->GetStateBits() & NS_STATE_IS_OUTER_SVG))) {
@@ -898,17 +895,17 @@ RestyleManager::ProcessRestyledFrames(ns
           // these frames. Repaint the whole frame.
           hint |= nsChangeHint_RepaintFrame;
         }
       }
 
       if (hint & (nsChangeHint_RepaintFrame | nsChangeHint_SyncFrameView |
                   nsChangeHint_UpdateOpacityLayer | nsChangeHint_UpdateTransformLayer |
                   nsChangeHint_ChildrenOnlyTransform | nsChangeHint_SchedulePaint)) {
-        ApplyRenderingChangeToTree(mPresContext, frame, hint);
+        ApplyRenderingChangeToTree(PresContext(), frame, hint);
       }
       if ((hint & nsChangeHint_RecomputePosition) && !didReflowThisFrame) {
         ActiveLayerTracker::NotifyOffsetRestyle(frame);
         // It is possible for this to fall back to a reflow
         if (!RecomputePosition(frame)) {
           didReflowThisFrame = true;
         }
       }
@@ -916,17 +913,17 @@ RestyleManager::ProcessRestyledFrames(ns
                    (hint & nsChangeHint_UpdateOverflow),
                    "nsChangeHint_UpdateOverflow should be passed too");
       if (!didReflowThisFrame &&
           (hint & (nsChangeHint_UpdateOverflow |
                    nsChangeHint_UpdatePostTransformOverflow |
                    nsChangeHint_UpdateParentOverflow |
                    nsChangeHint_UpdateSubtreeOverflow))) {
         if (hint & nsChangeHint_UpdateSubtreeOverflow) {
-          for (nsIFrame *cont = frame; cont; cont =
+          for (nsIFrame* cont = frame; cont; cont =
                  nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
             AddSubtreeToOverflowTracker(cont);
           }
           // The work we just did in AddSubtreeToOverflowTracker
           // subsumes some of the other hints:
           hint &= ~(nsChangeHint_UpdateOverflow |
                     nsChangeHint_UpdatePostTransformOverflow);
         }
@@ -968,38 +965,38 @@ RestyleManager::ProcessRestyledFrames(ns
             // nsChangeHint_UpdatePostTransformOverflow,
             // CHILDREN_CHANGED is selected as it is
             // strictly stronger.
             if (hint & nsChangeHint_UpdateOverflow) {
               changeKind = OverflowChangedTracker::CHILDREN_CHANGED;
             } else {
               changeKind = OverflowChangedTracker::TRANSFORM_CHANGED;
             }
-            for (nsIFrame *cont = frame; cont; cont =
+            for (nsIFrame* cont = frame; cont; cont =
                    nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
               mOverflowChangedTracker.AddFrame(cont, changeKind);
             }
           }
           // UpdateParentOverflow hints need to be processed in addition
           // to the above, since if the processing of the above hints
           // yields no change, the update will not propagate to the
           // parent.
           if (hint & nsChangeHint_UpdateParentOverflow) {
             MOZ_ASSERT(frame->GetParent(),
                        "shouldn't get style hints for the root frame");
-            for (nsIFrame *cont = frame; cont; cont =
+            for (nsIFrame* cont = frame; cont; cont =
                    nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
               mOverflowChangedTracker.AddFrame(cont->GetParent(),
                                    OverflowChangedTracker::CHILDREN_CHANGED);
             }
           }
         }
       }
       if ((hint & nsChangeHint_UpdateCursor) && !didUpdateCursor) {
-        mPresContext->PresShell()->SynthesizeMouseMove(false);
+        PresContext()->PresShell()->SynthesizeMouseMove(false);
         didUpdateCursor = true;
       }
     }
   }
 
   FrameConstructor()->EndUpdate();
 
   // cleanup references and verify the style tree.  Note that the latter needs
@@ -1048,19 +1045,19 @@ RestyleManager::RestyleElement(Element* 
     // of <area>s.  See bug 135040.  We can remove this block once that's fixed.
     aPrimaryFrame = nullptr;
   }
   NS_ASSERTION(!aPrimaryFrame || aPrimaryFrame->GetContent() == aElement,
                "frame/content mismatch");
 
   // If we're restyling the root element and there are 'rem' units in
   // use, handle dynamic changes to the definition of a 'rem' here.
-  if (mPresContext->UsesRootEMUnits() && aPrimaryFrame &&
+  if (PresContext()->UsesRootEMUnits() && aPrimaryFrame &&
       !mInRebuildAllStyleData) {
-    nsStyleContext *oldContext = aPrimaryFrame->StyleContext();
+    nsStyleContext* oldContext = aPrimaryFrame->StyleContext();
     if (!oldContext->GetParent()) { // check that we're the root element
       RefPtr<nsStyleContext> newContext = StyleSet()->
         ResolveStyleFor(aElement, nullptr /* == oldContext->GetParent() */);
       if (oldContext->StyleFont()->mFont.size !=
           newContext->StyleFont()->mFont.size) {
         // The basis for 'rem' units has changed.
         mRebuildAllRestyleHint |= aRestyleHint;
         if (aRestyleHint & eRestyle_SomeDescendants) {
@@ -1112,17 +1109,17 @@ RestyleManager::ReframingStyleContexts::
 
 RestyleManager::ReframingStyleContexts::~ReframingStyleContexts()
 {
   // Before we go away, we need to flush out any frame construction that
   // was enqueued, so that we start transitions.
   // Note that this is a little bit evil in that we're calling into code
   // that calls our member functions from our destructor, but it's at
   // the beginning of our destructor, so it shouldn't be too bad.
-  mRestyleManager->mPresContext->FrameConstructor()->CreateNeededFrames();
+  mRestyleManager->PresContext()->FrameConstructor()->CreateNeededFrames();
 }
 
 RestyleManager::AnimationsWithDestroyedFrame::AnimationsWithDestroyedFrame(
                                           RestyleManager* aRestyleManager)
   : mRestyleManager(aRestyleManager)
   , mRestorePointer(mRestyleManager->mAnimationsWithDestroyedFrame)
 {
   MOZ_ASSERT(!mRestyleManager->mAnimationsWithDestroyedFrame,
@@ -1181,85 +1178,21 @@ RestyleManager::ContentStateChanged(nsIC
   // XXXbz it would be good if this function only took Elements, but
   // we'd have to make ESM guarantee that usefully.
   if (!aContent->IsElement()) {
     return NS_OK;
   }
 
   Element* aElement = aContent->AsElement();
 
-  nsStyleSet* styleSet = StyleSet();
-  NS_ASSERTION(styleSet, "couldn't get style set");
-
-  nsChangeHint hint = NS_STYLE_HINT_NONE;
-  // Any change to a content state that affects which frames we construct
-  // must lead to a frame reconstruct here if we already have a frame.
-  // Note that we never decide through non-CSS means to not create frames
-  // based on content states, so if we already don't have a frame we don't
-  // need to force a reframe -- if it's needed, the HasStateDependentStyle
-  // call will handle things.
-  nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
-  CSSPseudoElementType pseudoType = CSSPseudoElementType::NotPseudo;
-  if (primaryFrame) {
-    // If it's generated content, ignore LOADING/etc state changes on it.
-    if (!primaryFrame->IsGeneratedContentFrame() &&
-        aStateMask.HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN |
-                                         NS_EVENT_STATE_USERDISABLED |
-                                         NS_EVENT_STATE_SUPPRESSED |
-                                         NS_EVENT_STATE_LOADING)) {
-      hint = nsChangeHint_ReconstructFrame;
-    } else {
-      uint8_t app = primaryFrame->StyleDisplay()->mAppearance;
-      if (app) {
-        nsITheme *theme = mPresContext->GetTheme();
-        if (theme && theme->ThemeSupportsWidget(mPresContext,
-                                                primaryFrame, app)) {
-          bool repaint = false;
-          theme->WidgetStateChanged(primaryFrame, app, nullptr, &repaint, nullptr);
-          if (repaint) {
-            hint |= nsChangeHint_RepaintFrame;
-          }
-        }
-      }
-    }
-
-    pseudoType = primaryFrame->StyleContext()->GetPseudoType();
-
-    primaryFrame->ContentStatesChanged(aStateMask);
-  }
-
-
-  nsRestyleHint rshint;
-
-  if (pseudoType >= CSSPseudoElementType::Count) {
-    rshint = styleSet->HasStateDependentStyle(aElement, aStateMask);
-  } else if (nsCSSPseudoElements::PseudoElementSupportsUserActionState(
-                                                                  pseudoType)) {
-    // If aElement is a pseudo-element, we want to check to see whether there
-    // are any state-dependent rules applying to that pseudo.
-    Element* ancestor = ElementForStyleContext(nullptr, primaryFrame,
-                                               pseudoType);
-    rshint = styleSet->HasStateDependentStyle(ancestor, pseudoType, aElement,
-                                              aStateMask);
-  } else {
-    rshint = nsRestyleHint(0);
-  }
-
-  if (aStateMask.HasState(NS_EVENT_STATE_HOVER) && rshint != 0) {
-    ++mHoverGeneration;
-  }
-
-  if (aStateMask.HasState(NS_EVENT_STATE_VISITED)) {
-    // Exposing information to the page about whether the link is
-    // visited or not isn't really something we can worry about here.
-    // FIXME: We could probably do this a bit better.
-    hint |= nsChangeHint_RepaintFrame;
-  }
-
-  PostRestyleEvent(aElement, rshint, hint);
+  nsChangeHint changeHint;
+  nsRestyleHint restyleHint;
+  ContentStateChangedInternal(aElement, aStateMask, &changeHint, &restyleHint);
+
+  PostRestyleEvent(aElement, restyleHint, changeHint);
   return NS_OK;
 }
 
 // Forwarded nsIMutationObserver method, to handle restyling.
 void
 RestyleManager::AttributeWillChange(Element* aElement,
                                     int32_t aNameSpaceID,
                                     nsIAtom* aAttribute,
@@ -1285,17 +1218,17 @@ RestyleManager::AttributeChanged(Element
                                  int32_t aNameSpaceID,
                                  nsIAtom* aAttribute,
                                  int32_t aModType,
                                  const nsAttrValue* aOldValue)
 {
   // Hold onto the PresShell to prevent ourselves from being destroyed.
   // XXXbz how, exactly, would this attribute change cause us to be
   // destroyed from inside this function?
-  nsCOMPtr<nsIPresShell> shell = mPresContext->GetPresShell();
+  nsCOMPtr<nsIPresShell> shell = PresContext()->GetPresShell();
 
   // Get the frame associated with the content which is the highest in the frame tree
   nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
 
 #if 0
   NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
      ("RestyleManager::AttributeChanged: content=%p[%s] frame=%p",
       aContent, ContentTag(aElement, 0), frame));
@@ -1307,45 +1240,45 @@ RestyleManager::AttributeChanged(Element
   bool reframe = (hint & nsChangeHint_ReconstructFrame) != 0;
 
 #ifdef MOZ_XUL
   // The following listbox widget trap prevents offscreen listbox widget
   // content from being removed and re-inserted (which is what would
   // happen otherwise).
   if (!primaryFrame && !reframe) {
     int32_t namespaceID;
-    nsIAtom* tag = mPresContext->Document()->BindingManager()->
+    nsIAtom* tag = PresContext()->Document()->BindingManager()->
                      ResolveTag(aElement, &namespaceID);
 
     if (namespaceID == kNameSpaceID_XUL &&
         (tag == nsGkAtoms::listitem ||
          tag == nsGkAtoms::listcell))
       return;
   }
 
   if (aAttribute == nsGkAtoms::tooltiptext ||
       aAttribute == nsGkAtoms::tooltip)
   {
-    nsIRootBox* rootBox = nsIRootBox::GetRootBox(mPresContext->GetPresShell());
+    nsIRootBox* rootBox = nsIRootBox::GetRootBox(PresContext()->GetPresShell());
     if (rootBox) {
       if (aModType == nsIDOMMutationEvent::REMOVAL)
         rootBox->RemoveTooltipSupport(aElement);
       if (aModType == nsIDOMMutationEvent::ADDITION)
         rootBox->AddTooltipSupport(aElement);
     }
   }
 
 #endif // MOZ_XUL
 
   if (primaryFrame) {
     // See if we have appearance information for a theme.
     const nsStyleDisplay* disp = primaryFrame->StyleDisplay();
     if (disp->mAppearance) {
-      nsITheme *theme = mPresContext->GetTheme();
-      if (theme && theme->ThemeSupportsWidget(mPresContext, primaryFrame, disp->mAppearance)) {
+      nsITheme* theme = PresContext()->GetTheme();
+      if (theme && theme->ThemeSupportsWidget(PresContext(), primaryFrame, disp->mAppearance)) {
         bool repaint = false;
         theme->WidgetStateChanged(primaryFrame, disp->mAppearance, aAttribute,
             &repaint, aOldValue);
         if (repaint)
           hint |= nsChangeHint_RepaintFrame;
       }
     }
 
@@ -1453,17 +1386,17 @@ RestyleManager::RestyleForAppend(Element
 
 // 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;
+  for (nsIContent* sibling = aStartingSibling; sibling;
        sibling = sibling->GetNextSibling()) {
     if (sibling->IsElement()) {
       aRestyleManager->
         PostRestyleEvent(sibling->AsElement(),
                          nsRestyleHint(eRestyle_Subtree | eRestyle_LaterSiblings),
                          NS_STYLE_HINT_NONE);
       break;
     }
@@ -1656,17 +1589,17 @@ RestyleManager::RebuildAllStyleData(nsCh
              "eRestyle_ForceDescendants");
 
   mRebuildAllExtraHint |= aExtraHint;
   mRebuildAllRestyleHint |= aRestyleHint;
 
   // Processing the style changes could cause a flush that propagates to
   // the parent frame and thus destroys the pres shell, so we must hold
   // a reference.
-  nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
+  nsCOMPtr<nsIPresShell> presShell = PresContext()->GetPresShell();
   if (!presShell || !presShell->GetRootFrame()) {
     mDoRebuildAllStyleData = false;
     return;
   }
 
   // Make sure that the viewmanager will outlive the presshell
   RefPtr<nsViewManager> vm = presShell->GetViewManager();
 
@@ -1681,17 +1614,17 @@ RestyleManager::RebuildAllStyleData(nsCh
   ProcessPendingRestyles();
 }
 
 void
 RestyleManager::StartRebuildAllStyleData(RestyleTracker& aRestyleTracker)
 {
   MOZ_ASSERT(mIsProcessingRestyles);
 
-  nsIFrame* rootFrame = mPresContext->PresShell()->GetRootFrame();
+  nsIFrame* rootFrame = PresContext()->PresShell()->GetRootFrame();
   if (!rootFrame) {
     // No need to do anything.
     return;
   }
 
   mInRebuildAllStyleData = true;
 
   // Tell the style set to get the old rule tree out of the way
@@ -1712,17 +1645,17 @@ RestyleManager::StartRebuildAllStyleData
       (restyleHint & ~(eRestyle_Force | eRestyle_ForceDescendants))) {
     // We want this hint to apply to the root node's primary frame
     // rather than the root frame, since it's the primary frame that has
     // the styles for the root element (rather than the ancestors of the
     // primary frame whose mContent is the root node but which have
     // different styles).  If we use up the hint for one of the
     // ancestors that we hit first, then we'll fail to do the restyling
     // we need to do.
-    Element* root = mPresContext->Document()->GetRootElement();
+    Element* root = PresContext()->Document()->GetRootElement();
     if (root) {
       // If the root element is gone, dropping the hint on the floor
       // should be fine.
       aRestyleTracker.AddPendingRestyle(root, restyleHint, nsChangeHint(0));
     }
     restyleHint = nsRestyleHint(0);
   }
 
@@ -1752,23 +1685,23 @@ RestyleManager::FinishRebuildAllStyleDat
   StyleSet()->EndReconstruct();
 
   mInRebuildAllStyleData = false;
 }
 
 void
 RestyleManager::ProcessPendingRestyles()
 {
-  NS_PRECONDITION(mPresContext->Document(), "No document?  Pshaw!");
+  NS_PRECONDITION(PresContext()->Document(), "No document?  Pshaw!");
   NS_PRECONDITION(!nsContentUtils::IsSafeToRunScript(),
                   "Missing a script blocker!");
 
   // First do any queued-up frame creation.  (We should really
   // merge this into the rest of the process, though; see bug 827239.)
-  mPresContext->FrameConstructor()->CreateNeededFrames();
+  PresContext()->FrameConstructor()->CreateNeededFrames();
 
   // Process non-animation restyles...
   MOZ_ASSERT(!mIsProcessingRestyles,
              "Nesting calls to ProcessPendingRestyles?");
   mIsProcessingRestyles = true;
 
   // Before we process any restyles, we need to ensure that style
   // resulting from any animations is up-to-date, so that if any style
@@ -1796,23 +1729,23 @@ RestyleManager::ProcessPendingRestyles()
     // want to tell the transition manager to act as though we're in
     // UpdateOnlyAnimationStyles.
     //
     // FIXME: In the future, we might want to refactor the way the
     // animation and transition manager do their refresh driver ticks so
     // that we can use UpdateOnlyAnimationStyles, with a different
     // boolean argument, for this update as well, instead of having them
     // post style updates in their WillRefresh methods.
-    mPresContext->TransitionManager()->SetInAnimationOnlyStyleUpdate(true);
+    PresContext()->TransitionManager()->SetInAnimationOnlyStyleUpdate(true);
   }
 
   ProcessRestyles(mPendingRestyles);
 
   if (!haveNonAnimation) {
-    mPresContext->TransitionManager()->SetInAnimationOnlyStyleUpdate(false);
+    PresContext()->TransitionManager()->SetInAnimationOnlyStyleUpdate(false);
   }
 
   mIsProcessingRestyles = false;
 
   NS_ASSERTION(haveNonAnimation || !mHavePendingNonAnimationRestyles,
                "should not have added restyles");
   mHavePendingNonAnimationRestyles = false;
 
@@ -1830,17 +1763,17 @@ RestyleManager::ProcessPendingRestyles()
              "should have called FinishRebuildAllStyleData");
 }
 
 void
 RestyleManager::BeginProcessingRestyles(RestyleTracker& aRestyleTracker)
 {
   // Make sure to not rebuild quote or counter lists while we're
   // processing restyles
-  mPresContext->FrameConstructor()->BeginUpdate();
+  PresContext()->FrameConstructor()->BeginUpdate();
 
   mInStyleRefresh = true;
 
   if (ShouldStartRebuildAllFor(aRestyleTracker)) {
     mDoRebuildAllStyleData = false;
     StartRebuildAllStyleData(aRestyleTracker);
   }
 }
@@ -1857,54 +1790,54 @@ RestyleManager::EndProcessingRestyles()
   // Set mInStyleRefresh to false now, since the EndUpdate call might
   // add more restyles.
   mInStyleRefresh = false;
 
   if (mInRebuildAllStyleData) {
     FinishRebuildAllStyleData();
   }
 
-  mPresContext->FrameConstructor()->EndUpdate();
+  PresContext()->FrameConstructor()->EndUpdate();
 
 #ifdef DEBUG
-  mPresContext->PresShell()->VerifyStyleTree();
+  PresContext()->PresShell()->VerifyStyleTree();
 #endif
 }
 
 void
 RestyleManager::UpdateOnlyAnimationStyles()
 {
-  bool doCSS = mPresContext->EffectCompositor()->HasPendingStyleUpdates();
-
-  nsIDocument* document = mPresContext->Document();
+  bool doCSS = PresContext()->EffectCompositor()->HasPendingStyleUpdates();
+
+  nsIDocument* document = PresContext()->Document();
   nsSMILAnimationController* animationController =
     document->HasAnimationController() ?
     document->GetAnimationController() :
     nullptr;
   bool doSMIL = animationController &&
                 animationController->MightHavePendingStyleUpdates();
 
   if (!doCSS && !doSMIL) {
     return;
   }
 
-  nsTransitionManager* transitionManager = mPresContext->TransitionManager();
+  nsTransitionManager* transitionManager = PresContext()->TransitionManager();
 
   transitionManager->SetInAnimationOnlyStyleUpdate(true);
 
   RestyleTracker tracker(ELEMENT_HAS_PENDING_ANIMATION_ONLY_RESTYLE |
                          ELEMENT_IS_POTENTIAL_ANIMATION_ONLY_RESTYLE_ROOT);
   tracker.Init(this);
 
   if (doCSS) {
     // FIXME:  We should have the transition manager and animation manager
     // add only the elements for which animations are currently throttled
     // (i.e., animating on the compositor with main-thread style updates
     // suppressed).
-    mPresContext->EffectCompositor()->AddStyleUpdatesTo(tracker);
+    PresContext()->EffectCompositor()->AddStyleUpdatesTo(tracker);
   }
 
   if (doSMIL) {
     animationController->AddStyleUpdatesTo(tracker);
   }
 
   ProcessRestyles(tracker);
 
@@ -1912,18 +1845,18 @@ RestyleManager::UpdateOnlyAnimationStyle
 }
 
 void
 RestyleManager::PostRestyleEvent(Element* aElement,
                                  nsRestyleHint aRestyleHint,
                                  nsChangeHint aMinChangeHint,
                                  const RestyleHintData* aRestyleHintData)
 {
-  if (MOZ_UNLIKELY(!mPresContext) ||
-      MOZ_UNLIKELY(mPresContext->PresShell()->IsDestroying())) {
+  if (MOZ_UNLIKELY(IsDisconnected()) ||
+      MOZ_UNLIKELY(PresContext()->PresShell()->IsDestroying())) {
     return;
   }
 
   if (aRestyleHint == 0 && !aMinChangeHint) {
     // Nothing to do here
     return;
   }
 
@@ -1943,20 +1876,20 @@ RestyleManager::PostRestyleEvent(Element
 
 void
 RestyleManager::PostRestyleEventInternal(bool aForLazyConstruction)
 {
   // Make sure we're not in a style refresh; if we are, we still have
   // a call to ProcessPendingRestyles coming and there's no need to
   // add ourselves as a refresh observer until then.
   bool inRefresh = !aForLazyConstruction && mInStyleRefresh;
-  nsIPresShell* presShell = mPresContext->PresShell();
-  if (!mObservingRefreshDriver && !inRefresh) {
-    mObservingRefreshDriver = mPresContext->RefreshDriver()->
-      AddStyleFlushObserver(presShell);
+  nsIPresShell* presShell = PresContext()->PresShell();
+  if (!ObservingRefreshDriver() && !inRefresh) {
+    SetObservingRefreshDriver(PresContext()->RefreshDriver()->
+        AddStyleFlushObserver(presShell));
   }
 
   // Unconditionally flag our document as needing a flush.  The other
   // option here would be a dedicated boolean to track whether we need
   // to do so (set here and unset in ProcessPendingRestyles).
   presShell->GetDocument()->SetNeedStyleFlush();
 }
 
@@ -2410,33 +2343,33 @@ RestyleManager::ReparentStyleContext(nsI
     // Check that our assumption that continuations of the same
     // pseudo-type and with the same style context parent have the
     // same style context is valid before the reresolution.  (We need
     // to check the pseudo-type and style context parent because of
     // :first-letter and :first-line, where we create styled and
     // unstyled letter/line frames distinguished by pseudo-type, and
     // then need to distinguish their descendants based on having
     // different parents.)
-    nsIFrame *nextContinuation = aFrame->GetNextContinuation();
+    nsIFrame* nextContinuation = aFrame->GetNextContinuation();
     if (nextContinuation) {
-      nsStyleContext *nextContinuationContext =
+      nsStyleContext* nextContinuationContext =
         nextContinuation->StyleContext();
       NS_ASSERTION(oldContext == nextContinuationContext ||
                    oldContext->GetPseudo() !=
                      nextContinuationContext->GetPseudo() ||
                    oldContext->GetParent() !=
                      nextContinuationContext->GetParent(),
                    "continuations should have the same style context");
     }
   }
 #endif
 
-  nsIFrame *prevContinuation =
+  nsIFrame* prevContinuation =
     GetPrevContinuationWithPossiblySameStyle(aFrame);
-  nsStyleContext *prevContinuationContext;
+  nsStyleContext* prevContinuationContext;
   bool copyFromContinuation =
     prevContinuation &&
     (prevContinuationContext = prevContinuation->StyleContext())
       ->GetPseudo() == oldContext->GetPseudo() &&
      prevContinuationContext->GetParent() == newParentContext;
   if (copyFromContinuation) {
     // Just use the style context from the frame's previous
     // continuation (see assertion about aFrame->GetNextContinuation()
@@ -3968,19 +3901,19 @@ ElementRestyler::RestyleSelf(nsIFrame* a
     mParentFrameHintsNotHandledForDescendants =
       nsChangeHint_Hints_NotHandledForDescendants;
   }
 
   LOG_RESTYLE("parentContext = %p", parentContext);
 
   // do primary context
   RefPtr<nsStyleContext> newContext;
-  nsIFrame *prevContinuation =
+  nsIFrame* prevContinuation =
     GetPrevContinuationWithPossiblySameStyle(aSelf);
-  nsStyleContext *prevContinuationContext;
+  nsStyleContext* prevContinuationContext;
   bool copyFromContinuation =
     prevContinuation &&
     (prevContinuationContext = prevContinuation->StyleContext())
       ->GetPseudo() == oldContext->GetPseudo() &&
      prevContinuationContext->GetParent() == parentContext;
   if (copyFromContinuation) {
     // Just use the style context from the frame's previous
     // continuation.
@@ -4435,17 +4368,17 @@ ElementRestyler::RestyleChildren(nsResty
   // on a frame change.  The act of reconstructing frames will force
   // new style contexts to be resolved on all of this frame's
   // descendants anyway, so we want to avoid wasting time processing
   // style contexts that we're just going to throw away anyway. - dwh
   // It's also important to check mHintsHandled since reresolving the
   // kids would use mFrame->StyleContext(), which is out of date if
   // mHintsHandled has a ReconstructFrame hint; doing this could trigger
   // assertions about mismatched rule trees.
-  nsIFrame *lastContinuation;
+  nsIFrame* lastContinuation;
   if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) {
     InitializeAccessibilityNotifications(mFrame->StyleContext());
 
     for (nsIFrame* f = mFrame; f;
          f = GetNextContinuationWithSameStyle(f, f->StyleContext())) {
       lastContinuation = f;
       RestyleContentChildren(f, aChildRestyleHint);
     }
@@ -5044,25 +4977,16 @@ RestyleManager::ComputeAndProcessStyleCh
   r.RestyleChildrenOfDisplayContentsElement(frame, aNewContext, aMinChange,
                                             aRestyleTracker,
                                             aRestyleHint, aRestyleHintData);
   ProcessRestyledFrames(changeList);
   ClearCachedInheritedStyleDataOnDescendants(contextsToClear);
 }
 
 nsStyleSet*
-RestyleManager::StyleSet() const
-{
-  MOZ_ASSERT(mPresContext->StyleSet()->IsGecko(),
-             "RestyleManager should only be used with a Gecko-flavored "
-             "style backend");
-  return mPresContext->StyleSet()->AsGecko();
-}
-
-nsStyleSet*
 ElementRestyler::StyleSet() const
 {
   MOZ_ASSERT(mPresContext->StyleSet()->IsGecko(),
              "ElementRestyler should only be used with a Gecko-flavored "
              "style backend");
   return mPresContext->StyleSet()->AsGecko();
 }
 
--- a/layout/base/RestyleManager.h
+++ b/layout/base/RestyleManager.h
@@ -7,16 +7,17 @@
  * Code responsible for managing style changes: tracking what style
  * changes need to happen, scheduling them, and doing them.
  */
 
 #ifndef mozilla_RestyleManager_h
 #define mozilla_RestyleManager_h
 
 #include "mozilla/RestyleLogging.h"
+#include "mozilla/RestyleManagerBase.h"
 #include "nsISupportsImpl.h"
 #include "nsChangeHint.h"
 #include "RestyleTracker.h"
 #include "nsPresContext.h"
 #include "nsRefreshDriver.h"
 #include "nsRefPtrHashtable.h"
 #include "nsTransitionManager.h"
 
@@ -28,48 +29,36 @@ namespace mozilla {
   enum class CSSPseudoElementType : uint8_t;
   class EventStates;
   struct UndisplayedNode;
 
 namespace dom {
   class Element;
 } // namespace dom
 
-class RestyleManager final
+class RestyleManager final : public RestyleManagerBase
 {
 public:
-  friend class ::nsRefreshDriver;
   friend class RestyleTracker;
 
-  typedef mozilla::dom::Element Element;
-
   explicit RestyleManager(nsPresContext* aPresContext);
 
 private:
   // Private destructor, to discourage deletion outside of Release():
   ~RestyleManager()
   {
     MOZ_ASSERT(!mReframingStyleContexts,
                "temporary member should be nulled out before destruction");
     MOZ_ASSERT(!mAnimationsWithDestroyedFrame,
                "leaving dangling pointers from AnimationsWithDestroyedFrame");
   }
 
 public:
   NS_INLINE_DECL_REFCOUNTING(mozilla::RestyleManager)
 
-  void Disconnect() {
-    mPresContext = nullptr;
-  }
-
-  nsPresContext* PresContext() const {
-    MOZ_ASSERT(mPresContext);
-    return mPresContext;
-  }
-
   // Should be called when a frame is going to be destroyed and
   // WillDestroyFrameTree hasn't been called yet.
   void NotifyDestroyingFrame(nsIFrame* aFrame);
 
   // Forwarded nsIDocumentObserver method, to handle restyling (and
   // passing the notification to the frame).
   nsresult ContentStateChanged(nsIContent*   aContent,
                                EventStates aStateMask);
@@ -83,24 +72,16 @@ public:
   // Forwarded nsIMutationObserver method, to handle restyling (and
   // passing the notification to the frame).
   void AttributeChanged(Element* aElement,
                         int32_t  aNameSpaceID,
                         nsIAtom* aAttribute,
                         int32_t  aModType,
                         const nsAttrValue* aOldValue);
 
-  // Get an integer that increments every time we process pending restyles.
-  // The value is never 0.
-  uint32_t GetRestyleGeneration() const { return mRestyleGeneration; }
-
-  // Get an integer that increments every time there is a style change
-  // as a result of a change to the :hover content state.
-  uint32_t GetHoverGeneration() const { return mHoverGeneration; }
-
   // Get a counter that increments on every style change, that we use to
   // track whether off-main-thread animations are up-to-date.
   uint64_t GetAnimationGeneration() const { return mAnimationGeneration; }
 
   static uint64_t GetAnimationGenerationForFrame(nsIFrame* aFrame);
 
   // Update the animation generation count to mark that animation state
   // has changed.
@@ -134,25 +115,23 @@ public:
    */
   nsresult ReparentStyleContext(nsIFrame* aFrame);
 
   void ClearSelectors() {
     mPendingRestyles.ClearSelectors();
   }
 
 private:
-  nsCSSFrameConstructor* FrameConstructor() const
-    { return PresContext()->FrameConstructor(); }
-
   // Used when restyling an element with a frame.
   void ComputeAndProcessStyleChange(nsIFrame*              aFrame,
                                     nsChangeHint           aMinChange,
                                     RestyleTracker&        aRestyleTracker,
                                     nsRestyleHint          aRestyleHint,
                                     const RestyleHintData& aRestyleHintData);
+
   // Used when restyling a display:contents element.
   void ComputeAndProcessStyleChange(nsStyleContext*        aNewContext,
                                     Element*               aElement,
                                     nsChangeHint           aMinChange,
                                     RestyleTracker&        aRestyleTracker,
                                     nsRestyleHint          aRestyleHint,
                                     const RestyleHintData& aRestyleHintData);
 
@@ -451,17 +430,17 @@ public:
 #endif
 
 #ifdef RESTYLE_LOGGING
   /**
    * Returns whether a restyle event currently being processed by this
    * RestyleManager should be logged.
    */
   bool ShouldLogRestyle() {
-    return ShouldLogRestyle(mPresContext);
+    return ShouldLogRestyle(PresContext());
   }
 
   /**
    * Returns whether a restyle event currently being processed for the
    * document with the specified nsPresContext should be logged.
    */
   static bool ShouldLogRestyle(nsPresContext* aPresContext) {
     return aPresContext->RestyleLoggingEnabled() &&
@@ -487,17 +466,22 @@ public:
   // environment variable.
   static uint32_t StructsToLog();
 
   static nsCString StructNamesToString(uint32_t aSIDs);
   int32_t& LoggingDepth() { return mLoggingDepth; }
 #endif
 
 private:
-  inline nsStyleSet* StyleSet() const;
+  inline nsStyleSet* StyleSet() const {
+    MOZ_ASSERT(PresContext()->StyleSet()->IsGecko(),
+               "RestyleManager should only be used with a Gecko-flavored "
+               "style backend");
+    return PresContext()->StyleSet()->AsGecko();
+  }
 
   /* aMinHint is the minimal change that should be made to the element */
   // XXXbz do we really need the aPrimaryFrame argument here?
   void RestyleElement(Element*        aElement,
                       nsIFrame*       aPrimaryFrame,
                       nsChangeHint    aMinHint,
                       RestyleTracker& aRestyleTracker,
                       nsRestyleHint   aRestyleHint,
@@ -522,44 +506,33 @@ private:
     return mDoRebuildAllStyleData &&
            &aRestyleTracker == &mPendingRestyles;
   }
 
   void ProcessRestyles(RestyleTracker& aRestyleTracker) {
     // Fast-path the common case (esp. for the animation restyle
     // tracker) of not having anything to do.
     if (aRestyleTracker.Count() || ShouldStartRebuildAllFor(aRestyleTracker)) {
-      if (++mRestyleGeneration == 0) {
-        // Keep mRestyleGeneration from being 0, since that's what
-        // nsPresContext::GetRestyleGeneration returns when it no
-        // longer has a RestyleManager.
-        ++mRestyleGeneration;
-      }
+      IncrementRestyleGeneration();
       aRestyleTracker.DoProcessRestyles();
     }
   }
 
 private:
-  nsPresContext* mPresContext; // weak, disconnected in Disconnect
-
   // True if we need to reconstruct the rule tree the next time we
   // process restyles.
   bool mDoRebuildAllStyleData : 1;
   // True if we're currently in the process of reconstructing the rule tree.
   bool mInRebuildAllStyleData : 1;
-  // True if we're already waiting for a refresh notification
-  bool mObservingRefreshDriver : 1;
   // True if we're in the middle of a nsRefreshDriver refresh
   bool mInStyleRefresh : 1;
   // Whether rule matching should skip styles associated with animation
   bool mSkipAnimationRules : 1;
   bool mHavePendingNonAnimationRestyles : 1;
 
-  uint32_t mRestyleGeneration;
-  uint32_t mHoverGeneration;
   nsChangeHint mRebuildAllExtraHint;
   nsRestyleHint mRebuildAllRestyleHint;
 
   OverflowChangedTracker mOverflowChangedTracker;
 
   // The total number of animation flushes by this frame constructor.
   // Used to keep the layer and animation manager in sync.
   uint64_t mAnimationGeneration;
new file mode 100644
--- /dev/null
+++ b/layout/base/RestyleManagerBase.cpp
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/RestyleManagerBase.h"
+#include "mozilla/StyleSetHandle.h"
+#include "nsIFrame.h"
+
+namespace mozilla {
+
+RestyleManagerBase::RestyleManagerBase(nsPresContext* aPresContext)
+  : mPresContext(aPresContext)
+  , mRestyleGeneration(1)
+  , mHoverGeneration(0)
+  , mObservingRefreshDriver(false)
+{
+  MOZ_ASSERT(mPresContext);
+}
+
+/**
+ * Calculates the change hint and the restyle hint for a given content state
+ * change.
+ *
+ * This is called from both Restyle managers.
+ */
+void
+RestyleManagerBase::ContentStateChangedInternal(Element* aElement,
+                                                EventStates aStateMask,
+                                                nsChangeHint* aOutChangeHint,
+                                                nsRestyleHint* aOutRestyleHint)
+{
+  MOZ_ASSERT(aOutChangeHint);
+  MOZ_ASSERT(aOutRestyleHint);
+
+  StyleSetHandle styleSet = PresContext()->StyleSet();
+  NS_ASSERTION(styleSet, "couldn't get style set");
+
+  *aOutChangeHint = NS_STYLE_HINT_NONE;
+  // Any change to a content state that affects which frames we construct
+  // must lead to a frame reconstruct here if we already have a frame.
+  // Note that we never decide through non-CSS means to not create frames
+  // based on content states, so if we already don't have a frame we don't
+  // need to force a reframe -- if it's needed, the HasStateDependentStyle
+  // call will handle things.
+  nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
+  CSSPseudoElementType pseudoType = CSSPseudoElementType::NotPseudo;
+  if (primaryFrame) {
+    // If it's generated content, ignore LOADING/etc state changes on it.
+    if (!primaryFrame->IsGeneratedContentFrame() &&
+        aStateMask.HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN |
+                                         NS_EVENT_STATE_USERDISABLED |
+                                         NS_EVENT_STATE_SUPPRESSED |
+                                         NS_EVENT_STATE_LOADING)) {
+      *aOutChangeHint = nsChangeHint_ReconstructFrame;
+    } else {
+      uint8_t app = primaryFrame->StyleDisplay()->mAppearance;
+      if (app) {
+        nsITheme *theme = PresContext()->GetTheme();
+        if (theme && theme->ThemeSupportsWidget(PresContext(),
+                                                primaryFrame, app)) {
+          bool repaint = false;
+          theme->WidgetStateChanged(primaryFrame, app, nullptr, &repaint, nullptr);
+          if (repaint) {
+            *aOutChangeHint |= nsChangeHint_RepaintFrame;
+          }
+        }
+      }
+    }
+
+    pseudoType = primaryFrame->StyleContext()->GetPseudoType();
+
+    primaryFrame->ContentStatesChanged(aStateMask);
+  }
+
+  if (pseudoType >= CSSPseudoElementType::Count) {
+    *aOutRestyleHint = styleSet->HasStateDependentStyle(aElement, aStateMask);
+  } else if (nsCSSPseudoElements::PseudoElementSupportsUserActionState(
+                                                                  pseudoType)) {
+    // If aElement is a pseudo-element, we want to check to see whether there
+    // are any state-dependent rules applying to that pseudo.
+    Element* ancestor = ElementForStyleContext(nullptr, primaryFrame,
+                                               pseudoType);
+    *aOutRestyleHint = styleSet->HasStateDependentStyle(ancestor, pseudoType, aElement,
+                                                       aStateMask);
+  } else {
+    *aOutRestyleHint = nsRestyleHint(0);
+  }
+
+  if (aStateMask.HasState(NS_EVENT_STATE_HOVER) && *aOutRestyleHint != 0) {
+    IncrementHoverGeneration();
+  }
+
+  if (aStateMask.HasState(NS_EVENT_STATE_VISITED)) {
+    // Exposing information to the page about whether the link is
+    // visited or not isn't really something we can worry about here.
+    // FIXME: We could probably do this a bit better.
+    *aOutChangeHint |= nsChangeHint_RepaintFrame;
+  }
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/layout/base/RestyleManagerBase.h
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_RestyleManagerBase_h
+#define mozilla_RestyleManagerBase_h
+
+#include "nsChangeHint.h"
+
+namespace mozilla {
+
+class ServoRestyleManager;
+class RestyleManager;
+
+/**
+ * Class for sharing data and logic common to both RestyleManager and
+ * ServoRestyleManager.
+ */
+class RestyleManagerBase
+{
+protected:
+  explicit RestyleManagerBase(nsPresContext* aPresContext);
+
+public:
+  typedef mozilla::dom::Element Element;
+
+  // Get an integer that increments every time we process pending restyles.
+  // The value is never 0.
+  uint32_t GetRestyleGeneration() const { return mRestyleGeneration; }
+
+  // Get an integer that increments every time there is a style change
+  // as a result of a change to the :hover content state.
+  uint32_t GetHoverGeneration() const { return mHoverGeneration; }
+
+  bool ObservingRefreshDriver() const { return mObservingRefreshDriver; }
+
+  void SetObservingRefreshDriver(bool aObserving) {
+      mObservingRefreshDriver = aObserving;
+  }
+
+  void Disconnect() { mPresContext = nullptr; }
+
+protected:
+  void ContentStateChangedInternal(Element* aElement,
+                                   EventStates aStateMask,
+                                   nsChangeHint* aOutChangeHint,
+                                   nsRestyleHint* aOutRestyleHint);
+
+  bool IsDisconnected() { return mPresContext == nullptr; }
+
+  void IncrementHoverGeneration() {
+    ++mHoverGeneration;
+  }
+
+  void IncrementRestyleGeneration() {
+    if (++mRestyleGeneration == 0) {
+      // Keep mRestyleGeneration from being 0, since that's what
+      // nsPresContext::GetRestyleGeneration returns when it no
+      // longer has a RestyleManager.
+      ++mRestyleGeneration;
+    }
+  }
+
+  nsPresContext* PresContext() const {
+    MOZ_ASSERT(mPresContext);
+    return mPresContext;
+  }
+
+  nsCSSFrameConstructor* FrameConstructor() const {
+    return PresContext()->FrameConstructor();
+  }
+
+  inline bool IsGecko() const {
+    return !IsServo();
+  }
+
+  inline bool IsServo() const {
+#ifdef MOZ_STYLO
+    return PresContext()->StyleSet()->IsServo();
+#else
+    return false;
+#endif
+  }
+
+private:
+  nsPresContext* mPresContext; // weak, can be null after Disconnect().
+  uint32_t mRestyleGeneration;
+  uint32_t mHoverGeneration;
+  // True if we're already waiting for a refresh notification.
+  bool mObservingRefreshDriver;
+};
+
+} // namespace mozilla
+
+#endif
--- a/layout/base/RestyleManagerHandle.h
+++ b/layout/base/RestyleManagerHandle.h
@@ -130,16 +130,18 @@ public:
     inline void AttributeChanged(dom::Element* aElement,
                                  int32_t aNameSpaceID,
                                  nsIAtom* aAttribute,
                                  int32_t aModType,
                                  const nsAttrValue* aOldValue);
     inline nsresult ReparentStyleContext(nsIFrame* aFrame);
     inline bool HasPendingRestyles();
     inline uint64_t GetRestyleGeneration() const;
+    inline uint32_t GetHoverGeneration() const;
+    inline void SetObservingRefreshDriver(bool aObserving);
 
   private:
     // Stores a pointer to an RestyleManager or a ServoRestyleManager.  The least
     // significant bit is 0 for the former, 1 for the latter.  This is
     // valid as the least significant bit will never be used for a pointer
     // value on platforms we care about.
     uintptr_t mValue;
   };
--- a/layout/base/RestyleManagerHandleInlines.h
+++ b/layout/base/RestyleManagerHandleInlines.h
@@ -138,14 +138,27 @@ RestyleManagerHandle::Ptr::HasPendingRes
 }
 
 uint64_t
 RestyleManagerHandle::Ptr::GetRestyleGeneration() const
 {
   FORWARD(GetRestyleGeneration, ());
 }
 
+uint32_t
+RestyleManagerHandle::Ptr::GetHoverGeneration() const
+{
+  FORWARD(GetHoverGeneration, ());
+}
+
+void
+RestyleManagerHandle::Ptr::SetObservingRefreshDriver(bool aObserving)
+{
+  FORWARD(SetObservingRefreshDriver, (aObserving));
+}
+
+
 } // namespace mozilla
 
 #undef FORWARD
 #undef FORWARD_CONCRETE
 
 #endif // mozilla_RestyleManagerHandleInlines_h
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -1,37 +1,57 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ServoRestyleManager.h"
+#include "mozilla/ServoStyleSet.h"
 
 using namespace mozilla::dom;
 
 namespace mozilla {
 
-ServoRestyleManager::ServoRestyleManager()
-  : mRestyleGeneration(1)
+ServoRestyleManager::ServoRestyleManager(nsPresContext* aPresContext)
+  : RestyleManagerBase(aPresContext)
 {
 }
 
 void
 ServoRestyleManager::Disconnect()
 {
   NS_ERROR("stylo: ServoRestyleManager::Disconnect not implemented");
 }
 
 void
 ServoRestyleManager::PostRestyleEvent(Element* aElement,
                                       nsRestyleHint aRestyleHint,
                                       nsChangeHint aMinChangeHint)
 {
-  NS_ERROR("stylo: ServoRestyleManager::PostRestyleEvent not implemented");
+  if (MOZ_UNLIKELY(IsDisconnected()) ||
+      MOZ_UNLIKELY(PresContext()->PresShell()->IsDestroying())) {
+    return;
+  }
+
+  nsIPresShell* presShell = PresContext()->PresShell();
+  if (!ObservingRefreshDriver()) {
+    SetObservingRefreshDriver(PresContext()->RefreshDriver()->
+        AddStyleFlushObserver(presShell));
+  }
+
+  aElement->SetIsDirtyForServo();
+  nsINode* cur = aElement;
+  while ((cur = cur->GetParentNode())) {
+    if (cur->HasDirtyDescendantsForServo())
+      break;
+    cur->SetHasDirtyDescendantsForServo();
+  }
+
+  presShell->GetDocument()->SetNeedStyleFlush();
 }
 
 void
 ServoRestyleManager::PostRestyleEventForLazyConstruction()
 {
   NS_ERROR("stylo: ServoRestyleManager::PostRestyleEventForLazyConstruction not implemented");
 }
 
@@ -48,17 +68,17 @@ ServoRestyleManager::PostRebuildAllStyle
 {
   MOZ_CRASH("stylo: ServoRestyleManager::PostRebuildAllStyleDataEvent not implemented");
 }
 
 void
 ServoRestyleManager::ProcessPendingRestyles()
 {
   // XXXheycam Do nothing for now.
-  mRestyleGeneration++;
+  IncrementRestyleGeneration();
 }
 
 void
 ServoRestyleManager::RestyleForInsertOrChange(Element* aContainer,
                                               nsIContent* aChild)
 {
   NS_ERROR("stylo: ServoRestyleManager::RestyleForInsertOrChange not implemented");
 }
@@ -77,17 +97,27 @@ ServoRestyleManager::RestyleForRemove(El
 {
   NS_ERROR("stylo: ServoRestyleManager::RestyleForRemove not implemented");
 }
 
 nsresult
 ServoRestyleManager::ContentStateChanged(nsIContent* aContent,
                                          EventStates aStateMask)
 {
-  NS_ERROR("stylo: ServoRestyleManager::ContentStateChanged not implemented");
+  if (!aContent->IsElement()) {
+    return NS_OK;
+  }
+
+  Element* aElement = aContent->AsElement();
+  nsChangeHint changeHint;
+  nsRestyleHint restyleHint;
+  ContentStateChangedInternal(aElement, aStateMask, &changeHint, &restyleHint);
+
+  // TODO(emilio): Post a restyle here, and make it effective.
+  // PostRestyleEvent(aElement, restyleHint, changeHint);
   return NS_OK;
 }
 
 void
 ServoRestyleManager::AttributeWillChange(Element* aElement,
                                          int32_t aNameSpaceID,
                                          nsIAtom* aAttribute,
                                          int32_t aModType,
@@ -114,15 +144,9 @@ ServoRestyleManager::ReparentStyleContex
 
 bool
 ServoRestyleManager::HasPendingRestyles()
 {
   NS_ERROR("stylo: ServoRestyleManager::HasPendingRestyles not implemented");
   return false;
 }
 
-uint64_t
-ServoRestyleManager::GetRestyleGeneration() const
-{
-  return mRestyleGeneration;
-}
-
 } // namespace mozilla
--- a/layout/base/ServoRestyleManager.h
+++ b/layout/base/ServoRestyleManager.h
@@ -3,40 +3,43 @@
 /* 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 http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_ServoRestyleManager_h
 #define mozilla_ServoRestyleManager_h
 
 #include "mozilla/EventStates.h"
+#include "mozilla/RestyleManagerBase.h"
 #include "nsChangeHint.h"
 #include "nsISupportsImpl.h"
+#include "nsPresContext.h"
+#include "nsINode.h"
 
 namespace mozilla {
 namespace dom {
 class Element;
 } // namespace dom
 } // namespace mozilla
 class nsAttrValue;
 class nsIAtom;
 class nsIContent;
 class nsIFrame;
 
 namespace mozilla {
 
 /**
  * Restyle manager for a Servo-backed style system.
  */
-class ServoRestyleManager
+class ServoRestyleManager : public RestyleManagerBase
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(ServoRestyleManager)
 
-  ServoRestyleManager();
+  explicit ServoRestyleManager(nsPresContext* aPresContext);
 
   void Disconnect();
   void PostRestyleEvent(dom::Element* aElement,
                         nsRestyleHint aRestyleHint,
                         nsChangeHint aMinChangeHint);
   void PostRestyleEventForLazyConstruction();
   void RebuildAllStyleData(nsChangeHint aExtraHint,
                            nsRestyleHint aRestyleHint);
@@ -59,19 +62,24 @@ public:
                            const nsAttrValue* aNewValue);
   void AttributeChanged(dom::Element* aElement,
                         int32_t aNameSpaceID,
                         nsIAtom* aAttribute,
                         int32_t aModType,
                         const nsAttrValue* aOldValue);
   nsresult ReparentStyleContext(nsIFrame* aFrame);
   bool HasPendingRestyles();
-  uint64_t GetRestyleGeneration() const;
 
 protected:
   ~ServoRestyleManager() {}
 
-  uint64_t mRestyleGeneration;
+private:
+  inline ServoStyleSet* StyleSet() const {
+    MOZ_ASSERT(PresContext()->StyleSet()->IsServo(),
+               "ServoRestyleManager should only be used with a Servo-flavored "
+               "style backend");
+    return PresContext()->StyleSet()->AsServo();
+  }
 };
 
 } // namespace mozilla
 
 #endif // mozilla_ServoRestyleManager_h
--- a/layout/base/moz.build
+++ b/layout/base/moz.build
@@ -102,16 +102,17 @@ EXPORTS += [
 EXPORTS.mozilla += [
     'ArenaObjectID.h',
     'ArenaRefPtr.h',
     'ArenaRefPtrInlines.h',
     'GeometryUtils.h',
     'PaintTracker.h',
     'RestyleLogging.h',
     'RestyleManager.h',
+    'RestyleManagerBase.h',
     'RestyleManagerHandle.h',
     'RestyleManagerHandleInlines.h',
     'ServoRestyleManager.h',
     'StaticPresData.h',
 ]
 
 UNIFIED_SOURCES += [
     'AccessibleCaret.cpp',
@@ -149,16 +150,17 @@ UNIFIED_SOURCES += [
     'nsPresContext.cpp',
     'nsPresShell.cpp',
     'nsQuoteList.cpp',
     'nsStyleChangeList.cpp',
     'nsStyleSheetService.cpp',
     'PaintTracker.cpp',
     'PositionedEventTargeting.cpp',
     'RestyleManager.cpp',
+    'RestyleManagerBase.cpp',
     'RestyleTracker.cpp',
     'ScrollbarStyles.cpp',
     'ServoRestyleManager.cpp',
     'StackArena.cpp',
     'StaticPresData.cpp',
     'TouchManager.cpp',
     'ZoomConstraintsClient.cpp',
 ]
--- a/layout/base/nsCSSRenderingBorders.cpp
+++ b/layout/base/nsCSSRenderingBorders.cpp
@@ -16,16 +16,17 @@
 #include "DottedCornerFinder.h"
 #include "nsLayoutUtils.h"
 #include "nsStyleConsts.h"
 #include "nsContentUtils.h"
 #include "nsCSSColorUtils.h"
 #include "GeckoProfiler.h"
 #include "nsExpirationTracker.h"
 #include "RoundedRect.h"
+#include "nsIScriptError.h"
 #include "nsClassHashtable.h"
 #include "nsPresContext.h"
 #include "nsStyleStruct.h"
 #include "mozilla/gfx/2D.h"
 #include "gfx2DGlue.h"
 #include "gfxGradientCache.h"
 #include <algorithm>
 
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -3677,33 +3677,28 @@ FlushLayoutRecursive(nsIDocument* aDocum
   return true;
 }
 
 void
 PresShell::DispatchSynthMouseMove(WidgetGUIEvent* aEvent,
                                   bool aFlushOnHoverChange)
 {
   RestyleManagerHandle restyleManager = mPresContext->RestyleManager();
-  if (restyleManager->IsServo()) {
-    NS_ERROR("stylo: cannot dispatch synthetic mouse moves when using a "
-             "ServoRestyleManager yet");
-    return;
-  }
   uint32_t hoverGenerationBefore =
-    restyleManager->AsGecko()->GetHoverGeneration();
+    restyleManager->GetHoverGeneration();
   nsEventStatus status;
   nsView* targetView = nsView::GetViewFor(aEvent->mWidget);
   if (!targetView)
     return;
   targetView->GetViewManager()->DispatchEvent(aEvent, targetView, &status);
   if (MOZ_UNLIKELY(mIsDestroying)) {
     return;
   }
   if (aFlushOnHoverChange &&
-      hoverGenerationBefore != restyleManager->AsGecko()->GetHoverGeneration()) {
+      hoverGenerationBefore != restyleManager->GetHoverGeneration()) {
     // Flush so that the resulting reflow happens now so that our caller
     // can suppress any synthesized mouse moves caused by that reflow.
     // This code only ever runs for the root document, but :hover changes
     // can happen in descendant documents too, so make sure we flush
     // all of them.
     FlushLayoutRecursive(mDocument);
   }
 }
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -1741,20 +1741,17 @@ nsRefreshDriver::Tick(int64_t aNowEpoch,
             profiler_tracing("Paint", "Styles", mStyleCause, TRACING_INTERVAL_START);
             mStyleCause = nullptr;
           }
 
           NS_ADDREF(shell);
           mStyleFlushObservers.RemoveElement(shell);
           RestyleManagerHandle restyleManager =
             shell->GetPresContext()->RestyleManager();
-          // XXX stylo: ServoRestyleManager does not observer the refresh driver yet.
-          if (restyleManager->IsGecko()) {
-            restyleManager->AsGecko()->mObservingRefreshDriver = false;
-          }
+          restyleManager->SetObservingRefreshDriver(false);
           shell->FlushPendingNotifications(ChangesToFlush(Flush_Style, false));
           // Inform the FontFaceSet that we ticked, so that it can resolve its
           // ready promise if it needs to (though it might still be waiting on
           // a layout flush).
           nsPresContext* presContext = shell->GetPresContext();
           if (presContext) {
             presContext->NotifyFontFaceSetOnRefresh();
           }