Bug 1435939: Process all the MediumFeatureChanges at the same time. r?xidorn draft
authorEmilio Cobos Álvarez <emilio@crisal.io>
Tue, 06 Feb 2018 16:52:22 +0100
changeset 751624 622bfbf26459c173d13d4292a065f2efa4c21ce7
parent 751623 25cf2c5ff1b25d848afb07f6be5aeb0f0072cb25
child 751809 177236f1c268fbe964556ac16dcb39caeb7a183a
push id98022
push userbmo:emilio@crisal.io
push dateTue, 06 Feb 2018 19:03:46 +0000
reviewersxidorn
bugs1435939
milestone60.0a1
Bug 1435939: Process all the MediumFeatureChanges at the same time. r?xidorn This avoids resetting the computed values all the time, and paves the way to avoid using a StyleSet on XBL bindings / Shadow DOM, which we should really really do because it's super overkill. There are some XBL bits that are kind of hacky, in particular the mStylistDirty, but they'll go away soon, since I want to redo how we store styles in XBL. The alternative, which was returning an array of indices or something was even more hacky I think. MozReview-Commit-ID: 6tEl5gebXVF
dom/xbl/nsBindingManager.cpp
layout/style/ServoBindingList.h
layout/style/ServoBindings.h
layout/style/ServoBindings.toml
layout/style/ServoStyleSet.cpp
layout/style/ServoStyleSet.h
servo/components/style/stylist.rs
servo/ports/geckolib/glue.rs
--- a/dom/xbl/nsBindingManager.cpp
+++ b/dom/xbl/nsBindingManager.cpp
@@ -762,57 +762,34 @@ nsBindingManager::WalkAllRules(nsIStyleR
   });
 }
 #endif
 
 bool
 nsBindingManager::MediumFeaturesChanged(nsPresContext* aPresContext,
                                         mozilla::MediaFeatureChangeReason aReason)
 {
+  MOZ_ASSERT(!mDocument->IsStyledByServo());
+#ifdef MOZ_OLD_STYLE
   bool rulesChanged = false;
   RefPtr<nsPresContext> presContext = aPresContext;
-  bool isStyledByServo = mDocument->IsStyledByServo();
-
   EnumerateBoundContentBindings([=, &rulesChanged](nsXBLBinding* aBinding) {
-    if (isStyledByServo) {
-      ServoStyleSet* styleSet = aBinding->PrototypeBinding()->GetServoStyleSet();
-      if (styleSet) {
-        bool styleSetChanged = false;
-
-        if (styleSet->IsPresContextChanged(presContext)) {
-          styleSetChanged = styleSet->SetPresContext(presContext);
-        } 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, aReason);
-          MOZ_ASSERT(!viewportUnitsUsed,
-                     "Non-master stylesets shouldn't get flagged as using "
-                     "viewport units!");
-        }
-        rulesChanged = rulesChanged || styleSetChanged;
-      }
-    } else {
-#ifdef MOZ_OLD_STYLE
-      nsIStyleRuleProcessor* ruleProcessor =
-        aBinding->PrototypeBinding()->GetRuleProcessor();
-      if (ruleProcessor) {
-        bool thisChanged = ruleProcessor->MediumFeaturesChanged(presContext);
-        rulesChanged = rulesChanged || thisChanged;
-      }
-#else
-      MOZ_CRASH("old style system disabled");
-#endif
+    nsIStyleRuleProcessor* ruleProcessor =
+      aBinding->PrototypeBinding()->GetRuleProcessor();
+    if (ruleProcessor) {
+      bool thisChanged = ruleProcessor->MediumFeaturesChanged(presContext);
+      rulesChanged = rulesChanged || thisChanged;
     }
     return true;
   });
-
   return rulesChanged;
