servo: Merge #18502 - style: Unify how servo and Gecko handle UA sheets (from emilio:ua-sheets-unify); r=simonsapin
authorEmilio Cobos Álvarez <emilio@crisal.io>
Thu, 14 Sep 2017 15:55:21 -0500
changeset 665362 be8ecd6907a416c55e7f47106cdd6e8c2aac4d44
parent 665361 41effc9b8c27c21a98dc995143a3f7562c9258a6
child 665363 442cc0fe3bec0c24499aedbeea051053bb30d1e3
push id80026
push userbmo:ralin@mozilla.com
push dateFri, 15 Sep 2017 10:04:21 +0000
reviewerssimonsapin
milestone57.0a1
servo: Merge #18502 - style: Unify how servo and Gecko handle UA sheets (from emilio:ua-sheets-unify); r=simonsapin Source-Repo: https://github.com/servo/servo Source-Revision: 75e126f139197c447686242082d9e7f61ba92c3f
servo/components/layout_thread/lib.rs
servo/components/script/dom/bindings/trace.rs
servo/components/script/dom/document.rs
servo/components/style/gecko/data.rs
servo/components/style/shared_lock.rs
servo/components/style/stylesheet_set.rs
servo/components/style/stylesheets/stylesheet.rs
servo/components/style/stylesheets/viewport_rule.rs
servo/components/style/stylist.rs
servo/ports/geckolib/glue.rs
servo/tests/unit/style/viewport.rs
--- a/servo/components/layout_thread/lib.rs
+++ b/servo/components/layout_thread/lib.rs
@@ -131,17 +131,17 @@ use style::driver;
 use style::error_reporting::{NullReporter, RustLogReporter};
 use style::invalidation::element::restyle_hints::RestyleHint;
 use style::logical_geometry::LogicalPoint;
 use style::media_queries::{Device, MediaList, MediaType};
 use style::properties::PropertyId;
 use style::selector_parser::SnapshotMap;
 use style::servo::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, REPOSITION, STORE_OVERFLOW};
 use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard, StylesheetGuards};
-use style::stylesheets::{Origin, OriginSet, Stylesheet, DocumentStyleSheet, StylesheetInDocument, UserAgentStylesheets};
+use style::stylesheets::{Origin, Stylesheet, DocumentStyleSheet, StylesheetInDocument, UserAgentStylesheets};
 use style::stylist::Stylist;
 use style::thread_state;
 use style::timer::Timer;
 use style::traversal::DomTraversal;
 use style::traversal_flags::TraversalFlags;
 use style_traits::CSSPixel;
 use style_traits::DevicePixel;
 use style_traits::SpeculativePainter;
