Bug 1170888 - Restyle the document in EnsureSafeToHandOutCSSRules if we previously cloned sheet inners outside of that method. r=bzbarsky
authorCameron McCormack <cam@mcc.id.au>
Fri, 26 Jun 2015 13:49:58 +1000
changeset 281073 3c5dac5653797c997cda7d561adfd13c4333d90a
parent 281072 368c057b5b00b22267856f6c162c9f04a238060b
child 281074 726a06ab93c59c71a89e671bf4a336ed9b47a5d1
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs1170888
milestone41.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 1170888 - Restyle the document in EnsureSafeToHandOutCSSRules if we previously cloned sheet inners outside of that method. r=bzbarsky
layout/base/nsPresContext.cpp
layout/style/CSSStyleSheet.cpp
layout/style/CSSStyleSheet.h
layout/style/nsStyleSet.cpp
layout/style/nsStyleSet.h
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -2327,24 +2327,21 @@ nsPresContext::NotifyMissingFonts()
   if (mMissingFonts) {
     mMissingFonts->Flush();
   }
 }
 
 void
 nsPresContext::EnsureSafeToHandOutCSSRules()
 {
-  CSSStyleSheet::EnsureUniqueInnerResult res =
-    mShell->StyleSet()->EnsureUniqueInnerOnCSSSheets();
-  if (res == CSSStyleSheet::eUniqueInner_AlreadyUnique) {
+  if (!mShell->StyleSet()->EnsureUniqueInnerOnCSSSheets()) {
     // Nothing to do.
     return;
   }
 
-  MOZ_ASSERT(res == CSSStyleSheet::eUniqueInner_ClonedInner);
   RebuildAllStyleData(nsChangeHint(0), eRestyle_Subtree);
 }
 
 void
 nsPresContext::FireDOMPaintEvent(nsInvalidateRequestList* aList)
 {
   nsPIDOMWindow* ourWindow = mDocument->GetWindow();
   if (!ourWindow)
--- a/layout/style/CSSStyleSheet.cpp
+++ b/layout/style/CSSStyleSheet.cpp
@@ -1181,16 +1181,30 @@ CSSStyleSheet::DropRuleProcessor(nsCSSRu
 {
   if (!mRuleProcessors)
     return NS_ERROR_FAILURE;
   return mRuleProcessors->RemoveElement(aProcessor)
            ? NS_OK
            : NS_ERROR_FAILURE;
 }
 
+void
+CSSStyleSheet::AddStyleSet(nsStyleSet* aStyleSet)
+{
+  NS_ASSERTION(!mStyleSets.Contains(aStyleSet),
+               "style set already registered");
+  mStyleSets.AppendElement(aStyleSet);
+}
+
+void
+CSSStyleSheet::DropStyleSet(nsStyleSet* aStyleSet)
+{
+  DebugOnly<bool> found = mStyleSets.RemoveElement(aStyleSet);
+  NS_ASSERTION(found, "didn't find style set");
+}
 
 void
 CSSStyleSheet::SetURIs(nsIURI* aSheetURI, nsIURI* aOriginalSheetURI,
                        nsIURI* aBaseURI)
 {
   NS_PRECONDITION(aSheetURI && aBaseURI, "null ptr");
 
   NS_ASSERTION(mInner->mOrderedRules.Count() == 0 && !mInner->mComplete,
@@ -1482,35 +1496,41 @@ CSSStyleSheet::StyleSheetCount() const
   while (child) {
     count++;
     child = child->mNext;
   }
 
   return count;
 }
 
-CSSStyleSheet::EnsureUniqueInnerResult
+void
 CSSStyleSheet::EnsureUniqueInner()
 {
   mDirty = true;
 
   MOZ_ASSERT(mInner->mSheets.Length() != 0,
              "unexpected number of outers");
   if (mInner->mSheets.Length() == 1) {
-    return eUniqueInner_AlreadyUnique;
+    // already unique
+    return;
   }
   CSSStyleSheetInner* clone = mInner->CloneFor(this);
   MOZ_ASSERT(clone);
   mInner->RemoveSheet(this);
   mInner = clone;
 
   // otherwise the rule processor has pointers to the old rules
   ClearRuleCascades();
 
-  return eUniqueInner_ClonedInner;
+  // let our containing style sets know that if we call
+  // nsPresContext::EnsureSafeToHandOutCSSRules we will need to restyle the
+  // document
+  for (nsStyleSet* styleSet : mStyleSets) {
+    styleSet->SetNeedsRestyleAfterEnsureUniqueInner();
+  }
 }
 
 void
 CSSStyleSheet::AppendAllChildSheets(nsTArray<CSSStyleSheet*>& aArray)
 {
   for (CSSStyleSheet* child = mInner->mFirstChild; child;
        child = child->mNext) {
     aArray.AppendElement(child);
--- a/layout/style/CSSStyleSheet.h
+++ b/layout/style/CSSStyleSheet.h
@@ -207,38 +207,34 @@ public:
     NS_ASSERTION(mDirty,
                  "sheet must be marked dirty before handing out child rules");
     DidDirty();
   }
 
   nsresult AddRuleProcessor(nsCSSRuleProcessor* aProcessor);
   nsresult DropRuleProcessor(nsCSSRuleProcessor* aProcessor);
 
+  void AddStyleSet(nsStyleSet* aStyleSet);
+  void DropStyleSet(nsStyleSet* aStyleSet);
+
   /**
    * Like the DOM insertRule() method, but doesn't do any security checks
    */
   nsresult InsertRuleInternal(const nsAString& aRule,
                               uint32_t aIndex, uint32_t* aReturn);
 
   /* Get the URI this sheet was originally loaded from, if any.  Can
      return null */
   virtual nsIURI* GetOriginalURI() const;
 
   // nsICSSLoaderObserver interface
   NS_IMETHOD StyleSheetLoaded(CSSStyleSheet* aSheet, bool aWasAlternate,
                               nsresult aStatus) override;
 
-  enum EnsureUniqueInnerResult {
-    // No work was needed to ensure a unique inner.
-    eUniqueInner_AlreadyUnique,
-    // A clone was done to ensure a unique inner (which means the style
-    // rules in this sheet have changed).
-    eUniqueInner_ClonedInner
-  };
-  EnsureUniqueInnerResult EnsureUniqueInner();
+  void EnsureUniqueInner();
 
   // Append all of this sheet's child sheets to aArray.
   void AppendAllChildSheets(nsTArray<CSSStyleSheet*>& aArray);
 
   bool UseForPresentation(nsPresContext* aPresContext,
                             nsMediaQueryResultCacheKey& aKey) const;
 
   nsresult ParseSheet(const nsAString& aInput);
@@ -361,16 +357,17 @@ protected:
   nsINode*              mOwningNode; // weak ref
   bool                  mDisabled;
   bool                  mDirty; // has been modified 
   nsRefPtr<dom::Element> mScopeElement;
 
   CSSStyleSheetInner*   mInner;
 
   nsAutoTArray<nsCSSRuleProcessor*, 8>* mRuleProcessors;
+  nsTArray<nsStyleSet*> mStyleSets;
 
   friend class ::nsMediaList;
   friend class ::nsCSSRuleProcessor;
   friend struct mozilla::ChildSheetListBuilder;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(CSSStyleSheet, NS_CSS_STYLE_SHEET_IMPL_CID)
 
--- a/layout/style/nsStyleSet.cpp
+++ b/layout/style/nsStyleSet.cpp
@@ -146,28 +146,48 @@ static const nsStyleSet::sheetType gCSSS
   // From lowest to highest in cascading order.
   nsStyleSet::eAgentSheet,
   nsStyleSet::eUserSheet,
   nsStyleSet::eDocSheet,
   nsStyleSet::eScopedDocSheet,
   nsStyleSet::eOverrideSheet
 };
 
+static bool IsCSSSheetType(nsStyleSet::sheetType aSheetType)
+{
+  for (nsStyleSet::sheetType type : gCSSSheetTypes) {
+    if (type == aSheetType) {
+      return true;
+    }
+  }
+  return false;
+}
+
 nsStyleSet::nsStyleSet()
   : mRuleTree(nullptr),
     mBatching(0),
     mInShutdown(false),
     mAuthorStyleDisabled(false),
     mInReconstruct(false),
     mInitFontFeatureValuesLookup(true),
+    mNeedsRestyleAfterEnsureUniqueInner(false),
     mDirty(0),
     mUnusedRuleNodeCount(0)
 {
 }
 
+nsStyleSet::~nsStyleSet()
+{
+  for (sheetType type : gCSSSheetTypes) {
+    for (uint32_t i = 0, n = mSheets[type].Length(); i < n; i++) {
+      static_cast<CSSStyleSheet*>(mSheets[type][i])->DropStyleSet(this);
+    }
+  }
+}
+
 size_t
 nsStyleSet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t n = aMallocSizeOf(this);
 
   for (int i = 0; i < eSheetTypeCount; i++) {
     if (mRuleProcessors[i]) {
       n += mRuleProcessors[i]->SizeOfIncludingThis(aMallocSizeOf);
@@ -495,74 +515,103 @@ IsScopedStyleSheet(nsIStyleSheet* aSheet
 }
 
 nsresult
 nsStyleSet::AppendStyleSheet(sheetType aType, nsIStyleSheet *aSheet)
 {
   NS_PRECONDITION(aSheet, "null arg");
   NS_ASSERTION(aSheet->IsApplicable(),
                "Inapplicable sheet being placed in style set");
-  mSheets[aType].RemoveObject(aSheet);
+  bool present = mSheets[aType].RemoveObject(aSheet);
   if (!mSheets[aType].AppendObject(aSheet))
     return NS_ERROR_OUT_OF_MEMORY;
 
+  if (!present && IsCSSSheetType(aType)) {
+    static_cast<CSSStyleSheet*>(aSheet)->AddStyleSet(this);
+  }
+
   return DirtyRuleProcessors(aType);
 }
 
 nsresult
 nsStyleSet::PrependStyleSheet(sheetType aType, nsIStyleSheet *aSheet)
 {
   NS_PRECONDITION(aSheet, "null arg");
   NS_ASSERTION(aSheet->IsApplicable(),
                "Inapplicable sheet being placed in style set");
-  mSheets[aType].RemoveObject(aSheet);
+  bool present = mSheets[aType].RemoveObject(aSheet);
   if (!mSheets[aType].InsertObjectAt(aSheet, 0))
     return NS_ERROR_OUT_OF_MEMORY;
 
+  if (!present && IsCSSSheetType(aType)) {
+    static_cast<CSSStyleSheet*>(aSheet)->AddStyleSet(this);
+  }
+
   return DirtyRuleProcessors(aType);
 }
 
 nsresult
 nsStyleSet::RemoveStyleSheet(sheetType aType, nsIStyleSheet *aSheet)
 {
   NS_PRECONDITION(aSheet, "null arg");
   NS_ASSERTION(aSheet->IsComplete(),
                "Incomplete sheet being removed from style set");
-  mSheets[aType].RemoveObject(aSheet);
+  if (mSheets[aType].RemoveObject(aSheet)) {
+    if (IsCSSSheetType(aType)) {
+      static_cast<CSSStyleSheet*>(aSheet)->DropStyleSet(this);
+    }
+  }
 
   return DirtyRuleProcessors(aType);
 }
 
 nsresult
 nsStyleSet::ReplaceSheets(sheetType aType,
                           const nsCOMArray<nsIStyleSheet> &aNewSheets)
 {
+  bool cssSheetType = IsCSSSheetType(aType);
+  if (cssSheetType) {
+    for (uint32_t i = 0, n = mSheets[aType].Length(); i < n; i++) {
+      static_cast<CSSStyleSheet*>(mSheets[aType][i])->DropStyleSet(this);
+    }
+  }
+
   mSheets[aType].Clear();
   if (!mSheets[aType].AppendObjects(aNewSheets))
     return NS_ERROR_OUT_OF_MEMORY;
 
+  if (cssSheetType) {
+    for (uint32_t i = 0, n = mSheets[aType].Length(); i < n; i++) {
+      static_cast<CSSStyleSheet*>(mSheets[aType][i])->AddStyleSet(this);
+    }
+  }
+
   return DirtyRuleProcessors(aType);
 }
 
 nsresult
 nsStyleSet::InsertStyleSheetBefore(sheetType aType, nsIStyleSheet *aNewSheet,
                                    nsIStyleSheet *aReferenceSheet)
 {
   NS_PRECONDITION(aNewSheet && aReferenceSheet, "null arg");
   NS_ASSERTION(aNewSheet->IsApplicable(),
                "Inapplicable sheet being placed in style set");
 
-  mSheets[aType].RemoveObject(aNewSheet);
+  bool present = mSheets[aType].RemoveObject(aNewSheet);
   int32_t idx = mSheets[aType].IndexOf(aReferenceSheet);
   if (idx < 0)
     return NS_ERROR_INVALID_ARG;
 
   if (!mSheets[aType].InsertObjectAt(aNewSheet, idx))
     return NS_ERROR_OUT_OF_MEMORY;
 
+  if (!present && IsCSSSheetType(aType)) {
+    static_cast<CSSStyleSheet*>(aNewSheet)->AddStyleSet(this);
+  }
+
   return DirtyRuleProcessors(aType);
 }
 
 nsresult
 nsStyleSet::DirtyRuleProcessors(sheetType aType)
 {
   if (!mBatching)
     return GatherRuleProcessors(aType);
@@ -600,17 +649,17 @@ nsStyleSet::AddDocStyleSheet(nsIStyleShe
   NS_ASSERTION(aSheet->IsApplicable(),
                "Inapplicable sheet being placed in style set");
 
   sheetType type = IsScopedStyleSheet(aSheet) ?
                      eScopedDocSheet :
                      eDocSheet;
   nsCOMArray<nsIStyleSheet>& sheets = mSheets[type];
 
-  sheets.RemoveObject(aSheet);
+  bool present = sheets.RemoveObject(aSheet);
   nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance();
 
   // lowest index first
   int32_t newDocIndex = aDocument->GetIndexOfStyleSheet(aSheet);
 
   int32_t count = sheets.Count();
   int32_t index;
   for (index = 0; index < count; index++) {
@@ -627,16 +676,20 @@ nsStyleSet::AddDocStyleSheet(nsIStyleShe
         ((sheetService &&
         sheetService->AuthorStyleSheets()->IndexOf(sheet) >= 0) ||
         sheet == aDocument->FirstAdditionalAuthorSheet()))
         break;
   }
   if (!sheets.InsertObjectAt(aSheet, index))
     return NS_ERROR_OUT_OF_MEMORY;
 
+  if (!present) {
+    static_cast<CSSStyleSheet*>(aSheet)->AddStyleSet(this);
+  }
+
   return DirtyRuleProcessors(type);
 }
 
 nsresult
 nsStyleSet::RemoveDocStyleSheet(nsIStyleSheet *aSheet)
 {
   nsRefPtr<CSSStyleSheet> cssSheet = do_QueryObject(aSheet);
   bool isScoped = cssSheet && cssSheet->GetScopeElement();
@@ -2305,48 +2358,45 @@ nsStyleSet::MediumFeaturesChanged()
     bool thisChanged = false;
     mBindingManager->MediumFeaturesChanged(presContext, &thisChanged);
     stylesChanged = stylesChanged || thisChanged;
   }
 
   return stylesChanged;
 }
 
-CSSStyleSheet::EnsureUniqueInnerResult
+bool
 nsStyleSet::EnsureUniqueInnerOnCSSSheets()
 {
   nsAutoTArray<CSSStyleSheet*, 32> queue;
   for (uint32_t i = 0; i < ArrayLength(gCSSSheetTypes); ++i) {
     nsCOMArray<nsIStyleSheet> &sheets = mSheets[gCSSSheetTypes[i]];
     for (uint32_t j = 0, j_end = sheets.Count(); j < j_end; ++j) {
       CSSStyleSheet* sheet = static_cast<CSSStyleSheet*>(sheets[j]);
       queue.AppendElement(sheet);
     }
   }
 
   if (mBindingManager) {
     mBindingManager->AppendAllSheets(queue);
   }
 
-  CSSStyleSheet::EnsureUniqueInnerResult res =
-    CSSStyleSheet::eUniqueInner_AlreadyUnique;
   while (!queue.IsEmpty()) {
     uint32_t idx = queue.Length() - 1;
     CSSStyleSheet* sheet = queue[idx];
     queue.RemoveElementAt(idx);
 
-    CSSStyleSheet::EnsureUniqueInnerResult sheetRes =
-      sheet->EnsureUniqueInner();
-    if (sheetRes == CSSStyleSheet::eUniqueInner_ClonedInner) {
-      res = sheetRes;
-    }
+    sheet->EnsureUniqueInner();
 
     // Enqueue all the sheet's children.
     sheet->AppendAllChildSheets(queue);
   }
+
+  bool res = mNeedsRestyleAfterEnsureUniqueInner;
+  mNeedsRestyleAfterEnsureUniqueInner = false;
   return res;
 }
 
 nsIStyleRule*
 nsStyleSet::InitialStyleRule()
 {
   if (!mInitialStyleRule) {
     mInitialStyleRule = new nsInitialStyleRule;
--- a/layout/style/nsStyleSet.h
+++ b/layout/style/nsStyleSet.h
@@ -78,20 +78,21 @@ public:
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
 };
 
 // The style set object is created by the document viewer and ownership is
 // then handed off to the PresShell.  Only the PresShell should delete a
 // style set.
 
-class nsStyleSet
+class nsStyleSet final
 {
  public:
   nsStyleSet();
+  ~nsStyleSet();
 
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
   void Init(nsPresContext *aPresContext);
 
   nsRuleNode* GetRuleTree() { return mRuleTree; }
 
   // get a style context for a non-pseudo frame.
@@ -376,17 +377,25 @@ class nsStyleSet
     ++mUnusedRuleNodeCount;
   }
 
   // Notify the style set that a rulenode that wasn't in use now is
   void RuleNodeInUse() {
     --mUnusedRuleNodeCount;
   }
 
-  mozilla::CSSStyleSheet::EnsureUniqueInnerResult EnsureUniqueInnerOnCSSSheets();
+  // Returns true if a restyle of the document is needed due to cloning
+  // sheet inners.
+  bool EnsureUniqueInnerOnCSSSheets();
+
+  // Called by CSSStyleSheet::EnsureUniqueInner to let us know it cloned
+  // its inner.
+  void SetNeedsRestyleAfterEnsureUniqueInner() {
+    mNeedsRestyleAfterEnsureUniqueInner = true;
+  }
 
   nsIStyleRule* InitialStyleRule();
 
  private:
   nsStyleSet(const nsStyleSet& aCopy) = delete;
   nsStyleSet& operator=(const nsStyleSet& aCopy) = delete;
 
   // Run mark-and-sweep GC on mRuleTree and mOldRuleTrees, based on mRoots.
@@ -454,18 +463,18 @@ class nsStyleSet
              mozilla::dom::Element* aElementForAnimation,
              uint32_t aFlags);
 
   nsPresContext* PresContext() { return mRuleTree->PresContext(); }
 
   // The sheets in each array in mSheets are stored with the most significant
   // sheet last.
   // The arrays for ePresHintSheet, eStyleAttrSheet, eTransitionSheet,
-  // and eAnimationSheet are always empty.  (FIXME:  We should reduce
-  // the storage needed for them.)
+  // eAnimationSheet and eSVGAttrAnimationSheet are always empty.
+  // (FIXME:  We should reduce the storage needed for them.)
   nsCOMArray<nsIStyleSheet> mSheets[eSheetTypeCount];
 
   // mRuleProcessors[eScopedDocSheet] is always null; rule processors
   // for scoped style sheets are stored in mScopedDocSheetRuleProcessors.
   nsCOMPtr<nsIStyleRuleProcessor> mRuleProcessors[eSheetTypeCount];
 
   // Rule processors for HTML5 scoped style sheets, one per scope.
   nsTArray<nsCOMPtr<nsIStyleRuleProcessor> > mScopedDocSheetRuleProcessors;
@@ -477,16 +486,17 @@ class nsStyleSet
                          // contexts use to look up properties.
 
   uint16_t mBatching;
 
   unsigned mInShutdown : 1;
   unsigned mAuthorStyleDisabled: 1;
   unsigned mInReconstruct : 1;
   unsigned mInitFontFeatureValuesLookup : 1;
+  unsigned mNeedsRestyleAfterEnsureUniqueInner : 1;
   unsigned mDirty : 10;  // one dirty bit is used per sheet type
 
   uint32_t mUnusedRuleNodeCount; // used to batch rule node GC
   nsTArray<nsStyleContext*> mRoots; // style contexts with no parent
 
   // Empty style rules to force things that restrict which properties
   // apply into different branches of the rule tree.
   nsRefPtr<nsEmptyStyleRule> mFirstLineRule, mFirstLetterRule, mPlaceholderRule;