Bug 1367553: Move dirty style tracking to the StyleSet. r=heycam draft
authorEmilio Cobos Álvarez <emilio@crisal.io>
Wed, 24 May 2017 02:53:08 +0200 (2017-05-24)
changeset 584467 d9f7d7f1da7b38e595021795ee902e63746c8161
parent 584466 d8e867982af710adaade6dce3acb14ccdc93218d
child 584468 386a1b9fe5ffc5fefbf20678068573ea195043f0
push id60745
push userbmo:emilio+bugs@crisal.io
push dateThu, 25 May 2017 14:13:21 +0000 (2017-05-25)
reviewersheycam
bugs1367553
milestone55.0a1
Bug 1367553: Move dirty style tracking to the StyleSet. r=heycam MozReview-Commit-ID: 2ut8SAwNGN2
layout/base/PresShell.cpp
layout/base/nsIPresShell.h
layout/style/ServoStyleSet.cpp
layout/style/ServoStyleSet.h
layout/style/StyleSetHandle.h
layout/style/StyleSetHandleInlines.h
layout/style/nsStyleSet.cpp
layout/style/nsStyleSet.h
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -47,17 +47,16 @@
 
 #include "gfxPrefs.h"
 #include "gfxUserFontSet.h"
 #include "nsPresContext.h"
 #include "nsIContent.h"
 #include "nsIContentIterator.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Event.h" // for Event::GetEventPopupControlState()
-#include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/dom/PointerEvent.h"
 #include "nsIDocument.h"
 #include "nsAnimationManager.h"
 #include "nsNameSpaceManager.h"  // for Pref-related rule management (bugs 22963,20760,31816)
 #include "nsFrame.h"
 #include "FrameLayerBuilder.h"
 #include "nsViewManager.h"
 #include "nsView.h"
@@ -779,17 +778,16 @@ nsIPresShell::nsIPresShell()
 #ifdef DEBUG
     , mDrawEventTargetFrame(nullptr)
 #endif
     , mPaintCount(0)
     , mAutoWeakFrames(nullptr)
     , mCanvasBackgroundColor(NS_RGBA(0,0,0,0))
     , mSelectionFlags(0)
     , mRenderFlags(0)
-    , mStylesHaveChanged(false)
     , mDidInitialize(false)
     , mIsDestroying(false)
     , mIsReflowing(false)
     , mPaintingSuppressed(false)
     , mIsThemeSupportDisabled(false)
     , mIsActive(false)
     , mFrozen(false)
     , mIsFirstPaint(false)