+#else
+  MOZ_CRASH("old style system disabled");
+  return false;
+#endif
 }
 
 void
 nsBindingManager::UpdateBoundContentBindingsForServo(nsPresContext* aPresContext)
 {
   MOZ_ASSERT(mDocument->IsStyledByServo(),
              "This should be called only by servo-backend!");
 
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -81,18 +81,20 @@ SERVO_BINDING_FUNC(Servo_StyleSheet_GetS
 SERVO_BINDING_FUNC(Servo_StyleSheet_GetOrigin, uint8_t,
                    RawServoStyleSheetContentsBorrowed sheet)
 SERVO_BINDING_FUNC(Servo_StyleSet_Init, RawServoStyleSet*, RawGeckoPresContextOwned pres_context)
 SERVO_BINDING_FUNC(Servo_StyleSet_RebuildCachedData, void,
                    RawServoStyleSetBorrowed set)
 // We'd like to return `OriginFlags` here, but bindgen bitfield enums don't
 // work as return values with the Linux 32-bit ABI at the moment because
 // they wrap the value in a struct.
-SERVO_BINDING_FUNC(Servo_StyleSet_MediumFeaturesChanged, uint8_t,
-                   RawServoStyleSetBorrowed set, bool* viewport_units_used,
+SERVO_BINDING_FUNC(Servo_StyleSet_MediumFeaturesChanged,
+                   MediumFeaturesChangedResult,
+                   RawServoStyleSetBorrowed document_set,
+                   const nsTArray<mozilla::ServoStyleSet*>* non_document_sets,
                    bool may_affect_default_style)
 // We'd like to return `OriginFlags` here, but bindgen bitfield enums don't
 // work as return values with the Linux 32-bit ABI at the moment because
 // they wrap the value in a struct.
 SERVO_BINDING_FUNC(Servo_StyleSet_SetDevice, uint8_t,
                    RawServoStyleSetBorrowed set, RawGeckoPresContextOwned pres_context)
 SERVO_BINDING_FUNC(Servo_StyleSet_Drop, void, RawServoStyleSetOwned set)
 SERVO_BINDING_FUNC(Servo_StyleSet_CompatModeChanged, void,
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -141,16 +141,23 @@ struct FontSizePrefs
   nscoord mDefaultFixedSize;
   nscoord mDefaultSerifSize;
   nscoord mDefaultSansSerifSize;
   nscoord mDefaultMonospaceSize;
   nscoord mDefaultCursiveSize;
   nscoord mDefaultFantasySize;
 };
 
+struct MediumFeaturesChangedResult {
+  bool affects_document_rules;
+  bool affects_non_document_rules;
+  bool uses_viewport_units;
+};
+
+
 // DOM Traversal.
 void Gecko_RecordTraversalStatistics(uint32_t total, uint32_t parallel,
                                      uint32_t total_t, uint32_t parallel_t,
                                      uint32_t total_s, uint32_t parallel_s);
 bool Gecko_IsSignificantChild(RawGeckoNodeBorrowed node,
                               bool text_is_significant,
                               bool whitespace_is_significant);
 RawGeckoNodeBorrowedOrNull Gecko_GetLastChild(RawGeckoNodeBorrowed node);
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -262,16 +262,17 @@ whitelist-types = [
     "gfxFontFeature",
     "gfxFontFeatureValueSet",
     "gfxFontVariation",
     "GridNamedArea",
     "mozilla::HalfCorner",
     "Image",
     "ImageURL",
     "Keyframe",
+    "MediumFeaturesChangedResult",
     "nsAttrName",
     "nsAttrValue",
     "nscolor",
     "nsChangeHint",
     "nsCSSCounterDesc",
     "nsCSSCounterStyleRule",
     "nsCSSFontDesc",
     "nsCSSFontFaceRule",
@@ -463,23 +464,25 @@ structs-types = [
     "mozilla::css::ErrorReporter",
     "mozilla::css::ImageValue",
     "mozilla::css::URLValue",
     "mozilla::css::URLValueData",
     "mozilla::dom::CallerType",
     "mozilla::AnonymousCounterStyle",
     "mozilla::AtomArray",
     "mozilla::MallocSizeOf",
+    "mozilla::ServoStyleSet",
     "mozilla::OriginFlags",
     "mozilla::UniquePtr",
     "ServoRawOffsetArc",
     "nsIContent",
     "nsIDocument",
     "nsIDocument_DocumentTheme",
     "nsSimpleContentList",
+    "MediumFeaturesChangedResult",
     "RawGeckoAnimationPropertySegment",
     "RawGeckoComputedTiming",
     "RawGeckoCSSPropertyIDList",
     "RawGeckoDocument",
     "RawGeckoElement",
     "Element",
     "RawGeckoKeyframeList",
     "RawGeckoPropertyValuePairList",
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -128,16 +128,20 @@ ServoStyleSet::CreateXBLServoStyleSet(
 
   // The XBL style sheets aren't document level sheets, but we need to
   // decide a particular SheetType to add them to style set. This type
   // doesn't affect the place where we pull those rules from
   // stylist::push_applicable_declarations_as_xbl_only_stylist().
   set->ReplaceSheets(SheetType::Doc, aNewSheets);
 
   // Update stylist immediately.
+  //
+  // NOTE(emilio): that this _needs_ to be the only call to UpdateStylist for
+  // XBL bindings, otherwise the Servo-side Device may have stale pres context
+  // pointers and such, which are not great.
   set->UpdateStylist();
 
   // XBL resources are shared for a given URL, even across documents, so we
   // can't safely keep this reference.
   set->mDocument = nullptr;
 
   return set;
 }
@@ -158,18 +162,16 @@ ServoStyleSet::GetPresContext()
 }
 
 void
 ServoStyleSet::Init(nsPresContext* aPresContext)
 {
   mDocument = aPresContext->Document();
   MOZ_ASSERT(GetPresContext() == aPresContext);
 
-  mLastPresContextUsesXBLStyleSet = aPresContext;
-
   mRawSet.reset(Servo_StyleSet_Init(aPresContext));
 
   aPresContext->DeviceContext()->InitFontCache();
 
   // Now that we have an mRawSet, go ahead and notify about whatever stylesheets
   // we have so far.
   for (auto& sheetArray : mSheets) {
     for (auto& sheet : sheetArray) {
@@ -204,34 +206,16 @@ void
 ServoStyleSet::InvalidateStyleForCSSRuleChanges()
 {
   MOZ_ASSERT(StylistNeedsUpdate());
   if (nsPresContext* pc = GetPresContext()) {
     pc->RestyleManager()->AsServo()->PostRestyleEventForCSSRuleChanges();
   }
 }
 
-bool
-ServoStyleSet::SetPresContext(nsPresContext* aPresContext)
-{
-  MOZ_ASSERT(IsForXBL(), "Only XBL styleset can set PresContext!");
-
-  mLastPresContextUsesXBLStyleSet = aPresContext;
-
-  const OriginFlags rulesChanged = static_cast<OriginFlags>(
-    Servo_StyleSet_SetDevice(mRawSet.get(), aPresContext));
-
-  if (rulesChanged != OriginFlags(0)) {
-    MarkOriginsDirty(rulesChanged);
-    return true;
-  }
-
-  return false;
-}
-
 void
 ServoStyleSet::InvalidateStyleForDocumentStateChanges(EventStates aStatesChanged)
 {
   MOZ_ASSERT(IsMaster());
   MOZ_ASSERT(mDocument);
   MOZ_ASSERT(!aStatesChanged.IsEmpty());
 
   nsPresContext* pc = GetPresContext();
@@ -259,72 +243,70 @@ ServoStyleSet::InvalidateStyleForDocumen
       }
       return true;
     });
 
   Servo_InvalidateStyleForDocStateChanges(
     root, &styleSets, aStatesChanged.ServoValue());
 }
 
+static const MediaFeatureChangeReason kMediaFeaturesAffectingDefaultStyle =
+  // Zoom changes change the meaning of em units.
+  MediaFeatureChangeReason::ZoomChange |
+  // Changes the meaning of em units, depending on which one is the actual
+  // min-font-size.
+  MediaFeatureChangeReason::MinFontSizeChange;
+
 nsRestyleHint
 ServoStyleSet::MediumFeaturesChanged(MediaFeatureChangeReason aReason)
 {
-  bool viewportUnitsUsed = false;
-  bool rulesChanged = MediumFeaturesChangedRules(&viewportUnitsUsed, aReason);
+  AutoTArray<ServoStyleSet*, 20> nonDocumentStyleSets;
+  // FIXME(emilio): When bug 1425759 is fixed we need to enumerate ShadowRoots
+  // too.
+  //
+  // FIXME(emilio): This is broken for XBL. See bug 1406875.
+  mDocument->BindingManager()->EnumerateBoundContentBindings(
+    [&](nsXBLBinding* aBinding) {
+      if (ServoStyleSet* set = aBinding->PrototypeBinding()->GetServoStyleSet()) {
+        nonDocumentStyleSets.AppendElement(set);
+      }
+      return true;
+    });
 
-  if (nsPresContext* pc = GetPresContext()) {
-    if (mDocument->BindingManager()->MediumFeaturesChanged(pc, aReason)) {
-      // TODO(emilio): We could technically just restyle the bound elements.
-      SetStylistXBLStyleSheetsDirty();
-      rulesChanged = true;
-    }
+  bool mayAffectDefaultStyle =
+    bool(aReason & kMediaFeaturesAffectingDefaultStyle);
+
+  const MediumFeaturesChangedResult result =
+    Servo_StyleSet_MediumFeaturesChanged(
+      mRawSet.get(), &nonDocumentStyleSets, mayAffectDefaultStyle);
+
+  const bool rulesChanged =
+    result.affects_document_rules || result.affects_non_document_rules;
+
+  if (result.affects_document_rules) {
+    SetStylistStyleSheetsDirty();
+  }
+
+  if (result.affects_non_document_rules) {
+    SetStylistXBLStyleSheetsDirty();
   }
 
   if (rulesChanged) {
     return eRestyle_Subtree;
   }
 
   const bool viewportChanged =
     bool(aReason & MediaFeatureChangeReason::ViewportChange);
-  if (viewportUnitsUsed && viewportChanged) {
+  if (result.uses_viewport_units && viewportChanged) {
     return eRestyle_ForceDescendants;
   }
 
   return nsRestyleHint(0);
 }
 
-static const MediaFeatureChangeReason kMediaFeaturesAffectingDefaultStyle =
-  // Zoom changes change the meaning of em units.
-  MediaFeatureChangeReason::ZoomChange |
-  // Changes the meaning of em units, depending on which one is the actual
-  // min-font-size.
-  MediaFeatureChangeReason::MinFontSizeChange;
-
-bool
-ServoStyleSet::MediumFeaturesChangedRules(
-  bool* aViewportUnitsUsed,
-  MediaFeatureChangeReason aReason)
-{
-  MOZ_ASSERT(aViewportUnitsUsed);
-
-  bool mayAffectDefaultStyle =
-    bool(aReason & kMediaFeaturesAffectingDefaultStyle);
-
-  const OriginFlags rulesChanged = static_cast<OriginFlags>(
-    Servo_StyleSet_MediumFeaturesChanged(
-      mRawSet.get(), aViewportUnitsUsed, mayAffectDefaultStyle));
-
-  if (rulesChanged != OriginFlags(0)) {
-    MarkOriginsDirty(rulesChanged);
-    return true;
-  }
-
-  return false;
-}
-
 MOZ_DEFINE_MALLOC_SIZE_OF(ServoStyleSetMallocSizeOf)
 MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoStyleSetMallocEnclosingSizeOf)
 
 void
 ServoStyleSet::AddSizeOfIncludingThis(nsWindowSizes& aSizes) const
 {
   MallocSizeOf mallocSizeOf = aSizes.mState.mMallocSizeOf;
 
@@ -1115,16 +1097,20 @@ ServoStyleSet::MarkOriginsDirty(OriginFl
   Servo_StyleSet_NoteStyleSheetsChanged(mRawSet.get(),
                                         mAuthorStyleDisabled,
                                         aChangedOrigins);
 }
 
 void
 ServoStyleSet::SetStylistStyleSheetsDirty()
 {
+  // Note that there's another hidden mutator of mStylistState for XBL style
+  // sets in MediumFeaturesChanged...
+  //
+  // We really need to stop using a full-blown StyleSet there...
   mStylistState |= StylistState::StyleSheetsDirty;
 
   // We need to invalidate cached style in getComputedStyle for undisplayed
   // elements, since we don't know if any of the style sheet change that we
   // do would affect undisplayed elements.
   if (nsPresContext* presContext = GetPresContext()) {
     // XBL sheets don't have a pres context, but invalidating the restyle
     // generation in that case is handled by SetXBLStyleSheetsDirty in the
@@ -1458,16 +1444,19 @@ ServoStyleSet::UpdateStylist()
       snapshots = &pc->RestyleManager()->AsServo()->Snapshots();
     }
     Servo_StyleSet_FlushStyleSheets(mRawSet.get(), root, snapshots);
   }
 
   if (MOZ_UNLIKELY(mStylistState & StylistState::XBLStyleSheetsDirty)) {
     MOZ_ASSERT(IsMaster(), "Only master styleset can mark XBL stylesets dirty!");
     MOZ_ASSERT(GetPresContext(), "How did they get dirty?");
+    // NOTE(emilio): This right now rebuilds the stylist in the prototype
+    // binding. That is fine, and if we wanted to be more incremental, which we
+    // probably should, we need to move away from using a StyleSet for XBL.
     mDocument->BindingManager()->UpdateBoundContentBindingsForServo(GetPresContext());
   }
 
   mStylistState = StylistState::NotDirty;
 }
 
 void
 ServoStyleSet::MaybeGCRuleTree()
--- a/layout/style/ServoStyleSet.h
+++ b/layout/style/ServoStyleSet.h
@@ -155,19 +155,16 @@ public:
   // app units.
   //
   // The SourceSizeList parameter can be null, in which case it will return
   // 100vw.
   nscoord EvaluateSourceSizeList(const RawServoSourceSizeList* aSourceSizeList) const {
     return Servo_SourceSizeList_Evaluate(mRawSet.get(), aSourceSizeList);
   }
 
-  // aViewportChanged outputs whether any viewport units is used.
-  bool MediumFeaturesChangedRules(bool* aViewportUnitsUsed, MediaFeatureChangeReason);
-
   void InvalidateStyleForCSSRuleChanges();
 
   void AddSizeOfIncludingThis(nsWindowSizes& aSizes) const;
   const RawServoStyleSet* RawSet() const {
     return mRawSet.get();
   }
 
   bool GetAuthorStyleDisabled() const;
@@ -443,21 +440,16 @@ public:
   // its inner.
   void SetNeedsRestyleAfterEnsureUniqueInner() {
     mNeedsRestyleAfterEnsureUniqueInner = true;
   }
 
   // Returns the style rule map.
   ServoStyleRuleMap* StyleRuleMap();
 
-  // Return whether this is the last PresContext which uses this XBL styleset.
-  bool IsPresContextChanged(nsPresContext* aPresContext) const {
-    return aPresContext != mLastPresContextUsesXBLStyleSet;
-  }
-
   // Set PresContext (i.e. Device) for mRawSet. This should be called only
   // by XBL stylesets. Returns true if there is any rule changing.
   bool SetPresContext(nsPresContext* aPresContext);
 
   /**
    * Returns true if a modification to an an attribute with the specified
    * local name might require us to restyle the element.
    *
@@ -603,23 +595,16 @@ private:
   }
 
   /**
    * Return the associated pres context if we're the master style set and we
    * have an associated pres shell.
    */
   nsPresContext* GetPresContext();
 
-  // Because XBL style set could be used by multiple PresContext, we need to
-  // store the last PresContext pointer which uses this XBL styleset for
-  // computing medium rule changes.
-  //
-  // FIXME(emilio): This is a hack, and is broken. See bug 1406875.
-  void* MOZ_NON_OWNING_REF mLastPresContextUsesXBLStyleSet = nullptr;
-
   UniquePtr<RawServoStyleSet> mRawSet;
   EnumeratedArray<SheetType, SheetType::Count,
                   nsTArray<RefPtr<ServoStyleSheet>>> mSheets;
   bool mAuthorStyleDisabled;
   StylistState mStylistState;
   uint64_t mUserFontSetUpdateGeneration;
   uint32_t mUserFontCacheUpdateGeneration;
 
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -1114,24 +1114,31 @@ impl Stylist {
                 ViewportConstraints::maybe_new(&device, &cascaded_rule, self.quirks_mode);
 
             if let Some(ref constraints) = self.viewport_constraints {
                 device.account_for_viewport_rule(constraints);
             }
         }
 
         self.device = device;
-        self.media_features_change_changed_style(guards)
+        self.media_features_change_changed_style(guards, &self.device)
     }
 
     /// Returns whether, given a media feature change, any previously-applicable
-    /// style has become non-applicable, or vice-versa for each origin.
+    /// style has become non-applicable, or vice-versa for each origin, using
+    /// `device`.
+    ///
+    /// Passing `device` is needed because this is used for XBL in Gecko, where
+    /// the `device` in those Stylist is used.
+    ///
+    /// Arguably XBL should use something more lightweight than a Stylist.
     pub fn media_features_change_changed_style(
         &self,
         guards: &StylesheetGuards,
+        device: &Device,
     ) -> OriginSet {
         debug!("Stylist::media_features_change_changed_style");
 
         let mut origins = OriginSet::empty();
         let stylesheets = self.stylesheets.iter();
 
         for (stylesheet, origin) in stylesheets {
             if origins.contains(origin.into()) {
@@ -1140,17 +1147,17 @@ impl Stylist {
 
             let guard = guards.for_origin(origin);
             let origin_cascade_data =
                 self.cascade_data.borrow_for_origin(origin);
 
             let affected_changed = !origin_cascade_data.media_feature_affected_matches(
                 stylesheet,
                 guard,
-                &self.device,
+                device,
                 self.quirks_mode
             );
 
             if affected_changed {
                 origins |= origin;
             }
         }
 
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -115,17 +115,17 @@ use style::gecko_bindings::structs::nsID
 use style::gecko_bindings::structs::nsStyleTransformMatrix::MatrixTransformOperator;
 use style::gecko_bindings::structs::nsTArray;
 use style::gecko_bindings::structs::nsresult;
 use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasFFI, HasArcFFI};
 use style::gecko_bindings::sugar::ownership::{HasSimpleFFI, Strong};
 use style::gecko_bindings::sugar::refptr::RefPtr;
 use style::gecko_properties;
 use style::invalidation::element::restyle_hints;
-use style::media_queries::{Device, MediaList, parse_media_query_list};
+use style::media_queries::{MediaList, parse_media_query_list};
 use style::parser::{Parse, ParserContext, self};
 use style::properties::{ComputedValues, DeclarationSource, Importance};
 use style::properties::{LonghandId, LonghandIdSet, PropertyDeclaration, PropertyDeclarationBlock, PropertyId};
 use style::properties::{PropertyDeclarationId, ShorthandId};
 use style::properties::{SourcePropertyDeclaration, StyleBuilder};
 use style::properties::{parse_one_declaration_into, parse_style_attribute};
 use style::properties::animated_properties::AnimationValue;
 use style::properties::animated_properties::compare_property_priority;
@@ -1117,72 +1117,82 @@ pub extern "C" fn Servo_StyleSet_AppendS
     let data = &mut *data;
     let guard = global_style_data.shared_lock.read();
     let sheet = unsafe { GeckoStyleSheet::new(sheet) };
     data.stylist.append_stylesheet(sheet, &guard);
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn Servo_StyleSet_MediumFeaturesChanged(
-    raw_data: RawServoStyleSetBorrowed,
-    viewport_units_used: *mut bool,
+    document_set: RawServoStyleSetBorrowed,
+    non_document_sets: *const nsTArray<*mut structs::ServoStyleSet>,
     may_affect_default_style: bool,
-) -> u8 {
+) -> structs::MediumFeaturesChangedResult {
     let global_style_data = &*GLOBAL_STYLE_DATA;
     let guard = global_style_data.shared_lock.read();
 
     // NOTE(emilio): We don't actually need to flush the stylist here and ensure
     // it's up to date.
     //
     // In case it isn't we would trigger a rebuild + restyle as needed too.
     //
     // We need to ensure the default computed values are up to date though,
     // because those can influence the result of media query evaluation.
-    //
-    // FIXME(emilio, bug 1369984): do the computation conditionally, to do it
-    // less often.
-    let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
-
-    *viewport_units_used = data.stylist.device().used_viewport_size();
+    let mut document_data =
+        PerDocumentStyleData::from_ffi(document_set).borrow_mut();
+
     if may_affect_default_style {
-        // FIXME(emilio): It's a shame we do this too for XBL stuff, but we need
-        // to right now to evaluate relative units in media queries correctly.
-        //
-        // We should instead just pass the `Device` reference from the master
-        // stylist, but that looked kinda gross... Maybe it's not actually.
-        data.stylist.device_mut().reset_computed_values();
+        document_data.stylist.device_mut().reset_computed_values();
     }
     let guards = StylesheetGuards::same(&guard);
+
     let origins_in_which_rules_changed =
-        data.stylist.media_features_change_changed_style(&guards);
-
-    // We'd like to return `OriginFlags` here, but bindgen bitfield enums don't
-    // work as return values with the Linux 32-bit ABI at the moment because
-    // they wrap the value in a struct, so for now just unwrap it.
-    OriginFlags::from(origins_in_which_rules_changed).0
-}
-
-#[no_mangle]
-pub extern "C" fn Servo_StyleSet_SetDevice(
-    raw_data: RawServoStyleSetBorrowed,
-    pres_context: RawGeckoPresContextOwned
-) -> u8 {
-    let global_style_data = &*GLOBAL_STYLE_DATA;
-    let guard = global_style_data.shared_lock.read();
-
-    let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
-    let device = Device::new(pres_context);
-    let guards = StylesheetGuards::same(&guard);
-    let origins_in_which_rules_changed =
-        data.stylist.set_device(device, &guards);
-
-    // We'd like to return `OriginFlags` here, but bindgen bitfield enums don't
-    // work as return values with the Linux 32-bit ABI at the moment because
-    // they wrap the value in a struct, so for now just unwrap it.
-    OriginFlags::from(origins_in_which_rules_changed).0
+        document_data.stylist.media_features_change_changed_style(
+            &guards,
+            document_data.stylist.device(),
+        );
+
+    let affects_document_rules = !origins_in_which_rules_changed.is_empty();
+    if affects_document_rules {
+        document_data.stylist.force_stylesheet_origins_dirty(origins_in_which_rules_changed);
+    }
+
+    let mut affects_non_document_rules = false;
+    for non_document_style_set in &**non_document_sets {
+        let non_document_data = &*(**non_document_style_set).mRawSet.mPtr;
+        let non_document_data =
+            mem::transmute::<&structs::RawServoStyleSet, &bindings::RawServoStyleSet>(non_document_data);
+        let mut non_document_data =
+            PerDocumentStyleData::from_ffi(non_document_data).borrow_mut();
+
+        let origins_changed =
+            non_document_data.stylist.media_features_change_changed_style(
+                &guards,
+                document_data.stylist.device(),
+            );
+        if !origins_changed.is_empty() {
+            affects_non_document_rules = true;
+            // XBL stylesets are rebuilt entirely, so we need to mark them
+            // dirty from here instead of going through the stylist
+            // force_origin_dirty stuff, which would be useless.
+            //
+            // FIXME(emilio, bug 1436059): This is super-hacky, make XBL /
+            // Shadow DOM not use a style set at all.
+            (**non_document_style_set).mStylistState = structs::StylistState_StyleSheetsDirty;
+        }
+    }
+
+    let uses_viewport_units =
+        document_data.stylist.device().used_viewport_size();
+
+    structs::MediumFeaturesChangedResult {
+        affects_document_rules,
+        affects_non_document_rules,
+        uses_viewport_units,
+    }
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_StyleSet_PrependStyleSheet(
     raw_data: RawServoStyleSetBorrowed,
     sheet: *const ServoStyleSheet,
 ) {
     let global_style_data = &*GLOBAL_STYLE_DATA;