Bug 1454030 - Allow placeholder import sheets. r=emilio
authorBobby Holley <bobbyholley@gmail.com>
Tue, 20 Feb 2018 15:52:13 -0800
changeset 467528 0b6f99cbcab6dc1c0366fee97b8bb9928d3cf322
parent 467527 ef9eec5460a77561b39b11e024a81a79551ac71e
child 467529 bb4fe8662ae37b2dd680f2d7383d81e1d3008c5f
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
bugs1454030
milestone61.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 1454030 - Allow placeholder import sheets. r=emilio This is necessary because we can't create GeckoStyleSheets off-main-thread, so we need a placeholder until it can be filled in. MozReview-Commit-ID: ssRme4fLYg
servo/components/style/gecko/data.rs
servo/components/style/stylesheet_set.rs
servo/components/style/stylesheets/import_rule.rs
servo/components/style/stylesheets/rules_iterator.rs
servo/components/style/stylesheets/stylesheet.rs
servo/ports/geckolib/glue.rs
servo/ports/geckolib/stylesheet_loader.rs
--- a/servo/components/style/gecko/data.rs
+++ b/servo/components/style/gecko/data.rs
@@ -1,28 +1,29 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Data needed to style a Gecko document.
 
 use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
+use context::QuirksMode;
 use dom::TElement;
 use gecko_bindings::bindings::{self, RawServoStyleSet};
 use gecko_bindings::structs::{self, RawGeckoPresContextOwned, ServoStyleSetSizes, ServoStyleSheet};
 use gecko_bindings::structs::{ServoStyleSheetInner, StyleSheetInfo, nsIDocument};
 use gecko_bindings::sugar::ownership::{HasArcFFI, HasBoxFFI, HasFFI, HasSimpleFFI};
 use invalidation::media_queries::{MediaListKey, ToMediaListKey};
 use malloc_size_of::MallocSizeOfOps;
 use media_queries::{Device, MediaList};
 use properties::ComputedValues;
 use selector_parser::SnapshotMap;
 use servo_arc::Arc;
 use shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
-use stylesheets::{StylesheetContents, StylesheetInDocument};
+use stylesheets::{CssRule, Origin, StylesheetContents, StylesheetInDocument};
 use stylist::Stylist;
 
 /// Little wrapper to a Gecko style sheet.
 #[derive(Debug, Eq, PartialEq)]
 pub struct GeckoStyleSheet(*const ServoStyleSheet);
 
 impl ToMediaListKey for ::gecko::data::GeckoStyleSheet {
     fn to_media_list_key(&self) -> MediaListKey {
@@ -53,39 +54,48 @@ impl GeckoStyleSheet {
         unsafe { &*self.0 }
     }
 
     fn inner(&self) -> &ServoStyleSheetInner {
         unsafe {
             &*(self.raw()._base.mInner as *const StyleSheetInfo as *const ServoStyleSheetInner)
         }
     }
+
+    /// Gets the StylesheetContents for this stylesheet.
+    pub fn contents(&self) -> &StylesheetContents {
+        debug_assert!(!self.inner().mContents.mRawPtr.is_null());
+        unsafe {
+            let contents =
+                (&**StylesheetContents::as_arc(&&*self.inner().mContents.mRawPtr)) as *const _;
+            &*contents
+        }
+    }
 }
 
 impl Drop for GeckoStyleSheet {
     fn drop(&mut self) {
         unsafe { bindings::Gecko_StyleSheet_Release(self.0) };
     }
 }
 
 impl Clone for GeckoStyleSheet {
     fn clone(&self) -> Self {
         unsafe { bindings::Gecko_StyleSheet_AddRef(self.0) };
         GeckoStyleSheet(self.0)
     }
 }
 
 impl StylesheetInDocument for GeckoStyleSheet {
-    fn contents(&self, _: &SharedRwLockReadGuard) -> &StylesheetContents {
-        debug_assert!(!self.inner().mContents.mRawPtr.is_null());
-        unsafe {
-            let contents =
-                (&**StylesheetContents::as_arc(&&*self.inner().mContents.mRawPtr)) as *const _;
-            &*contents
-        }
+    fn origin(&self, _guard: &SharedRwLockReadGuard) -> Origin {
+        self.contents().origin
+    }
+
+    fn quirks_mode(&self, _guard: &SharedRwLockReadGuard) -> QuirksMode {
+        self.contents().quirks_mode
     }
 
     fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
         use gecko_bindings::structs::ServoMediaList;
         use std::mem;
 
         unsafe {
             let servo_media_list = self.raw()._base.mMedia.mRawPtr as *const ServoMediaList;
@@ -98,16 +108,21 @@ impl StylesheetInDocument for GeckoStyle
         }
     }
 
     // All the stylesheets Servo knows about are enabled, because that state is
     // handled externally by Gecko.
     fn enabled(&self) -> bool {
         true
     }
+
+    #[inline]
+    fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
+        self.contents().rules(guard)
+    }
 }
 
 /// The container for data that a Servo-backed Gecko document needs to style
 /// itself.
 pub struct PerDocumentStyleDataImpl {
     /// Rule processor.
     pub stylist: Stylist,
 }