@@ -1539,17 +1537,16 @@ PresShell::AddUserSheet(StyleSheet* aShe
 
   // Now iterate backwards, so that the order of userSheets will be the same as
   // the order of sheets from it in the style set.
   for (StyleSheet* sheet : Reversed(userSheets)) {
     mStyleSet->PrependStyleSheet(SheetType::User, sheet);
   }
 
   mStyleSet->EndUpdate();
-
   RestyleForCSSRuleChanges();
 }
 
 void
 PresShell::AddAgentSheet(StyleSheet* aSheet)
 {
   // Make sure this does what nsDocumentViewer::CreateStyleSet does
   // wrt ordering.
@@ -2530,18 +2527,19 @@ PresShell::EndUpdate(nsIDocument *aDocum
 {
 #ifdef DEBUG
   NS_PRECONDITION(0 != mUpdateCount, "too many EndUpdate's");
   --mUpdateCount;
 #endif
 
   if (aUpdateType & UPDATE_STYLE) {
     mStyleSet->EndUpdate();
-    if (mStylesHaveChanged || !mChangedScopeStyleRoots.IsEmpty())
+    if (mStyleSet->StyleSheetsHaveChanged()) {
       RestyleForCSSRuleChanges();
+    }
   }
 
   mFrameConstructor->EndUpdate();
 }
 
 void
 PresShell::RestoreRootScrollPosition()
 {
@@ -4541,93 +4539,43 @@ PresShell::ReconstructFrames()
   mFrameConstructor->ReconstructDocElementHierarchy();
   VERIFY_STYLE_TREE;
   mFrameConstructor->EndUpdate();
 }
 
 void
 nsIPresShell::RestyleForCSSRuleChanges()
 {
-  AutoTArray<RefPtr<mozilla::dom::Element>,1> scopeRoots;
-  mChangedScopeStyleRoots.SwapElements(scopeRoots);
-
-  if (mStylesHaveChanged) {
-    // If we need to restyle everything, no need to restyle individual
-    // scoped style roots.
-    scopeRoots.Clear();
-  }
-
-  mStylesHaveChanged = false;
-
   if (mIsDestroying) {
     // We don't want to mess with restyles at this point
     return;
   }
 
   mDocument->RebuildUserFontSet();
 
   if (mPresContext) {
     mPresContext->RebuildCounterStyles();
   }
 
-  Element* root = mDocument->GetRootElement();
   if (!mDidInitialize) {
     // Nothing to do here, since we have no frames yet
     return;
   }
 
-  if (!root) {
-    // No content to restyle
-    return;
-  }
-
-  RestyleManager* restyleManager = mPresContext->RestyleManager();
-
-  if (scopeRoots.IsEmpty()) {
-    // If scopeRoots is empty, we know that mStylesHaveChanged was true at
-    // the beginning of this function, and that we need to restyle the whole
-    // document.
-    restyleManager->PostRestyleEventForCSSRuleChanges(root,
-                                                      eRestyle_Subtree,
-                                                      nsChangeHint(0));
-  } else {
-    for (Element* scopeRoot : scopeRoots) {
-      restyleManager->PostRestyleEventForCSSRuleChanges(scopeRoot,
-                                                        eRestyle_Subtree,
-                                                        nsChangeHint(0));
-    }
-  }
+  mStyleSet->InvalidateStyleForCSSRuleChanges();
 }
 
 void
 PresShell::RecordStyleSheetChange(StyleSheet* aStyleSheet)
 {
   // too bad we can't check that the update is UPDATE_STYLE
   NS_ASSERTION(mUpdateCount != 0, "must be in an update");
-
-  if (mStylesHaveChanged)
-    return;
-
-  // Tell Servo that the contents of style sheets have changed.
-  if (ServoStyleSet* set = mStyleSet->GetAsServo()) {
-    set->NoteStyleSheetsChanged();
-  }
-
-  if (aStyleSheet->IsGecko()) {
-    // XXXheycam ServoStyleSheets don't support <style scoped> yet.
-    Element* scopeElement = aStyleSheet->AsGecko()->GetScopeElement();
-    if (scopeElement) {
-      mChangedScopeStyleRoots.AppendElement(scopeElement);
-      return;
-    }
-  } else {
-    NS_WARNING("stylo: ServoStyleSheets don't support <style scoped>");
-  }
-
-  mStylesHaveChanged = true;
+  MOZ_ASSERT(aStyleSheet->IsServo() == mStyleSet->IsServo());
+
+  mStyleSet->RecordStyleSheetChange(aStyleSheet);
 }
 
 void
 PresShell::StyleSheetAdded(StyleSheet* aStyleSheet,
                            bool aDocumentSheet)
 {
   // We only care when enabled sheets are added
   NS_PRECONDITION(aStyleSheet, "Must have a style sheet!");
@@ -6347,17 +6295,17 @@ public:
 private:
   PresShell* mShell;
   uint32_t mFlags;
 };
 
 void
 PresShell::RecordShadowStyleChange(ShadowRoot* aShadowRoot)
 {
-  mChangedScopeStyleRoots.AppendElement(aShadowRoot->GetHost()->AsElement());
+  mStyleSet->RecordShadowStyleChange(aShadowRoot);
 }
 
 void
 PresShell::Paint(nsView*         aViewToPaint,
                  const nsRegion& aDirtyRegion,
                  uint32_t        aFlags)
 {
 #ifdef MOZ_GECKO_PROFILER
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -1857,21 +1857,16 @@ protected:
   int16_t                   mSelectionFlags;
 
   // Flags controlling how our document is rendered.  These persist
   // between paints and so are tied with retained layer pixels.
   // PresShell flushes retained layers when the rendering state
   // changes in a way that prevents us from being able to (usefully)
   // re-use old pixels.
   RenderFlags               mRenderFlags;
-
-  // Indicates that the whole document must be restyled.  Changes to scoped
-  // style sheets are recorded in mChangedScopeStyleRoots rather than here
-  // in mStylesHaveChanged.
-  bool                      mStylesHaveChanged : 1;
   bool                      mDidInitialize : 1;
   bool                      mIsDestroying : 1;
   bool                      mIsReflowing : 1;
 
   // For all documents we initially lock down painting.
   bool                      mPaintingSuppressed : 1;
 
   // Whether or not form controls should use nsITheme in this shell.
@@ -1901,25 +1896,16 @@ protected:
   bool mObservingLayoutFlushes: 1;
 
   // True if there are throttled animations that would be processed when
   // performing a flush with mFlushAnimations == true.
   bool mNeedThrottledAnimationFlush : 1;
 
   uint32_t                  mPresShellId;
 
-  // List of subtrees rooted at style scope roots that need to be restyled.
-  // When a change to a scoped style sheet is made, we add the style scope
-  // root to this array rather than setting mStylesHaveChanged = true, since
-  // we know we don't need to restyle the whole document.  However, if in the
-  // same update block we have already had other changes that require
-  // the whole document to be restyled (i.e., mStylesHaveChanged is already
-  // true), then we don't bother adding the scope root here.
-  AutoTArray<RefPtr<mozilla::dom::Element>,1> mChangedScopeStyleRoots;
-
   static nsIContent*        gKeyDownTarget;
 
   // Cached font inflation values. This is done to prevent changing of font
   // inflation until a page is reloaded.
   uint32_t mFontSizeInflationEmPerLine;
   uint32_t mFontSizeInflationMinTwips;
   uint32_t mFontSizeInflationLineThreshold;
   bool mFontSizeInflationForceEnabled;
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -120,16 +120,25 @@ void
 ServoStyleSet::Shutdown()
 {
   // Make sure we drop our cached style contexts before the presshell arena
   // starts going away.
   ClearNonInheritingStyleContexts();
   mRawSet = nullptr;
 }
 
+void
+ServoStyleSet::InvalidateStyleForCSSRuleChanges()
+{
+  if (Element* root = mPresContext->Document()->GetRootElement()) {
+    mPresContext->RestyleManager()->PostRestyleEventForCSSRuleChanges(
+        root, eRestyle_Subtree, nsChangeHint(0));
+  }
+}
+
 size_t
 ServoStyleSet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t n = aMallocSizeOf(this);
 
   // Measurement of the following members may be added later if DMD finds it is
   // worthwhile:
   // - mRawSet
--- a/layout/style/ServoStyleSet.h
+++ b/layout/style/ServoStyleSet.h
@@ -100,16 +100,34 @@ public:
 
   ServoStyleSet();
   ~ServoStyleSet();
 
   void Init(nsPresContext* aPresContext);
   void BeginShutdown();
   void Shutdown();
 
+  void RecordStyleSheetChange(mozilla::ServoStyleSheet*)
+  {
+    NoteStyleSheetsChanged();
+  }
+
+  void RecordShadowStyleChange(mozilla::dom::ShadowRoot* aShadowRoot) {
+    // FIXME(emilio): When we properly support shadow dom we'll need to do
+    // better.
+    NoteStyleSheetsChanged();
+  }
+
+  bool StyleSheetsHaveChanged() const
+  {
+    return StylistNeedsUpdate();
+  }
+
+  void InvalidateStyleForCSSRuleChanges();
+
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
   const RawServoStyleSet& RawSet() const { return *mRawSet; }
 
   bool GetAuthorStyleDisabled() const;
   nsresult SetAuthorStyleDisabled(bool aStyleDisabled);
 
   void BeginUpdate();
   nsresult EndUpdate();
--- a/layout/style/StyleSetHandle.h
+++ b/layout/style/StyleSetHandle.h
@@ -17,16 +17,17 @@
 #include "nsCSSPseudoElements.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 class CSSStyleSheet;
 class ServoStyleSet;
 namespace dom {
 class Element;
+class ShadowRoot;
 } // namespace dom
 } // namespace mozilla
 class nsCSSCounterStyleRule;
 struct nsFontFaceRuleContainer;
 class nsIAtom;
 class nsIContent;
 class nsIDocument;
 class nsStyleContext;
@@ -148,16 +149,20 @@ public:
                            const nsTArray<RefPtr<StyleSheet>>& aNewSheets);
     inline nsresult InsertStyleSheetBefore(SheetType aType,
                                     StyleSheet* aNewSheet,
                                     StyleSheet* aReferenceSheet);
     inline int32_t SheetCount(SheetType aType) const;
     inline StyleSheet* StyleSheetAt(SheetType aType, int32_t aIndex) const;
     inline nsresult RemoveDocStyleSheet(StyleSheet* aSheet);
     inline nsresult AddDocStyleSheet(StyleSheet* aSheet, nsIDocument* aDocument);
+    inline void RecordStyleSheetChange(StyleSheet* aSheet);
+    inline void RecordShadowStyleChange(mozilla::dom::ShadowRoot* aShadowRoot);
+    inline bool StyleSheetsHaveChanged() const;
+    inline void InvalidateStyleForCSSRuleChanges();
     inline already_AddRefed<nsStyleContext>
     ProbePseudoElementStyle(dom::Element* aParentElement,
                             mozilla::CSSPseudoElementType aType,
                             nsStyleContext* aParentContext);
     inline already_AddRefed<nsStyleContext>
     ProbePseudoElementStyle(dom::Element* aParentElement,
                             mozilla::CSSPseudoElementType aType,
                             nsStyleContext* aParentContext,
--- a/layout/style/StyleSetHandleInlines.h
+++ b/layout/style/StyleSetHandleInlines.h
@@ -215,16 +215,41 @@ StyleSetHandle::Ptr::RemoveDocStyleSheet
 nsresult
 StyleSetHandle::Ptr::AddDocStyleSheet(StyleSheet* aSheet,
                                       nsIDocument* aDocument)
 {
   FORWARD_CONCRETE(AddDocStyleSheet, (aSheet->AsGecko(), aDocument),
                                      (aSheet->AsServo(), aDocument));
 }
 
+void
+StyleSetHandle::Ptr::RecordStyleSheetChange(StyleSheet* aSheet)
+{
+  FORWARD_CONCRETE(RecordStyleSheetChange, (aSheet->AsGecko()),
+                                           (aSheet->AsServo()));
+}
+
+void
+StyleSetHandle::Ptr::RecordShadowStyleChange(mozilla::dom::ShadowRoot* aShadowRoot)
+{
+  FORWARD(RecordShadowStyleChange, (aShadowRoot));
+}
+
+bool
+StyleSetHandle::Ptr::StyleSheetsHaveChanged() const
+{
+  FORWARD(StyleSheetsHaveChanged, ());
+}
+
+void
+StyleSetHandle::Ptr::InvalidateStyleForCSSRuleChanges()
+{
+  FORWARD(InvalidateStyleForCSSRuleChanges, ());
+}
+
 // check whether there is ::before/::after style for an element
 already_AddRefed<nsStyleContext>
 StyleSetHandle::Ptr::ProbePseudoElementStyle(dom::Element* aParentElement,
                                              CSSPseudoElementType aType,
                                              nsStyleContext* aParentContext)
 {
   FORWARD(ProbePseudoElementStyle, (aParentElement, aType, aParentContext));
 }
--- a/layout/style/nsStyleSet.cpp
+++ b/layout/style/nsStyleSet.cpp
@@ -28,16 +28,17 @@
 #include "nsCSSRuleProcessor.h"
 #include "nsDataHashtable.h"
 #include "nsIContent.h"
 #include "nsRuleData.h"
 #include "nsRuleProcessorData.h"
 #include "nsAnimationManager.h"
 #include "nsStyleSheetService.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/ShadowRoot.h"
 #include "GeckoProfiler.h"
 #include "nsHTMLCSSStyleSheet.h"
 #include "nsHTMLStyleSheet.h"
 #include "nsCSSRules.h"
 #include "nsPrintfCString.h"
 #include "nsIFrame.h"
 #include "mozilla/RestyleManager.h"
 #include "mozilla/RestyleManagerInlines.h"
@@ -205,16 +206,17 @@ nsStyleSet::IsCSSSheetType(SheetType aSh
     }
   }
   return false;
 }
 
 nsStyleSet::nsStyleSet()
   : mRuleTree(nullptr),
     mBatching(0),
