Bug 1382078 Part 3 - Support media changes for XBL stylesheets. draft
authorTing-Yu Lin <tlin@mozilla.com>
Sat, 02 Sep 2017 15:25:45 +0800
changeset 658691 cf3bf901963a27356b16dcb15dff19cdf26d1e10
parent 658690 788b2b1dc450925f67d259c423fd5057443c138e
child 658692 54baa95de1eb35a3f59fab9d6a6e8f5a8377f972
push id77838
push userbmo:tlin@mozilla.com
push dateMon, 04 Sep 2017 14:13:04 +0000
bugs1382078
milestone57.0a1
Bug 1382078 Part 3 - Support media changes for XBL stylesheets. 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]