Bug 1382078 Part 3 - Support media changes for XBL stylesheets. r=emilio
authorTing-Yu Lin <tlin@mozilla.com>
Sat, 02 Sep 2017 15:25:45 +0800
changeset 428301 e06654eaa8c755d6eceef8003d91b5bdb0d01a28
parent 428300 af60919d25dc5109e61c4aed6af2cb1cf560b0b8
child 428302 b58e786853341e5b280d6c0212bde732f447681d
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
bugs1382078
milestone57.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 1382078 Part 3 - Support media changes for XBL stylesheets. r=emilio MozReview-Commit-ID: EJp8EMyanBT
dom/xbl/nsBindingManager.cpp
dom/xbl/nsBindingManager.h
dom/xbl/nsXBLPrototypeBinding.cpp
dom/xbl/nsXBLPrototypeBinding.h
layout/style/ServoStyleSet.cpp
layout/style/ServoStyleSet.h
layout/style/test/mochitest.ini
--- a/dom/xbl/nsBindingManager.cpp
+++ b/dom/xbl/nsBindingManager.cpp
@@ -45,16 +45,17 @@
 
 #include "nsIScriptContext.h"
 #include "xpcpublic.h"
 #include "jswrapper.h"
 
 #include "nsThreadUtils.h"
 #include "mozilla/dom/NodeListBinding.h"
 #include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/ServoStyleSet.h"
 #include "mozilla/Unused.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 // Implement our nsISupports methods
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsBindingManager)
@@ -740,30 +741,70 @@ nsBindingManager::WalkAllRules(nsIStyleR
   });
 }
 
 bool
 nsBindingManager::MediumFeaturesChanged(nsPresContext* aPresContext)
 {
   bool rulesChanged = false;
   RefPtr<nsPresContext> presContext = aPresContext;
+  bool isStyledByServo = mDocument->IsStyledByServo();
 
   EnumerateBoundContentBindings([=, &rulesChanged](nsXBLBinding* aBinding) {
-    nsIStyleRuleProcessor* ruleProcessor =
-      aBinding->PrototypeBinding()->GetRuleProcessor();
-    if (ruleProcessor) {
-      bool thisChanged = ruleProcessor->MediumFeaturesChanged(presContext);
-      rulesChanged = rulesChanged || thisChanged;
+    if (isStyledByServo) {
+      ServoStyleSet* styleSet = aBinding->PrototypeBinding()->GetServoStyleSet();
+      if (styleSet) {
+        bool styleSetChanged = false;
+
+        if (styleSet->IsPresContextChanged(presContext)) {
+          styleSetChanged = true;
+        } else {
+          // PresContext is not changed. This means aPresContext is still
+          // alive since the last time it initialized this XBL styleset.
+          // It's safe to check whether medium features changed.
+          bool viewportUnitsUsed = false;
+          styleSetChanged =
+            styleSet->MediumFeaturesChangedRules(&viewportUnitsUsed);
+          MOZ_ASSERT(!viewportUnitsUsed,
+                     "Non-master stylesets shouldn't get flagged as using "
+                     "viewport units!");
+        }
+        rulesChanged = rulesChanged || styleSetChanged;
+      }
+    } else {
+      nsIStyleRuleProcessor* ruleProcessor =
+        aBinding->PrototypeBinding()->GetRuleProcessor();
+      if (ruleProcessor) {
+        bool thisChanged = ruleProcessor->MediumFeaturesChanged(presContext);
+        rulesChanged = rulesChanged || thisChanged;
+      }
     }
   });
 
   return rulesChanged;
 }
 
 void
+nsBindingManager::UpdateBoundContentBindingsForServo(nsPresContext* aPresContext)
+{
+  MOZ_ASSERT(mDocument->IsStyledByServo(),
+             "This should be called only by servo-backend!");
+
+  RefPtr<nsPresContext> presContext = aPresContext;
+
+  EnumerateBoundContentBindings([=](nsXBLBinding* aBinding) {
+    nsXBLPrototypeBinding* protoBinding = aBinding->PrototypeBinding();
+    ServoStyleSet* styleSet = protoBinding->GetServoStyleSet();
+    if (styleSet && styleSet->StyleSheetsHaveChanged()) {
+      protoBinding->ComputeServoStyleSet(presContext);
+    }
+  });
+}
+
+void
 nsBindingManager::AppendAllSheets(nsTArray<StyleSheet*>& aArray)
 {
   EnumerateBoundContentBindings([&aArray](nsXBLBinding* aBinding) {
     aBinding->PrototypeBinding()->AppendStyleSheetsTo(aArray);
   });
 }
 
 static void
--- a/dom/xbl/nsBindingManager.h
+++ b/dom/xbl/nsBindingManager.h
@@ -125,23 +125,27 @@ public:
 
   // Style rule methods
   nsresult WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc,
                      ElementDependentRuleProcessorData* aData,
                      bool* aCutOffInheritance);
 
   void WalkAllRules(nsIStyleRuleProcessor::EnumFunc aFunc,
                     ElementDependentRuleProcessorData* aData);