+    mStylesHaveChanged(0),
     mInShutdown(false),
     mInGC(false),
     mAuthorStyleDisabled(false),
     mInReconstruct(false),
     mInitFontFeatureValuesLookup(true),
     mNeedsRestyleAfterEnsureUniqueInner(false),
     mDirty(0),
     mRootStyleContextCount(0),
@@ -2361,16 +2363,78 @@ nsStyleSet::Shutdown()
   // starts going away.
   ClearNonInheritingStyleContexts();
   mRuleTree = nullptr;
   GCRuleTrees();
   MOZ_ASSERT(mUnusedRuleNodeList.isEmpty());
   MOZ_ASSERT(mUnusedRuleNodeCount == 0);
 }
 
+void
+nsStyleSet::RecordStyleSheetChange(CSSStyleSheet* aStyleSheet)
+{
+  MOZ_ASSERT(mBatching != 0, "Should be in an update");
+
+  if (mStylesHaveChanged) {
+    return;
+  }
+
+  if (Element* scopeElement = aStyleSheet->GetScopeElement()) {
+    mChangedScopeStyleRoots.AppendElement(scopeElement);
+    return;
+  }
+
+  mStylesHaveChanged = true;
+  // If we need to restyle everything, no need to restyle individual
+  // scoped style roots.
+  mChangedScopeStyleRoots.Clear();
+}
+
+void
+nsStyleSet::RecordShadowStyleChange(ShadowRoot* aShadowRoot)
+{
+  if (mStylesHaveChanged) {
+    return;
+  }
+
+  mChangedScopeStyleRoots.AppendElement(aShadowRoot->GetHost()->AsElement());
+}
+
+void
+nsStyleSet::InvalidateStyleForCSSRuleChanges()
+{
+  MOZ_ASSERT_IF(mStylesHaveChanged, mChangedScopeStyleRoots.IsEmpty());
+
+  AutoTArray<RefPtr<mozilla::dom::Element>, 1> scopeRoots;
+  mChangedScopeStyleRoots.SwapElements(scopeRoots);
+  mStylesHaveChanged = false;
+
+  nsPresContext* presContext = PresContext();
+  RestyleManager* restyleManager = presContext->RestyleManager();
+  Element* root = presContext->Document()->GetRootElement();
+  if (!root) {
+    // No content to restyle
+    return;
+  }
+
+  if (scopeRoots.IsEmpty()) {
+    // If scopeRoots is empty, we know that mStylesHaveChanged was true at
+    // the beginning of this function, and that we need to restyle the whole
+    // document.
+    restyleManager->PostRestyleEventForCSSRuleChanges(root,
+                                                      eRestyle_Subtree,
+                                                      nsChangeHint(0));
+  } else {
+    for (Element* scopeRoot : scopeRoots) {
+      restyleManager->PostRestyleEventForCSSRuleChanges(scopeRoot,
+                                                        eRestyle_Subtree,
+                                                        nsChangeHint(0));
+    }
+  }
+}
 
 void
 nsStyleSet::GCRuleTrees()
 {
   MOZ_ASSERT(!mInReconstruct);
   MOZ_ASSERT(!mInGC);
   mInGC = true;
 
--- a/layout/style/nsStyleSet.h
+++ b/layout/style/nsStyleSet.h
@@ -36,18 +36,21 @@ class nsCSSCounterStyleRule;
 class nsICSSPseudoComparator;
 class nsRuleWalker;
 struct ElementDependentRuleProcessorData;
 struct nsFontFaceRuleContainer;
 struct TreeMatchContext;
 
 namespace mozilla {
 class CSSStyleSheet;
+enum class CSSPseudoElementType : uint8_t;
 class EventStates;
-enum class CSSPseudoElementType : uint8_t;
+namespace dom {
+class ShadowRoot;
+} // namespace dom
 } // namespace mozilla
 
 class nsEmptyStyleRule final : public nsIStyleRule
 {
 private:
   ~nsEmptyStyleRule() {}
 
 public:
@@ -324,16 +327,29 @@ class nsStyleSet final
 
   // Begin ignoring style context destruction, to avoid lots of unnecessary
   // work on document teardown.
   void BeginShutdown();
 
   // Free all of the data associated with this style set.
   void Shutdown();
 
+  // Notes that a style sheet has changed.
+  void RecordStyleSheetChange(mozilla::CSSStyleSheet* aStyleSheet);
+
+  // Notes that style sheets have changed in a shadow root.
+  void RecordShadowStyleChange(mozilla::dom::ShadowRoot* aShadowRoot);
+
+  bool StyleSheetsHaveChanged() const
+  {
+    return mStylesHaveChanged || !mChangedScopeStyleRoots.IsEmpty();
+  }
+
+  void InvalidateStyleForCSSRuleChanges();
+
   // Get a new style context that lives in a different parent
   // The new context will be the same as the old if the new parent is the
   // same as the old parent.
   // aElement should be non-null if this is a style context for an
   // element or pseudo-element; in the latter case it should be the
   // real element the pseudo-element is for.
   already_AddRefed<nsStyleContext>
   ReparentStyleContext(nsStyleContext* aStyleContext,
@@ -598,18 +614,31 @@ private:
   nsTArray<nsCOMPtr<nsIStyleRuleProcessor> > mScopedDocSheetRuleProcessors;
 
   RefPtr<nsBindingManager> mBindingManager;
 
   RefPtr<nsRuleNode> mRuleTree; // This is the root of our rule tree.  It is a
                                 // lexicographic tree of matched rules that style
                                 // contexts use to look up properties.
 
+  // List of subtrees rooted at style scope roots that need to be restyled.
+  // When a change to a scoped style sheet is made, we add the style scope
+  // root to this array rather than setting mStylesHaveChanged = true, since
+  // we know we don't need to restyle the whole document.  However, if in the
+  // same update block we have already had other changes that require
+  // the whole document to be restyled (i.e., mStylesHaveChanged is already
+  // true), then we don't bother adding the scope root here.
+  AutoTArray<RefPtr<mozilla::dom::Element>,1> mChangedScopeStyleRoots;
+
   uint16_t mBatching;
 
+  // Indicates that the whole document must be restyled.  Changes to scoped
+  // style sheets are recorded in mChangedScopeStyleRoots rather than here
+  // in mStylesHaveChanged.
+  unsigned mStylesHaveChanged : 1;
   unsigned mInShutdown : 1;
   unsigned mInGC : 1;
   unsigned mAuthorStyleDisabled: 1;
   unsigned mInReconstruct : 1;
   unsigned mInitFontFeatureValuesLookup : 1;
   unsigned mNeedsRestyleAfterEnsureUniqueInner : 1;
   unsigned mDirty : int(mozilla::SheetType::Count);  // one bit per sheet type