--- a/servo/components/style/stylesheet_set.rs
+++ b/servo/components/style/stylesheet_set.rs
@@ -473,17 +473,17 @@ where
         }
     }
 
     fn collection_for(
         &mut self,
         sheet: &S,
         guard: &SharedRwLockReadGuard,
     ) -> &mut SheetCollection<S> {
-        let origin = sheet.contents(guard).origin;
+        let origin = sheet.origin(guard);
         self.collections.borrow_mut_for_origin(&origin)
     }
 
     sheet_set_methods!("DocumentStylesheetSet");
 
     /// Returns the number of stylesheets in the set.
     pub fn len(&self) -> usize {
         self.collections
--- a/servo/components/style/stylesheets/import_rule.rs
+++ b/servo/components/style/stylesheets/import_rule.rs
@@ -1,65 +1,132 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! The [`@import`][import] at-rule.
 //!
 //! [import]: https://drafts.csswg.org/css-cascade-3/#at-import
 
+use context::QuirksMode;
 use cssparser::SourceLocation;
 use media_queries::MediaList;
 use shared_lock::{DeepCloneParams, DeepCloneWithLock};
 use shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
 use std::fmt::{self, Write};
 use str::CssStringWriter;
 use style_traits::{CssWriter, ToCss};
-use stylesheets::{StylesheetContents, StylesheetInDocument};
+use stylesheets::{CssRule, Origin, StylesheetInDocument};
 use values::CssUrl;
 
+/// With asynchronous stylesheet parsing, we can't synchronously create a
+/// GeckoStyleSheet. So we use this placeholder instead.
+#[derive(Clone, Debug)]
+pub struct PendingSheet {
+    origin: Origin,
+    quirks_mode: QuirksMode,
+}
+
 /// A sheet that is held from an import rule.
 #[cfg(feature = "gecko")]
 #[derive(Debug)]
-pub struct ImportSheet(pub ::gecko::data::GeckoStyleSheet);
+pub enum ImportSheet {
+    /// A bonafide stylesheet.
+    Sheet(::gecko::data::GeckoStyleSheet),
+    /// An @import created while parsing off-main-thread, whose Gecko sheet has
+    /// yet to be created and attached.
+    Pending(PendingSheet),
+}
+
+#[cfg(feature = "gecko")]
+impl ImportSheet {
+    /// Creates a new ImportSheet from a GeckoStyleSheet.
+    pub fn new(sheet: ::gecko::data::GeckoStyleSheet) -> Self {
+        ImportSheet::Sheet(sheet)
+    }
+
+    /// Creates a pending ImportSheet for a load that has not started yet.
+    pub fn new_pending(origin: Origin, quirks_mode: QuirksMode) -> Self {
+        ImportSheet::Pending(PendingSheet {
+            origin,
+            quirks_mode,
+        })
+    }
+
+    /// Returns a reference to the GeckoStyleSheet in this ImportSheet, if it
+    /// exists.
+    pub fn as_sheet(&self) -> Option<&::gecko::data::GeckoStyleSheet> {
+        match *self {
+            ImportSheet::Sheet(ref s) => Some(s),
+            ImportSheet::Pending(_) => None,
+        }
+    }
+}
 
 #[cfg(feature = "gecko")]
 impl DeepCloneWithLock for ImportSheet {
     fn deep_clone_with_lock(
         &self,
         _lock: &SharedRwLock,
         _guard: &SharedRwLockReadGuard,
         params: &DeepCloneParams,
     ) -> Self {
         use gecko::data::GeckoStyleSheet;
         use gecko_bindings::bindings;
-        let clone = unsafe {
-            bindings::Gecko_StyleSheet_Clone(self.0.raw() as *const _, params.reference_sheet)
-        };
-        ImportSheet(unsafe { GeckoStyleSheet::from_addrefed(clone) })
+        match *self {
+            ImportSheet::Sheet(ref s) => {
+                let clone = unsafe {
+                    bindings::Gecko_StyleSheet_Clone(s.raw() as *const _, params.reference_sheet)
+                };
+                ImportSheet::Sheet(unsafe { GeckoStyleSheet::from_addrefed(clone) })
+            },
+            ImportSheet::Pending(ref p) => ImportSheet::Pending(p.clone()),
+        }
     }
 }
 
 /// A sheet that is held from an import rule.
 #[cfg(feature = "servo")]
 #[derive(Debug)]
 pub struct ImportSheet(pub ::servo_arc::Arc<::stylesheets::Stylesheet>);
 
 impl StylesheetInDocument for ImportSheet {
-    /// Get the media associated with this stylesheet.
-    fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
-        self.0.media(guard)
+    fn origin(&self, _guard: &SharedRwLockReadGuard) -> Origin {
+        match *self {
+            ImportSheet::Sheet(ref s) => s.contents().origin,
+            ImportSheet::Pending(ref p) => p.origin,
+        }
     }
 
-    fn contents(&self, guard: &SharedRwLockReadGuard) -> &StylesheetContents {
-        self.0.contents(guard)
+    fn quirks_mode(&self, _guard: &SharedRwLockReadGuard) -> QuirksMode {
+        match *self {
+            ImportSheet::Sheet(ref s) => s.contents().quirks_mode,
+            ImportSheet::Pending(ref p) => p.quirks_mode,
+        }
     }
 
     fn enabled(&self) -> bool {
-        self.0.enabled()
+        match *self {
+            ImportSheet::Sheet(ref s) => s.enabled(),
+            ImportSheet::Pending(_) => true,
+        }
+    }
+
+    fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
+        match *self {
+            ImportSheet::Sheet(ref s) => s.media(guard),
+            ImportSheet::Pending(_) => None,
+        }
+    }
+
+    fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
+        match *self {
+            ImportSheet::Sheet(ref s) => s.contents().rules(guard),
+            ImportSheet::Pending(_) => &[],
+        }
     }
 }
 
 #[cfg(feature = "servo")]
 impl DeepCloneWithLock for ImportSheet {
     fn deep_clone_with_lock(
         &self,
         _lock: &SharedRwLock,
--- a/servo/components/style/stylesheets/rules_iterator.rs
+++ b/servo/components/style/stylesheets/rules_iterator.rs
@@ -4,18 +4,19 @@
 
 //! An iterator over a list of rules.
 
 use context::QuirksMode;
 use media_queries::Device;
 use shared_lock::SharedRwLockReadGuard;
 use smallvec::SmallVec;
 use std::slice;
-use stylesheets::{CssRule, CssRules, DocumentRule, ImportRule, MediaRule, SupportsRule};
+use stylesheets::{CssRule, DocumentRule, ImportRule, MediaRule, SupportsRule};
 use stylesheets::StylesheetInDocument;
+use stylesheets::import_rule::ImportSheet;
 
 /// An iterator over a list of rules.
 pub struct RulesIterator<'a, 'b, C>
 where
     'b: 'a,
     C: NestedRuleIterationCondition + 'static,
 {
     device: &'a Device,
@@ -30,20 +31,20 @@ where
     'b: 'a,
     C: NestedRuleIterationCondition + 'static,
 {
     /// Creates a new `RulesIterator` to iterate over `rules`.
     pub fn new(
         device: &'a Device,
         quirks_mode: QuirksMode,
         guard: &'a SharedRwLockReadGuard<'b>,
-        rules: &'a CssRules,
+        rules: &'a [CssRule],
     ) -> Self {
         let mut stack = SmallVec::new();
-        stack.push(rules.0.iter());
+        stack.push(rules.iter());
         Self {
             device: device,
             quirks_mode: quirks_mode,
             guard: guard,
             stack: stack,
             _phantom: ::std::marker::PhantomData,
         }
     }
@@ -97,20 +98,17 @@ where
                             self.device,
                             self.quirks_mode,
                             import_rule,
                         ) {
                             continue;
                         }
                         import_rule
                             .stylesheet
-                            .contents(self.guard)
-                            .rules
-                            .read_with(self.guard)
-                            .0
+                            .rules(self.guard)
                             .iter()
                     },
                     CssRule::Document(ref doc_rule) => {
                         let doc_rule = doc_rule.read_with(self.guard);
                         if !C::process_document(self.guard, self.device, self.quirks_mode, doc_rule)
                         {
                             continue;
                         }
--- a/servo/components/style/stylesheets/stylesheet.rs
+++ b/servo/components/style/stylesheets/stylesheet.rs
@@ -98,32 +98,20 @@ impl StylesheetContents {
             url_data: RwLock::new(url_data),
             namespaces: namespaces,
             quirks_mode: quirks_mode,
             source_map_url: RwLock::new(source_map_url),
             source_url: RwLock::new(source_url),
         }
     }
 
-    /// Return an iterator using the condition `C`.
+    /// Returns a reference to the list of rules.
     #[inline]
-    pub fn iter_rules<'a, 'b, C>(
-        &'a self,
-        device: &'a Device,
-        guard: &'a SharedRwLockReadGuard<'b>,
-    ) -> RulesIterator<'a, 'b, C>
-    where
-        C: NestedRuleIterationCondition,
-    {
-        RulesIterator::new(
-            device,
-            self.quirks_mode,
-            guard,
-            &self.rules.read_with(guard),
-        )
+    pub fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
+        &self.rules.read_with(guard).0
     }
 
     /// Measure heap usage.
     #[cfg(feature = "gecko")]
     pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
         // Measurement of other fields may be added later.
         self.rules.unconditional_shallow_size_of(ops) +
             self.rules.read_with(guard).size_of(guard, ops)
@@ -184,54 +172,50 @@ macro_rules! rule_filter {
                 }
             }
         )+
     }
 }
 
 /// A trait to represent a given stylesheet in a document.
 pub trait StylesheetInDocument {
-    /// Get the contents of this stylesheet.
-    fn contents(&self, guard: &SharedRwLockReadGuard) -> &StylesheetContents;
-
     /// Get the stylesheet origin.
-    fn origin(&self, guard: &SharedRwLockReadGuard) -> Origin {
-        self.contents(guard).origin
-    }
+    fn origin(&self, guard: &SharedRwLockReadGuard) -> Origin;
 
     /// Get the stylesheet quirks mode.
-    fn quirks_mode(&self, guard: &SharedRwLockReadGuard) -> QuirksMode {
-        self.contents(guard).quirks_mode
-    }
+    fn quirks_mode(&self, guard: &SharedRwLockReadGuard) -> QuirksMode;
+
+    /// Get whether this stylesheet is enabled.
+    fn enabled(&self) -> bool;
 
     /// Get the media associated with this stylesheet.
     fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList>;
 
-    /// Returns whether the style-sheet applies for the current device.
-    fn is_effective_for_device(&self, device: &Device, guard: &SharedRwLockReadGuard) -> bool {
-        match self.media(guard) {
-            Some(medialist) => medialist.evaluate(device, self.quirks_mode(guard)),
-            None => true,
-        }
-    }
-
-    /// Get whether this stylesheet is enabled.
-    fn enabled(&self) -> bool;
+    /// Returns a reference to the list of rules in this stylesheet.
+    fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule];
 
     /// Return an iterator using the condition `C`.
     #[inline]
     fn iter_rules<'a, 'b, C>(
         &'a self,
         device: &'a Device,
         guard: &'a SharedRwLockReadGuard<'b>,
     ) -> RulesIterator<'a, 'b, C>
     where
         C: NestedRuleIterationCondition,
     {
-        self.contents(guard).iter_rules(device, guard)
+        RulesIterator::new(device, self.quirks_mode(guard), guard, self.rules(guard))
+    }
+
+    /// Returns whether the style-sheet applies for the current device.
+    fn is_effective_for_device(&self, device: &Device, guard: &SharedRwLockReadGuard) -> bool {
+        match self.media(guard) {
+            Some(medialist) => medialist.evaluate(device, self.quirks_mode(guard)),
+            None => true,
+        }
     }
 
     /// Return an iterator over the effective rules within the style-sheet, as
     /// according to the supplied `Device`.
     #[inline]
     fn effective_rules<'a, 'b>(
         &'a self,
         device: &'a Device,
@@ -250,27 +234,36 @@ pub trait StylesheetInDocument {
         effective_keyframes_rules(Keyframes => KeyframesRule),
         effective_supports_rules(Supports => SupportsRule),
         effective_page_rules(Page => PageRule),
         effective_document_rules(Document => DocumentRule),
     }
 }
 
 impl StylesheetInDocument for Stylesheet {
-    fn contents(&self, _: &SharedRwLockReadGuard) -> &StylesheetContents {
-        &self.contents
+    fn origin(&self, _guard: &SharedRwLockReadGuard) -> Origin {
+        self.contents.origin
+    }
+
+    fn quirks_mode(&self, _guard: &SharedRwLockReadGuard) -> QuirksMode {
+        self.contents.quirks_mode
     }
 
     fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
         Some(self.media.read_with(guard))
     }
 
     fn enabled(&self) -> bool {
         !self.disabled()
     }
+
+    #[inline]
+    fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
+        self.contents.rules(guard)
+    }
 }
 
 /// A simple wrapper over an `Arc<Stylesheet>`, with pointer comparison, and
 /// suitable for its use in a `StylesheetSet`.
 #[derive(Clone)]
 #[cfg_attr(feature = "servo", derive(MallocSizeOf))]
 pub struct DocumentStyleSheet(
     #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")] pub Arc<Stylesheet>,
@@ -284,27 +277,36 @@ impl PartialEq for DocumentStyleSheet {
 
 impl ToMediaListKey for DocumentStyleSheet {
     fn to_media_list_key(&self) -> MediaListKey {
         self.0.to_media_list_key()
     }
 }
 
 impl StylesheetInDocument for DocumentStyleSheet {
-    fn contents(&self, guard: &SharedRwLockReadGuard) -> &StylesheetContents {
-        self.0.contents(guard)
+    fn origin(&self, guard: &SharedRwLockReadGuard) -> Origin {
+        self.0.origin(guard)
+    }
+
+    fn quirks_mode(&self, guard: &SharedRwLockReadGuard) -> QuirksMode {
+        self.0.quirks_mode(guard)
     }
 
     fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
         self.0.media(guard)
     }
 
     fn enabled(&self) -> bool {
         self.0.enabled()
     }
+
+    #[inline]
+    fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
+        self.0.rules(guard)
+    }
 }
 
 impl Stylesheet {
     /// Updates an empty stylesheet from a given string of text.
     pub fn update_from_str<R>(
         existing: &Stylesheet,
         css: &str,
         url_data: UrlExtraData,
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -2043,17 +2043,17 @@ pub extern "C" fn Servo_ImportRule_GetHr
     })
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_ImportRule_GetSheet(
     rule: RawServoImportRuleBorrowed,
 ) -> *const ServoStyleSheet {
     read_locked_arc(rule, |rule: &ImportRule| {
-        rule.stylesheet.0.raw() as *const ServoStyleSheet
+        rule.stylesheet.as_sheet().unwrap().raw() as *const ServoStyleSheet
     })
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_Keyframe_GetKeyText(
     keyframe: RawServoKeyframeBorrowed,
     result: *mut nsAString
 ) {
--- a/servo/ports/geckolib/stylesheet_loader.rs
+++ b/servo/ports/geckolib/stylesheet_loader.rs
@@ -50,14 +50,13 @@ impl StyleStylesheetLoader for Styleshee
                                  base_url_data,
                                  spec_bytes,
                                  spec_len as u32,
                                  media.into_strong())
         };
 
         debug_assert!(!child_sheet.is_null(),
                       "Import rules should always have a strong sheet");
-        let stylesheet = unsafe {
-            ImportSheet(GeckoStyleSheet::from_addrefed(child_sheet))
-        };
+        let sheet = unsafe { GeckoStyleSheet::from_addrefed(child_sheet) };
+        let stylesheet = ImportSheet::new(sheet);
         Arc::new(lock.wrap(ImportRule { url, source_location, stylesheet }))
     }
 }