-  /**
-   * Do any processing that needs to happen as a result of a change in
-   * the characteristics of the medium, and return whether this rule
-   * processor's rules have changed (e.g., because of media queries).
-   */
+
+  // Do any processing that needs to happen as a result of a change in the
+  // characteristics of the medium, and return whether this rule processor's
+  // rules or the servo style set have changed (e.g., because of media
+  // queries).
   bool MediumFeaturesChanged(nsPresContext* aPresContext);
 
+  // Update the content bindings in mBoundContentSet due to medium features
+  // changed.
+  void UpdateBoundContentBindingsForServo(nsPresContext* aPresContext);
+
   void AppendAllSheets(nsTArray<mozilla::StyleSheet*>& aArray);
 
   void Traverse(nsIContent *aContent,
                             nsCycleCollectionTraversalCallback &cb);
 
   NS_DECL_CYCLE_COLLECTION_CLASS(nsBindingManager)
 
   // Notify the binding manager when an outermost update begins and
--- a/dom/xbl/nsXBLPrototypeBinding.cpp
+++ b/dom/xbl/nsXBLPrototypeBinding.cpp
@@ -564,16 +564,24 @@ nsXBLPrototypeBinding::GetRuleProcessor(
 {
   if (mResources) {
     return mResources->GetRuleProcessor();
   }
 
   return nullptr;
 }
 
+void
+nsXBLPrototypeBinding::ComputeServoStyleSet(nsPresContext* aPresContext)
+{
+  if (mResources) {
+    mResources->ComputeServoStyleSet(aPresContext);
+  }
+}
+
 ServoStyleSet*
 nsXBLPrototypeBinding::GetServoStyleSet() const
 {
   return mResources ? mResources->GetServoStyleSet() : nullptr;
 }
 
 void
 nsXBLPrototypeBinding::EnsureAttributeTable()
--- a/dom/xbl/nsXBLPrototypeBinding.h
+++ b/dom/xbl/nsXBLPrototypeBinding.h
@@ -126,16 +126,17 @@ public:
   void RemoveStyleSheet(mozilla::StyleSheet* aSheet);
   void InsertStyleSheetAt(size_t aIndex, mozilla::StyleSheet* aSheet);
   mozilla::StyleSheet* StyleSheetAt(size_t aIndex) const;
   size_t SheetCount() const;
   bool HasStyleSheets() const;
   void AppendStyleSheetsTo(nsTArray<mozilla::StyleSheet*>& aResult) const;
 
   nsIStyleRuleProcessor* GetRuleProcessor();
+  void ComputeServoStyleSet(nsPresContext* aPresContext);
   mozilla::ServoStyleSet* GetServoStyleSet() const;
 
   nsresult FlushSkinSheets();
 
   nsIAtom* GetBaseTag(int32_t* aNamespaceID);
   void SetBaseTag(int32_t aNamespaceID, nsIAtom* aTag);
 
   bool ImplementsInterface(REFNSIID aIID) const;
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -104,16 +104,18 @@ ServoStyleSet::~ServoStyleSet()
     }
   }
 }
 
 void
 ServoStyleSet::Init(nsPresContext* aPresContext, nsBindingManager* aBindingManager)
 {
   mPresContext = aPresContext;
+  mPresContextInitXBLStyleSet = aPresContext;
+
   mRawSet.reset(Servo_StyleSet_Init(aPresContext));
   mBindingManager = aBindingManager;
 
   mPresContext->DeviceContext()->InitFontCache();
 
   // Now that we have an mRawSet, go ahead and notify about whatever stylesheets
   // we have so far.
   for (auto& sheetArray : mSheets) {
@@ -163,31 +165,51 @@ ServoStyleSet::InvalidateStyleForCSSRule
   MOZ_ASSERT(StylistNeedsUpdate());
   mPresContext->RestyleManager()->AsServo()->PostRestyleEventForCSSRuleChanges();
 }
 
 nsRestyleHint
 ServoStyleSet::MediumFeaturesChanged(bool aViewportChanged)
 {
   bool viewportUnitsUsed = false;
-  const OriginFlags rulesChanged = static_cast<OriginFlags>(
-    Servo_StyleSet_MediumFeaturesChanged(mRawSet.get(), &viewportUnitsUsed));
+  bool rulesChanged = MediumFeaturesChangedRules(&viewportUnitsUsed);
 
-  if (rulesChanged != OriginFlags(0)) {
-    MarkOriginsDirty(rulesChanged);
+  if (mBindingManager &&
+      mBindingManager->MediumFeaturesChanged(mPresContext)) {
+    SetStylistXBLStyleSheetsDirty();
+    rulesChanged = true;
+  }
+
+  if (rulesChanged) {
     return eRestyle_Subtree;
   }
 
   if (viewportUnitsUsed && aViewportChanged) {
     return eRestyle_ForceDescendants;
   }
 
   return nsRestyleHint(0);
 }
 
+bool
+ServoStyleSet::MediumFeaturesChangedRules(bool* aViewportUnitsUsed)
+{
+  MOZ_ASSERT(aViewportUnitsUsed);
+
+  const OriginFlags rulesChanged = static_cast<OriginFlags>(
+    Servo_StyleSet_MediumFeaturesChanged(mRawSet.get(), aViewportUnitsUsed));
+
+  if (rulesChanged != OriginFlags(0)) {
+    MarkOriginsDirty(rulesChanged);
+    return true;
+  }
+
+  return false;
+}
+
 MOZ_DEFINE_MALLOC_SIZE_OF(ServoStyleSetMallocSizeOf)
 
 void
 ServoStyleSet::AddSizeOfIncludingThis(nsWindowSizes& aSizes) const
 {
   MallocSizeOf mallocSizeOf = aSizes.mState.mMallocSizeOf;
 
   aSizes.mLayoutServoStyleSetsOther += mallocSizeOf(this);
@@ -1367,23 +1389,31 @@ ServoStyleSet::ResolveForDeclarations(
                                                aDeclarations).Consume();
 }
 
 void
 ServoStyleSet::UpdateStylist()
 {
   MOZ_ASSERT(StylistNeedsUpdate());
 
-  // There's no need to compute invalidations and such for an XBL styleset,
-  // since they are loaded and unloaded synchronously, and they don't have to
-  // deal with dynamic content changes.
-  Element* root =
-    IsMaster() ? mPresContext->Document()->GetDocumentElement() : nullptr;
+  if (mStylistState & StylistState::StyleSheetsDirty) {
+    // There's no need to compute invalidations and such for an XBL styleset,
+    // since they are loaded and unloaded synchronously, and they don't have to
+    // deal with dynamic content changes.
+    Element* root =
+      IsMaster() ? mPresContext->Document()->GetDocumentElement() : nullptr;
 
-  Servo_StyleSet_FlushStyleSheets(mRawSet.get(), root);
+    Servo_StyleSet_FlushStyleSheets(mRawSet.get(), root);
+  }
+
+  if (MOZ_UNLIKELY(mStylistState & StylistState::XBLStyleSheetsDirty)) {
+    MOZ_ASSERT(IsMaster(), "Only master styleset can mark XBL stylesets dirty!");
+    mBindingManager->UpdateBoundContentBindingsForServo(mPresContext);
+  }
+
   mStylistState = StylistState::NotDirty;
 }
 
 void
 ServoStyleSet::MaybeGCRuleTree()
 {
   MOZ_ASSERT(NS_IsMainThread());
   Servo_MaybeGCRuleTree(mRawSet.get());
--- a/layout/style/ServoStyleSet.h
+++ b/layout/style/ServoStyleSet.h
@@ -41,28 +41,32 @@ class nsIContent;
 class nsIDocument;
 class nsPresContext;
 struct nsTimingFunction;
 struct RawServoRuleNode;
 struct TreeMatchContext;
 
 namespace mozilla {
 
-/**
- * A few flags used to track which kind of stylist state we may need to
- * update.
- */
+// A few flags used to track which kind of stylist state we may need to
+// update.
 enum class StylistState : uint8_t {
-  /** The stylist is not dirty, we should do nothing */
+  // The stylist is not dirty, we should do nothing.
   NotDirty = 0,
 
-  /** The style sheets have changed, so we need to update the style data. */
-  StyleSheetsDirty,
+  // The style sheets have changed, so we need to update the style data.
+  StyleSheetsDirty = 1 << 0,
+
+  // Some of the style sheets of the bound elements in binding manager have
+  // changed, so we need to tell the binding manager to update style data.
+  XBLStyleSheetsDirty = 1 << 1,
 };
 
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(StylistState)
+
 // Bitfield type to represent Servo stylesheet origins.
 enum class OriginFlags : uint8_t {
   UserAgent = 0x01,
   User      = 0x02,
   Author    = 0x04,
   All       = 0x07,
 };
 
@@ -128,16 +132,19 @@ public:
 
   bool StyleSheetsHaveChanged() const
   {
     return StylistNeedsUpdate();
   }
 
   nsRestyleHint MediumFeaturesChanged(bool aViewportChanged);
 
+  // aViewportChanged outputs whether any viewport units is used.
+  bool MediumFeaturesChangedRules(bool* aViewportUnitsUsed);
+
   void InvalidateStyleForCSSRuleChanges();
 
   void AddSizeOfIncludingThis(nsWindowSizes& aSizes) const;
   const RawServoStyleSet* RawSet() const {
     return mRawSet.get();
   }
 
   bool GetAuthorStyleDisabled() const;
@@ -428,16 +435,21 @@ public:
   // Returns the style rule map.
   ServoStyleRuleMap* StyleRuleMap();
 
   // Clear mPresContext. This is needed after XBL ServoStyleSet is created.
   void ClearPresContext() {
     mPresContext = nullptr;
   }
 
+  // Return whether this is the PresContext that initialized us.
+  bool IsPresContextChanged(nsPresContext* aPresContext) const {
+    return aPresContext != mPresContextInitXBLStyleSet;
+  }
+
   /**
    * Returns true if a modification to an an attribute with the specified
    * local name might require us to restyle the element.
    *
    * This function allows us to skip taking a an attribute snapshot when
    * the modified attribute doesn't appear in an attribute selector in
    * a style sheet.
    */
@@ -520,17 +532,22 @@ private:
    */
   void MarkOriginsDirty(OriginFlags aChangedOrigins);
 
   /**
    * Note that the stylist needs a style flush due to style sheet changes.
    */
   void SetStylistStyleSheetsDirty()
   {
-    mStylistState = StylistState::StyleSheetsDirty;
+    mStylistState |= StylistState::StyleSheetsDirty;
+  }
+
+  void SetStylistXBLStyleSheetsDirty()
+  {
+    mStylistState |= StylistState::XBLStyleSheetsDirty;
   }
 
   bool StylistNeedsUpdate() const
   {
     return mStylistState != StylistState::NotDirty;
   }
 
   /**
@@ -560,17 +577,25 @@ private:
   void InsertSheetOfType(SheetType aType,
                          ServoStyleSheet* aSheet,
                          ServoStyleSheet* aBeforeSheet);
 
   void RemoveSheetOfType(SheetType aType,
                          ServoStyleSheet* aSheet);
 
   const Kind mKind;
-  nsPresContext* mPresContext;
+
+  // Nullptr if this is an XBL style set.
+  nsPresContext* MOZ_NON_OWNING_REF mPresContext = nullptr;
+
+  // Because XBL style set could be used by multiple PresContext, we need to
+  // store the PresContext pointer which initializes this style set for
+  // computing medium rule changes.
+  void* MOZ_NON_OWNING_REF mPresContextInitXBLStyleSet = nullptr;
+
   UniquePtr<RawServoStyleSet> mRawSet;
   EnumeratedArray<SheetType, SheetType::Count,
                   nsTArray<RefPtr<ServoStyleSheet>>> mSheets;
   bool mAuthorStyleDisabled;
   StylistState mStylistState;
   uint64_t mUserFontSetUpdateGeneration;
   uint32_t mUserFontCacheUpdateGeneration;
 
--- a/layout/style/test/mochitest.ini
+++ b/layout/style/test/mochitest.ini
@@ -237,17 +237,16 @@ skip-if = !stylo
 [test_keyframes_rules.html]
 [test_keyframes_vendor_prefix.html]
 [test_load_events_on_stylesheets.html]
 [test_logical_properties.html]
 [test_media_queries.html]
 skip-if = android_version == '18' #debug-only failure; timed out #Android 4.3 aws only; bug 1030419
 [test_media_queries_dynamic.html]
 [test_media_queries_dynamic_xbl.html]
-fail-if = stylo # bug 1382078
 [test_media_query_list.html]
 [test_media_query_serialization.html]
 [test_moz_device_pixel_ratio.html]
 [test_namespace_rule.html]
 [test_of_type_selectors.xhtml]
 [test_page_parser.html]
 [test_parse_eof.html]
 [test_parse_ident.html]