@@ -1134,24 +1134,32 @@ impl LayoutThread {
 
         let initial_viewport = data.window_size.initial_viewport;
         let device_pixel_ratio = data.window_size.device_pixel_ratio;
         let old_viewport_size = self.viewport_size;
         let current_screen_size = Size2D::new(Au::from_f32_px(initial_viewport.width),
                                               Au::from_f32_px(initial_viewport.height));
 
         // Calculate the actual viewport as per DEVICE-ADAPT ยง 6
-
+        // If the entire flow tree is invalid, then it will be reflowed anyhow.
         let document_shared_lock = document.style_shared_lock();
         self.document_shared_lock = Some(document_shared_lock.clone());
         let author_guard = document_shared_lock.read();
+
+        let ua_stylesheets = &*UA_STYLESHEETS;
+        let ua_or_user_guard = ua_stylesheets.shared_lock.read();
+        let guards = StylesheetGuards {
+            author: &author_guard,
+            ua_or_user: &ua_or_user_guard,
+        };
+
         let had_used_viewport_units = self.stylist.device().used_viewport_units();
         let device = Device::new(MediaType::screen(), initial_viewport, device_pixel_ratio);
         let sheet_origins_affected_by_device_change =
-            self.stylist.set_device(device, &author_guard);
+            self.stylist.set_device(device, &guards);
 
         self.stylist.force_stylesheet_origins_dirty(sheet_origins_affected_by_device_change);
         self.viewport_size =
             self.stylist.viewport_constraints().map_or(current_screen_size, |constraints| {
                 debug!("Viewport constraints: {:?}", constraints);
 
                 // other rules are evaluated against the actual viewport
                 Size2D::new(Au::from_f32_px(constraints.size.width),
@@ -1168,46 +1176,42 @@ impl LayoutThread {
             }
             if had_used_viewport_units {
                 if let Some(mut data) = element.mutate_data() {
                     data.hint.insert(RestyleHint::recascade_subtree());
                 }
             }
         }
 
-        // If the entire flow tree is invalid, then it will be reflowed anyhow.
-        let ua_stylesheets = &*UA_STYLESHEETS;
-        let ua_or_user_guard = ua_stylesheets.shared_lock.read();
-        let guards = StylesheetGuards {
-            author: &author_guard,
-            ua_or_user: &ua_or_user_guard,
-        };
-
         {
             if self.first_reflow.get() {
                 debug!("First reflow, rebuilding user and UA rules");
-                let mut ua_and_user = OriginSet::empty();
-                ua_and_user |= Origin::User;
-                ua_and_user |= Origin::UserAgent;
-                self.stylist.force_stylesheet_origins_dirty(ua_and_user);
                 for stylesheet in &ua_stylesheets.user_or_user_agent_stylesheets {
-                    self.handle_add_stylesheet(stylesheet, &ua_or_user_guard);
+                    self.stylist.append_stylesheet(stylesheet.clone(), &ua_or_user_guard);
+                    self.handle_add_stylesheet(&stylesheet.0, &ua_or_user_guard);
+                }
+
+                if self.stylist.quirks_mode() != QuirksMode::NoQuirks {
+                    self.stylist.append_stylesheet(
+                        ua_stylesheets.quirks_mode_stylesheet.clone(),
+                        &ua_or_user_guard,
+                    );
+                    self.handle_add_stylesheet(
+                        &ua_stylesheets.quirks_mode_stylesheet.0,
+                        &ua_or_user_guard,
+                    );
                 }
             }
 
             if data.stylesheets_changed {
                 debug!("Doc sheets changed, flushing author sheets too");
                 self.stylist.force_stylesheet_origins_dirty(Origin::Author.into());
             }
 
-            self.stylist.flush(
-                &guards,
-                Some(ua_stylesheets),
-                Some(element),
-            );
+            self.stylist.flush(&guards, Some(element));
         }
 
         if viewport_size_changed {
             if let Some(mut flow) = self.try_get_layout_root(element.as_node()) {
                 LayoutThread::reflow_all_nodes(FlowRef::deref_mut(&mut flow));
             }
         }
 
@@ -1701,43 +1705,57 @@ fn get_root_flow_background_color(flow: 
     let kid_block_flow = kid.as_block();
     kid_block_flow.fragment
                   .style
                   .resolve_color(kid_block_flow.fragment.style.get_background().background_color)
                   .to_gfx_color()
 }
 
 fn get_ua_stylesheets() -> Result<UserAgentStylesheets, &'static str> {
-    fn parse_ua_stylesheet(shared_lock: &SharedRwLock, filename: &'static str)
-                           -> Result<Stylesheet, &'static str> {
+    fn parse_ua_stylesheet(
+        shared_lock: &SharedRwLock,
+        filename: &'static str,
+    ) -> Result<DocumentStyleSheet, &'static str> {
         let res = read_resource_file(filename).map_err(|_| filename)?;
-        Ok(Stylesheet::from_bytes(
+        Ok(DocumentStyleSheet(ServoArc::new(Stylesheet::from_bytes(
             &res,
             ServoUrl::parse(&format!("chrome://resources/{:?}", filename)).unwrap(),
             None,
             None,
             Origin::UserAgent,
             MediaList::empty(),
             shared_lock.clone(),
             None,
             &NullReporter,
-            QuirksMode::NoQuirks))
+            QuirksMode::NoQuirks,
+        ))))
     }
 
     let shared_lock = SharedRwLock::new();
     let mut user_or_user_agent_stylesheets = vec!();
     // FIXME: presentational-hints.css should be at author origin with zero specificity.
     //        (Does it make a difference?)
     for &filename in &["user-agent.css", "servo.css", "presentational-hints.css"] {
         user_or_user_agent_stylesheets.push(parse_ua_stylesheet(&shared_lock, filename)?);
     }
     for &(ref contents, ref url) in &opts::get().user_stylesheets {
-        user_or_user_agent_stylesheets.push(Stylesheet::from_bytes(
-            &contents, url.clone(), None, None, Origin::User, MediaList::empty(),
-            shared_lock.clone(), None, &RustLogReporter, QuirksMode::NoQuirks));
+        user_or_user_agent_stylesheets.push(
+            DocumentStyleSheet(ServoArc::new(Stylesheet::from_bytes(
+                &contents,
+                url.clone(),
+                None,
+                None,
+                Origin::User,
+                MediaList::empty(),
+                shared_lock.clone(),
+                None,
+                &RustLogReporter,
+                QuirksMode::NoQuirks,
+            )))
+        );
     }
 
     let quirks_mode_stylesheet = parse_ua_stylesheet(&shared_lock, "quirks-mode.css")?;
 
     Ok(UserAgentStylesheets {
         shared_lock: shared_lock,
         user_or_user_agent_stylesheets: user_or_user_agent_stylesheets,
         quirks_mode_stylesheet: quirks_mode_stylesheet,
--- a/servo/components/script/dom/bindings/trace.rs
+++ b/servo/components/script/dom/bindings/trace.rs
@@ -650,17 +650,17 @@ unsafe impl JSTraceable for StyleLocked<
     }
 }
 
 unsafe impl<S> JSTraceable for StylesheetSet<S>
 where
     S: JSTraceable + ::style::stylesheets::StylesheetInDocument + PartialEq + 'static,
 {
     unsafe fn trace(&self, tracer: *mut JSTracer) {
-        for s in self.iter() {
+        for (s, _origin) in self.iter() {
             s.trace(tracer)
         }
     }
 }
 
 
 /// Holds a set of JSTraceables that need to be rooted
 struct RootedTraceableSet {
--- a/servo/components/script/dom/document.rs
+++ b/servo/components/script/dom/document.rs
@@ -2392,19 +2392,23 @@ impl Document {
     pub fn add_stylesheet(&self, owner: &Element, sheet: Arc<Stylesheet>) {
         // FIXME(emilio): It'd be nice to unify more code between the elements
         // that own stylesheets, but StylesheetOwner is more about loading
         // them...
         debug_assert!(owner.as_stylesheet_owner().is_some() ||
                       owner.is::<HTMLMetaElement>(), "Wat");
 
         let mut stylesheets = self.stylesheets.borrow_mut();
-        let insertion_point = stylesheets.iter().find(|sheet_in_doc| {
-            owner.upcast::<Node>().is_before(sheet_in_doc.owner.upcast())
-        }).cloned();
+        let insertion_point =
+            stylesheets
+                .iter()
+                .map(|(sheet, _origin)| sheet)
+                .find(|sheet_in_doc| {
+                    owner.upcast::<Node>().is_before(sheet_in_doc.owner.upcast())
+                }).cloned();
 
         self.window()
             .layout_chan()
             .send(Msg::AddStylesheet(
                 sheet.clone(),
                 insertion_point.as_ref().map(|s| s.sheet.clone())
             ))
             .unwrap();
--- a/servo/components/style/gecko/data.rs
+++ b/servo/components/style/gecko/data.rs
@@ -150,17 +150,16 @@ impl PerDocumentStyleDataImpl {
         guard: &SharedRwLockReadGuard,
         document_element: Option<E>,
     ) -> bool
     where
         E: TElement,
     {
         self.stylist.flush(
             &StylesheetGuards::same(guard),
-            /* ua_sheets = */ None,
             document_element,
         )
     }
 
     /// Returns whether private browsing is enabled.
     pub fn is_private_browsing_enabled(&self) -> bool {
         let doc =
             self.stylist.device().pres_context().mDocument.raw::<nsIDocument>();
--- a/servo/components/style/shared_lock.rs
+++ b/servo/components/style/shared_lock.rs
@@ -8,16 +8,17 @@
 use atomic_refcell::{AtomicRefCell, AtomicRef, AtomicRefMut};
 #[cfg(feature = "servo")]
 use parking_lot::RwLock;
 use servo_arc::Arc;
 use std::cell::UnsafeCell;
 use std::fmt;
 #[cfg(feature = "gecko")]
 use std::ptr;
+use stylesheets::Origin;
 
 /// A shared read/write lock that can protect multiple objects.
 ///
 /// In Gecko builds, we don't need the blocking behavior, just the safety. As
 /// such we implement this with an AtomicRefCell instead in Gecko builds,
 /// which is ~2x as fast, and panics (rather than deadlocking) when things go
 /// wrong (which is much easier to debug on CI).
 ///
@@ -257,24 +258,32 @@ pub trait DeepCloneWithLock : Sized {
         guard: &SharedRwLockReadGuard,
         params: &DeepCloneParams,
     ) -> Self;
 }
 
 /// Guards for a document
 #[derive(Clone)]
 pub struct StylesheetGuards<'a> {
-    /// For author-origin stylesheets
+    /// For author-origin stylesheets.
     pub author: &'a SharedRwLockReadGuard<'a>,
 
     /// For user-agent-origin and user-origin stylesheets
     pub ua_or_user: &'a SharedRwLockReadGuard<'a>,
 }
 
 impl<'a> StylesheetGuards<'a> {
+    /// Get the guard for a given stylesheet origin.
+    pub fn for_origin(&self, origin: Origin) -> &SharedRwLockReadGuard<'a> {
+        match origin {
+            Origin::Author => &self.author,
+            _ => &self.ua_or_user,
+        }
+    }
+
     /// Same guard for all origins
     pub fn same(guard: &'a SharedRwLockReadGuard<'a>) -> Self {
         StylesheetGuards {
             author: guard,
             ua_or_user: guard,
         }
     }
 }
--- a/servo/components/style/stylesheet_set.rs
+++ b/servo/components/style/stylesheet_set.rs
@@ -54,39 +54,42 @@ where
 /// An iterator over the flattened view of the stylesheet collections.
 #[derive(Clone)]
 pub struct StylesheetIterator<'a, S>
 where
     S: StylesheetInDocument + PartialEq + 'static,
 {
     origins: OriginSetIterator,
     collections: &'a PerOrigin<SheetCollection<S>>,
-    current: Option<StylesheetCollectionIterator<'a, S>>,
+    current: Option<(Origin, StylesheetCollectionIterator<'a, S>)>,
 }
 
 impl<'a, S> Iterator for StylesheetIterator<'a, S>
 where
     S: StylesheetInDocument + PartialEq + 'static,
 {
-    type Item = &'a S;
+    type Item = (&'a S, Origin);
 
     fn next(&mut self) -> Option<Self::Item> {
         loop {
             if self.current.is_none() {
                 let next_origin = match self.origins.next() {
                     Some(o) => o,
                     None => return None,
                 };
 
                 self.current =
-                    Some(self.collections.borrow_for_origin(&next_origin).iter());
+                    Some((next_origin, self.collections.borrow_for_origin(&next_origin).iter()));
             }
 
-            if let Some(s) = self.current.as_mut().unwrap().next() {
-                return Some(s)
+            {
+                let (origin, ref mut iter) = *self.current.as_mut().unwrap();
+                if let Some(s) = iter.next() {
+                    return Some((s, origin))
+                }
             }
 
             self.current = None;
         }
     }
 }
 
 /// The validity of the data in a given cascade origin.
@@ -107,27 +110,25 @@ pub enum OriginValidity {
 
 impl Default for OriginValidity {
     fn default() -> Self {
         OriginValidity::Valid
     }
 }
 
 /// A struct to iterate over the different stylesheets to be flushed.
-pub struct StylesheetFlusher<'a, 'b, S>
+pub struct StylesheetFlusher<'a, S>
 where
-    'b: 'a,
     S: StylesheetInDocument + PartialEq + 'static,
 {
-    guard: &'a SharedRwLockReadGuard<'b>,
     origins_dirty: OriginSetIterator,
     // NB: Bound to the StylesheetSet lifetime when constructed, see
     // StylesheetSet::flush.
     collections: *mut PerOrigin<SheetCollection<S>>,
-    current: Option<slice::IterMut<'a, StylesheetSetEntry<S>>>,
+    current: Option<(Origin, slice::IterMut<'a, StylesheetSetEntry<S>>)>,
     origin_data_validity: PerOrigin<OriginValidity>,
     author_style_disabled: bool,
     had_invalidations: bool,
 }
 
 /// The type of rebuild that we need to do for a given stylesheet.
 pub enum SheetRebuildKind {
     /// A full rebuild, of both cascade data and invalidation data.
@@ -138,19 +139,18 @@ pub enum SheetRebuildKind {
 
 impl SheetRebuildKind {
     /// Whether the stylesheet invalidation data should be rebuilt.
     pub fn should_rebuild_invalidation(&self) -> bool {
         matches!(*self, SheetRebuildKind::Full)
     }
 }
 
-impl<'a, 'b, S> StylesheetFlusher<'a, 'b, S>
+impl<'a, S> StylesheetFlusher<'a, S>
 where
-    'b: 'a,
     S: StylesheetInDocument + PartialEq + 'static,
 {
     /// The data validity for a given origin.
     pub fn origin_validity(&self, origin: Origin) -> OriginValidity {
         *self.origin_data_validity.borrow_for_origin(&origin)
     }
 
     /// Returns whether running the whole flushing process would be a no-op.
@@ -161,35 +161,33 @@ where
     /// Returns whether any DOM invalidations were processed as a result of the
     /// stylesheet flush.
     pub fn had_invalidations(&self) -> bool {
         self.had_invalidations
     }
 }
 
 #[cfg(debug_assertions)]
-impl<'a, 'b, S> Drop for StylesheetFlusher<'a, 'b, S>
+impl<'a, S> Drop for StylesheetFlusher<'a, S>
 where
-    'b: 'a,
     S: StylesheetInDocument + PartialEq + 'static,
 {
     fn drop(&mut self) {
         debug_assert!(
             self.origins_dirty.next().is_none(),
             "You're supposed to fully consume the flusher"
         );
     }
 }
 
-impl<'a, 'b, S> Iterator for StylesheetFlusher<'a, 'b, S>
+impl<'a, S> Iterator for StylesheetFlusher<'a, S>
 where
-    'b: 'a,
     S: StylesheetInDocument + PartialEq + 'static,
 {
-    type Item = (&'a S, SheetRebuildKind);
+    type Item = (&'a S, Origin, SheetRebuildKind);
 
     fn next(&mut self) -> Option<Self::Item> {
         use std::mem;
 
         loop {
             if self.current.is_none() {
                 let next_origin = match self.origins_dirty.next() {
                     Some(o) => o,
@@ -200,46 +198,53 @@ where
                 debug_assert_eq!(
                     unsafe { &*self.collections }
                         .borrow_for_origin(&next_origin)
                         .data_validity,
                     OriginValidity::Valid
                 );
 
                 self.current =
-                    Some(unsafe { &mut *self.collections }.borrow_mut_for_origin(&next_origin).entries.iter_mut());
+                    Some((
+                        next_origin,
+                        unsafe { &mut *self.collections }
+                            .borrow_mut_for_origin(&next_origin)
+                            .entries
+                            .iter_mut()
+                    ));
             }
 
-            let potential_sheet = match self.current.as_mut().unwrap().next() {
+            let potential_sheet = match self.current.as_mut().unwrap().1.next() {
                 Some(s) => s,
                 None => {
                     self.current = None;
                     continue;
                 }
             };
 
+            let origin = self.current.as_ref().unwrap().0;
+
             let dirty = mem::replace(&mut potential_sheet.dirty, false);
 
             if dirty {
                 // If the sheet was dirty, we need to do a full rebuild anyway.
-                return Some((&potential_sheet.sheet, SheetRebuildKind::Full))
+                return Some((&potential_sheet.sheet, origin, SheetRebuildKind::Full))
             }
 
-            let origin = potential_sheet.sheet.contents(self.guard).origin;
             if self.author_style_disabled && matches!(origin, Origin::Author) {
                 continue;
             }
 
             let rebuild_kind = match self.origin_validity(origin) {
                 OriginValidity::Valid => continue,
                 OriginValidity::CascadeInvalid => SheetRebuildKind::CascadeOnly,
                 OriginValidity::FullyInvalid => SheetRebuildKind::Full,
             };
 
-            return Some((&potential_sheet.sheet, rebuild_kind));
+            return Some((&potential_sheet.sheet, origin, rebuild_kind));
         }
     }
 }
 
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 struct SheetCollection<S>
 where
     S: StylesheetInDocument + PartialEq + 'static,
@@ -478,21 +483,20 @@ where
 
     /// Returns whether the given set has changed from the last flush.
     pub fn has_changed(&self) -> bool {
         !self.origins_dirty.is_empty()
     }
 
     /// Flush the current set, unmarking it as dirty, and returns a
     /// `StylesheetFlusher` in order to rebuild the stylist.
-    pub fn flush<'a, 'b, E>(
+    pub fn flush<'a, E>(
         &'a mut self,
         document_element: Option<E>,
-        guard: &'a SharedRwLockReadGuard<'b>,
-    ) -> StylesheetFlusher<'a, 'b, S>
+    ) -> StylesheetFlusher<'a, S>
     where
         E: TElement,
     {
         use std::mem;
 
         debug!("StylesheetSet::flush");
 
         let had_invalidations = self.invalidations.flush(document_element);
@@ -507,17 +511,16 @@ where
         }
 
         StylesheetFlusher {
             collections: &mut self.collections,
             author_style_disabled: self.author_style_disabled,
             had_invalidations,
             origins_dirty,
             origin_data_validity,
-            guard,
             current: None,
         }
     }
 
     /// Flush stylesheets, but without running any of the invalidation passes.
     #[cfg(feature = "servo")]
     pub fn flush_without_invalidation(&mut self) -> OriginSet {
         use std::mem;
--- a/servo/components/style/stylesheets/stylesheet.rs
+++ b/servo/components/style/stylesheets/stylesheet.rs
@@ -25,19 +25,19 @@ use stylesheets::rule_parser::{State, To
 use stylesheets::rules_iterator::{EffectiveRules, EffectiveRulesIterator, NestedRuleIterationCondition, RulesIterator};
 use values::specified::NamespaceId;
 
 /// This structure holds the user-agent and user stylesheets.
 pub struct UserAgentStylesheets {
     /// The lock used for user-agent stylesheets.
     pub shared_lock: SharedRwLock,
     /// The user or user agent stylesheets.
-    pub user_or_user_agent_stylesheets: Vec<Stylesheet>,
+    pub user_or_user_agent_stylesheets: Vec<DocumentStyleSheet>,
     /// The quirks mode stylesheet.
-    pub quirks_mode_stylesheet: Stylesheet,
+    pub quirks_mode_stylesheet: DocumentStyleSheet,
 }
 
 /// A set of namespaces applying to a given stylesheet.
 ///
 /// The namespace id is used in gecko
 #[derive(Clone, Debug, Default)]
 #[allow(missing_docs)]
 pub struct Namespaces {
--- a/servo/components/style/stylesheets/viewport_rule.rs
+++ b/servo/components/style/stylesheets/viewport_rule.rs
@@ -14,17 +14,17 @@ use cssparser::{CowRcStr, ToCss as Parse
 use error_reporting::{ContextualParseError, ParseErrorReporter};
 use euclid::TypedSize2D;
 use font_metrics::get_metrics_provider_for_product;
 use media_queries::Device;
 use parser::{ParserContext, ParserErrorContext};
 use properties::StyleBuilder;
 use rule_cache::RuleCacheConditions;
 use selectors::parser::SelectorParseError;
-use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
+use shared_lock::{SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard};
 use std::ascii::AsciiExt;
 use std::borrow::Cow;
 use std::cell::RefCell;
 use std::fmt;
 use std::iter::Enumerate;
 use std::str::Chars;
 use style_traits::{PinchZoomFactor, ToCss, ParseError, StyleParseError};
 use style_traits::viewport::{Orientation, UserZoom, ViewportConstraints, Zoom};
@@ -566,26 +566,26 @@ impl Cascade {
         Cascade {
             declarations: vec![None; VIEWPORT_DESCRIPTOR_VARIANTS],
             count_so_far: 0,
         }
     }
 
     pub fn from_stylesheets<'a, I, S>(
         stylesheets: I,
-        guard: &SharedRwLockReadGuard,
+        guards: &StylesheetGuards,
         device: &Device
     ) -> Self
     where
-        I: Iterator<Item = &'a S>,
+        I: Iterator<Item = (&'a S, Origin)>,
         S: StylesheetInDocument + 'static,
     {
         let mut cascade = Self::new();
-        for stylesheet in stylesheets {
-            stylesheet.effective_viewport_rules(device, guard, |rule| {
+        for (stylesheet, origin) in stylesheets {
+            stylesheet.effective_viewport_rules(device, guards.for_origin(origin), |rule| {
                 for declaration in &rule.declarations {
                     cascade.add(Cow::Borrowed(declaration))
                 }
             })
         }
         cascade
     }
 
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -42,17 +42,16 @@ use std::fmt::Debug;
 use std::ops;
 use style_traits::viewport::ViewportConstraints;
 use stylesheet_set::{OriginValidity, SheetRebuildKind, StylesheetSet, StylesheetFlusher};
 #[cfg(feature = "gecko")]
 use stylesheets::{CounterStyleRule, FontFaceRule, FontFeatureValuesRule, PageRule};
 use stylesheets::{CssRule, Origin, OriginSet, PerOrigin, PerOriginIter};
 use stylesheets::StyleRule;
 use stylesheets::StylesheetInDocument;
-use stylesheets::UserAgentStylesheets;
 use stylesheets::keyframes_rule::KeyframesAnimation;
 use stylesheets::viewport_rule::{self, MaybeNew, ViewportRule};
 use thread_state;
 
 pub use ::fnv::FnvHashMap;
 
 /// The type of the stylesheets that the stylist contains.
 #[cfg(feature = "servo")]
@@ -90,26 +89,24 @@ impl DocumentCascadeData {
 
     fn iter_origins_rev(&self) -> PerOriginIter<CascadeData> {
         self.per_origin.iter_origins_rev()
     }
 
     /// Rebuild the cascade data for the given document stylesheets, and
     /// optionally with a set of user agent stylesheets.  Returns Err(..)
     /// to signify OOM.
-    fn rebuild<'a, 'b, S>(
+    fn rebuild<'a, S>(
         &mut self,
         device: &Device,
         quirks_mode: QuirksMode,
-        flusher: StylesheetFlusher<'a, 'b, S>,
+        flusher: StylesheetFlusher<'a, S>,
         guards: &StylesheetGuards,
-        ua_stylesheets: Option<&UserAgentStylesheets>,
     ) -> Result<(), FailedAllocationError>
     where
-        'b: 'a,
         S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static,
     {
         debug_assert!(!flusher.nothing_to_do());
 
         for (cascade_data, origin) in self.per_origin.iter_mut_origins() {
             let validity = flusher.origin_validity(origin);
 
             if validity == OriginValidity::Valid {
@@ -123,94 +120,26 @@ impl DocumentCascadeData {
             if validity == OriginValidity::CascadeInvalid {
                 cascade_data.clear_cascade_data()
             } else {
                 debug_assert_eq!(validity, OriginValidity::FullyInvalid);
                 cascade_data.clear();
             }
         }
 
-        if let Some(ua_stylesheets) = ua_stylesheets {
-            debug_assert!(cfg!(feature = "servo"));
-
-            for stylesheet in &ua_stylesheets.user_or_user_agent_stylesheets {
-                let sheet_origin =
-                    stylesheet.contents(guards.ua_or_user).origin;
-
-                debug_assert!(matches!(
-                    sheet_origin,
-                    Origin::UserAgent | Origin::User
-                ));
-
-                let validity = flusher.origin_validity(sheet_origin);
-
-                // Servo doesn't support to incrementally mutate UA sheets.
-                debug_assert!(matches!(
-                    validity,
-                    OriginValidity::Valid | OriginValidity::FullyInvalid
-                ));
-
-                if validity == OriginValidity::Valid {
-                    continue;
-                }
-
-                self.per_origin
-                    .borrow_mut_for_origin(&sheet_origin)
-                    .add_stylesheet(
-                        device,
-                        quirks_mode,
-                        stylesheet,
-                        guards.ua_or_user,
-                        SheetRebuildKind::Full,
-                        &mut self.precomputed_pseudo_element_decls,
-                    )?;
-            }
-
-            if quirks_mode != QuirksMode::NoQuirks {
-                let stylesheet = &ua_stylesheets.quirks_mode_stylesheet;
-                let sheet_origin =
-                    stylesheet.contents(guards.ua_or_user).origin;
-
-                debug_assert!(matches!(
-                    sheet_origin,
-                    Origin::UserAgent | Origin::User
-                ));
-
-                let validity = flusher.origin_validity(sheet_origin);
-
-                // Servo doesn't support to incrementally mutate UA sheets.
-                debug_assert!(matches!(
-                    validity,
-                    OriginValidity::Valid | OriginValidity::FullyInvalid
-                ));
-
-                if validity != OriginValidity::Valid {
-                    self.per_origin
-                        .borrow_mut_for_origin(&sheet_origin)
-                        .add_stylesheet(
-                            device,
-                            quirks_mode,
-                            &ua_stylesheets.quirks_mode_stylesheet,
-                            guards.ua_or_user,
-                            SheetRebuildKind::Full,
-                            &mut self.precomputed_pseudo_element_decls,
-                        )?;
-                }
-            }
-        }
-
-        for (stylesheet, rebuild_kind) in flusher {
-            let origin = stylesheet.origin(guards.author);
+        for (stylesheet, origin, rebuild_kind) in flusher {
+            let guard = guards.for_origin(origin);
+            let origin = stylesheet.origin(guard);
             self.per_origin
                 .borrow_mut_for_origin(&origin)
                 .add_stylesheet(
                     device,
                     quirks_mode,
                     stylesheet,
-                    guards.author,
+                    guard,
                     rebuild_kind,
                     &mut self.precomputed_pseudo_element_decls,
                 )?;
         }
 
         Ok(())
     }
 
@@ -393,17 +322,16 @@ impl Stylist {
 
     /// Flush the list of stylesheets if they changed, ensuring the stylist is
     /// up-to-date.
     ///
     /// FIXME(emilio): Move the `ua_sheets` to the Stylist too?
     pub fn flush<E>(
         &mut self,
         guards: &StylesheetGuards,
-        ua_sheets: Option<&UserAgentStylesheets>,
         document_element: Option<E>,
     ) -> bool
     where
         E: TElement,
     {
         if !self.stylesheets.has_changed() {
             return false;
         }
@@ -422,43 +350,42 @@ impl Stylist {
             //
             // Processing it with the rest of rules seems tricky since it
             // overrides the viewport size which may change the evaluation of
             // media queries (or may not? how are viewport units in media
             // queries defined?)
             let cascaded_rule = ViewportRule {
                 declarations: viewport_rule::Cascade::from_stylesheets(
                     self.stylesheets.iter(),
-                    guards.author,
+                    guards,
                     &self.device,
                 ).finish()
             };
 
             self.viewport_constraints =
                 ViewportConstraints::maybe_new(
                     &self.device,
                     &cascaded_rule,
                     self.quirks_mode,
                 );
 
             if let Some(ref constraints) = self.viewport_constraints {
                 self.device.account_for_viewport_rule(constraints);
             }
         }
 
-        let flusher = self.stylesheets.flush(document_element, &guards.author);
+        let flusher = self.stylesheets.flush(document_element);
 
         let had_invalidations = flusher.had_invalidations();
 
         self.cascade_data.rebuild(
             &self.device,
             self.quirks_mode,
             flusher,
             guards,
-            ua_sheets,
         ).unwrap_or_else(|_| warn!("OOM in Stylist::flush"));
 
         had_invalidations
     }
 
     /// Insert a given stylesheet before another stylesheet in the document.
     pub fn insert_stylesheet_before(
         &mut self,
@@ -998,62 +925,61 @@ impl Stylist {
     /// Also, the device that arrives here may need to take the viewport rules
     /// into account.
     ///
     /// For Gecko, this is called when XBL bindings are used by different
     /// documents.
     pub fn set_device(
         &mut self,
         mut device: Device,
-        guard: &SharedRwLockReadGuard,
+        guards: &StylesheetGuards,
     ) -> OriginSet {
         if viewport_rule::enabled() {
             let cascaded_rule = {
                 let stylesheets = self.stylesheets.iter();
 
                 ViewportRule {
                     declarations: viewport_rule::Cascade::from_stylesheets(
                         stylesheets.clone(),
-                        guard,
+                        guards,
                         &device
                     ).finish(),
                 }
             };
 
             self.viewport_constraints =
                 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(guard)
+        self.media_features_change_changed_style(guards)
     }
 
     /// Returns whether, given a media feature change, any previously-applicable
     /// style has become non-applicable, or vice-versa for each origin.
     pub fn media_features_change_changed_style(
         &self,
-        guard: &SharedRwLockReadGuard,
+        guards: &StylesheetGuards,
     ) -> OriginSet {
         use invalidation::media_queries::PotentiallyEffectiveMediaRules;
 
         debug!("Stylist::media_features_change_changed_style");
 
         let mut origins = OriginSet::empty();
         let stylesheets = self.stylesheets.iter();
 
-        'stylesheets_loop: for stylesheet in stylesheets {
+        'stylesheets_loop: for (stylesheet, origin) in stylesheets {
+            let guard = guards.for_origin(origin);
             let effective_now =
                 stylesheet.is_effective_for_device(&self.device, guard);
 
-            let origin = stylesheet.origin(guard);
-
             if origins.contains(origin.into()) {
                 continue;
             }
 
             let origin_cascade_data =
                 self.cascade_data.per_origin.borrow_for_origin(&origin);
 
             let effective_then =
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -940,18 +940,19 @@ pub extern "C" fn Servo_StyleSet_MediumF
     // FIXME(emilio, bug 1369984): do the computation conditionally, to do it
     // less often.
     let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
 
     unsafe {
         *viewport_units_used = data.stylist.device().used_viewport_size();
     }
     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(&guard);
+        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]
@@ -959,18 +960,19 @@ pub extern "C" fn Servo_StyleSet_SetDevi
     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, &guard);
+        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
 }
 
 #[no_mangle]
--- a/servo/tests/unit/style/viewport.rs
+++ b/servo/tests/unit/style/viewport.rs
@@ -7,17 +7,17 @@ use euclid::ScaleFactor;
 use euclid::TypedSize2D;
 use media_queries::CSSErrorReporterTest;
 use servo_arc::Arc;
 use servo_config::prefs::{PREFS, PrefValue};
 use servo_url::ServoUrl;
 use style::context::QuirksMode;
 use style::media_queries::{Device, MediaList, MediaType};
 use style::parser::{ParserContext, ParserErrorContext};
-use style::shared_lock::SharedRwLock;
+use style::shared_lock::{SharedRwLock, StylesheetGuards};
 use style::stylesheets::{CssRuleType, Stylesheet, StylesheetInDocument, Origin};
 use style::stylesheets::viewport_rule::*;
 use style::values::specified::LengthOrPercentageOrAuto::{self, Auto};
 use style::values::specified::NoCalcLength::{self, ViewportPercentage};
 use style::values::specified::ViewportPercentageLength::Vw;
 use style_traits::{PARSING_MODE_DEFAULT, PinchZoomFactor};
 use style_traits::viewport::*;
 
@@ -266,36 +266,36 @@ fn multiple_stylesheets_cascading() {
                     UserAgent, error_reporter, shared_lock.clone()),
         stylesheet!("@viewport { min-width: 200px; min-height: 200px; }",
                     User, error_reporter, shared_lock.clone()),
         stylesheet!("@viewport { min-width: 300px; }",
                     Author, error_reporter, shared_lock.clone())
     ];
 
     let declarations = Cascade::from_stylesheets(
-        stylesheets.iter().map(|s| &**s),
-        &shared_lock.read(),
+        stylesheets.iter().map(|s| (&**s, Origin::Author)),
+        &StylesheetGuards::same(&shared_lock.read()),
         &device,
     ).finish();
     assert_decl_len!(declarations == 3);
     assert_decl_eq!(&declarations[0], UserAgent, Zoom: Zoom::Number(1.));
     assert_decl_eq!(&declarations[1], User, MinHeight: viewport_length!(200., px));
     assert_decl_eq!(&declarations[2], Author, MinWidth: viewport_length!(300., px));
 
     let stylesheets = vec![
         stylesheet!("@viewport { min-width: 100px !important; }",
                     UserAgent, error_reporter, shared_lock.clone()),
         stylesheet!("@viewport { min-width: 200px !important; min-height: 200px !important; }",
                     User, error_reporter, shared_lock.clone()),
         stylesheet!("@viewport { min-width: 300px !important; min-height: 300px !important; zoom: 3 !important; }",
                     Author, error_reporter, shared_lock.clone())
     ];
     let declarations = Cascade::from_stylesheets(
-        stylesheets.iter().map(|s| &**s),
-        &shared_lock.read(),
+        stylesheets.iter().map(|s| (&**s, Origin::Author)),
+        &StylesheetGuards::same(&shared_lock.read()),
         &device,
     ).finish();
     assert_decl_len!(declarations == 3);
     assert_decl_eq!(&declarations[0], UserAgent, MinWidth: viewport_length!(100., px), !important);
     assert_decl_eq!(&declarations[1], User, MinHeight: viewport_length!(200., px), !important);
     assert_decl_eq!(&declarations[2], Author, Zoom: Zoom::Number(3.), !important);
 }