aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/script/dom/bindings/trace.rs5
-rw-r--r--components/script/dom/css.rs2
-rw-r--r--components/script/dom/csskeyframerule.rs2
-rw-r--r--components/script/dom/csskeyframesrule.rs3
-rw-r--r--components/script/dom/csssupportsrule.rs2
-rw-r--r--components/script/dom/cssviewportrule.rs2
-rw-r--r--components/script/dom/htmlmetaelement.rs3
-rw-r--r--components/style/animation.rs2
-rw-r--r--components/style/gecko/arc_types.rs2
-rw-r--r--components/style/lib.rs4
-rw-r--r--components/style/shared_lock.rs11
-rw-r--r--components/style/stylesheets.rs1943
-rw-r--r--components/style/stylesheets/counter_style_rule.rs12
-rw-r--r--components/style/stylesheets/document_rule.rs (renamed from components/style/document_condition.rs)57
-rw-r--r--components/style/stylesheets/font_face_rule.rs12
-rw-r--r--components/style/stylesheets/import_rule.rs59
-rw-r--r--components/style/stylesheets/keyframes_rule.rs (renamed from components/style/keyframes.rs)91
-rw-r--r--components/style/stylesheets/loader.rs43
-rw-r--r--components/style/stylesheets/media_rule.rs60
-rw-r--r--components/style/stylesheets/memory.rs71
-rw-r--r--components/style/stylesheets/mod.rs344
-rw-r--r--components/style/stylesheets/namespace_rule.rs39
-rw-r--r--components/style/stylesheets/page_rule.rs60
-rw-r--r--components/style/stylesheets/rule_list.rs168
-rw-r--r--components/style/stylesheets/rule_parser.rs520
-rw-r--r--components/style/stylesheets/rules_iterator.rs273
-rw-r--r--components/style/stylesheets/style_rule.rs76
-rw-r--r--components/style/stylesheets/stylesheet.rs342
-rw-r--r--components/style/stylesheets/supports_rule.rs (renamed from components/style/supports.rs)61
-rw-r--r--components/style/stylesheets/viewport_rule.rs (renamed from components/style/viewport.rs)2
-rw-r--r--components/style/stylist.rs8
-rw-r--r--ports/geckolib/glue.rs4
-rw-r--r--tests/unit/style/keyframes.rs4
-rw-r--r--tests/unit/style/parsing/supports.rs2
-rw-r--r--tests/unit/style/stylesheets.rs2
-rw-r--r--tests/unit/style/viewport.rs2
36 files changed, 2298 insertions, 1995 deletions
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs
index 64ef88da5e3..db3cd3b5c42 100644
--- a/components/script/dom/bindings/trace.rs
+++ b/components/script/dom/bindings/trace.rs
@@ -95,16 +95,15 @@ use std::time::{SystemTime, Instant};
use style::attr::{AttrIdentifier, AttrValue, LengthOrPercentageOrAuto};
use style::context::QuirksMode;
use style::element_state::*;
-use style::keyframes::Keyframe;
use style::media_queries::MediaList;
use style::properties::PropertyDeclarationBlock;
use style::selector_parser::{PseudoElement, Snapshot};
use style::shared_lock::{SharedRwLock as StyleSharedRwLock, Locked as StyleLocked};
use style::stylearc::Arc as StyleArc;
use style::stylesheets::{CssRules, FontFaceRule, KeyframesRule, MediaRule};
-use style::stylesheets::{NamespaceRule, StyleRule, ImportRule, SupportsRule};
+use style::stylesheets::{NamespaceRule, StyleRule, ImportRule, SupportsRule, ViewportRule};
+use style::stylesheets::keyframes_rule::Keyframe;
use style::values::specified::Length;
-use style::viewport::ViewportRule;
use time::Duration;
use uuid::Uuid;
use webrender_traits::{WebGLBufferId, WebGLError, WebGLFramebufferId, WebGLProgramId};
diff --git a/components/script/dom/css.rs b/components/script/dom/css.rs
index d56575b9d38..521c09ed4c2 100644
--- a/components/script/dom/css.rs
+++ b/components/script/dom/css.rs
@@ -12,7 +12,7 @@ use dom_struct::dom_struct;
use style::context::QuirksMode;
use style::parser::{PARSING_MODE_DEFAULT, ParserContext};
use style::stylesheets::CssRuleType;
-use style::supports::{Declaration, parse_condition_or_declaration};
+use style::stylesheets::supports_rule::{Declaration, parse_condition_or_declaration};
#[dom_struct]
pub struct CSS {
diff --git a/components/script/dom/csskeyframerule.rs b/components/script/dom/csskeyframerule.rs
index 46dac811d9f..bd8b0a47fc0 100644
--- a/components/script/dom/csskeyframerule.rs
+++ b/components/script/dom/csskeyframerule.rs
@@ -12,9 +12,9 @@ use dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration, CSSSt
use dom::cssstylesheet::CSSStyleSheet;
use dom::window::Window;
use dom_struct::dom_struct;
-use style::keyframes::Keyframe;
use style::shared_lock::{Locked, ToCssWithGuard};
use style::stylearc::Arc;
+use style::stylesheets::keyframes_rule::Keyframe;
#[dom_struct]
pub struct CSSKeyframeRule {
diff --git a/components/script/dom/csskeyframesrule.rs b/components/script/dom/csskeyframesrule.rs
index f48abe60751..42cb4b374fa 100644
--- a/components/script/dom/csskeyframesrule.rs
+++ b/components/script/dom/csskeyframesrule.rs
@@ -16,10 +16,9 @@ use dom::cssrulelist::{CSSRuleList, RulesSource};
use dom::cssstylesheet::CSSStyleSheet;
use dom::window::Window;
use dom_struct::dom_struct;
-use style::keyframes::{Keyframe, KeyframeSelector};
use style::shared_lock::{Locked, ToCssWithGuard};
use style::stylearc::Arc;
-use style::stylesheets::KeyframesRule;
+use style::stylesheets::keyframes_rule::{KeyframesRule, Keyframe, KeyframeSelector};
use style::values::KeyframesName;
#[dom_struct]
diff --git a/components/script/dom/csssupportsrule.rs b/components/script/dom/csssupportsrule.rs
index cba47f0acd7..5d01c2cfdd3 100644
--- a/components/script/dom/csssupportsrule.rs
+++ b/components/script/dom/csssupportsrule.rs
@@ -17,7 +17,7 @@ use style::parser::{PARSING_MODE_DEFAULT, ParserContext};
use style::shared_lock::{Locked, ToCssWithGuard};
use style::stylearc::Arc;
use style::stylesheets::{CssRuleType, SupportsRule};
-use style::supports::SupportsCondition;
+use style::stylesheets::supports_rule::SupportsCondition;
use style_traits::ToCss;
#[dom_struct]
diff --git a/components/script/dom/cssviewportrule.rs b/components/script/dom/cssviewportrule.rs
index 4438aabb53e..a3b259a4ab5 100644
--- a/components/script/dom/cssviewportrule.rs
+++ b/components/script/dom/cssviewportrule.rs
@@ -12,7 +12,7 @@ use dom::window::Window;
use dom_struct::dom_struct;
use style::shared_lock::{Locked, ToCssWithGuard};
use style::stylearc::Arc;
-use style::viewport::ViewportRule;
+use style::stylesheets::ViewportRule;
#[dom_struct]
pub struct CSSViewportRule {
diff --git a/components/script/dom/htmlmetaelement.rs b/components/script/dom/htmlmetaelement.rs
index 30bce19c299..888855dc7b6 100644
--- a/components/script/dom/htmlmetaelement.rs
+++ b/components/script/dom/htmlmetaelement.rs
@@ -26,8 +26,7 @@ use style::attr::AttrValue;
use style::media_queries::MediaList;
use style::str::HTML_SPACE_CHARACTERS;
use style::stylearc::Arc;
-use style::stylesheets::{Stylesheet, CssRule, CssRules, Origin};
-use style::viewport::ViewportRule;
+use style::stylesheets::{Stylesheet, CssRule, CssRules, Origin, ViewportRule};
#[dom_struct]
pub struct HTMLMetaElement {
diff --git a/components/style/animation.rs b/components/style/animation.rs
index d7eab3f314e..005d7948e7a 100644
--- a/components/style/animation.rs
+++ b/components/style/animation.rs
@@ -11,7 +11,6 @@ use context::SharedStyleContext;
use dom::OpaqueNode;
use euclid::point::Point2D;
use font_metrics::FontMetricsProvider;
-use keyframes::{KeyframesStep, KeyframesStepValue};
use properties::{self, CascadeFlags, ComputedValues, Importance};
use properties::animated_properties::{AnimatedProperty, TransitionProperty};
use properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection;
@@ -22,6 +21,7 @@ use properties::longhands::transition_timing_function::single_value::computed_va
use rule_tree::CascadeLevel;
use std::sync::mpsc::Sender;
use stylearc::Arc;
+use stylesheets::keyframes_rule::{KeyframesStep, KeyframesStepValue};
use timer::Timer;
use values::computed::Time;
diff --git a/components/style/gecko/arc_types.rs b/components/style/gecko/arc_types.rs
index 6619059d54c..3ff90bc9584 100644
--- a/components/style/gecko/arc_types.rs
+++ b/components/style/gecko/arc_types.rs
@@ -17,7 +17,6 @@ use gecko_bindings::bindings::{ServoComputedValues, ServoCssRules};
use gecko_bindings::structs::{RawServoDeclarationBlock, RawServoStyleRule};
use gecko_bindings::structs::RawServoAnimationValue;
use gecko_bindings::sugar::ownership::{HasArcFFI, HasFFI};
-use keyframes::Keyframe;
use media_queries::MediaList;
use properties::{ComputedValues, PropertyDeclarationBlock};
use properties::animated_properties::AnimationValue;
@@ -26,6 +25,7 @@ use shared_lock::Locked;
use std::{mem, ptr};
use stylesheets::{CssRules, Stylesheet, StyleRule, ImportRule, KeyframesRule, MediaRule};
use stylesheets::{NamespaceRule, PageRule, SupportsRule, DocumentRule};
+use stylesheets::keyframes_rule::Keyframe;
macro_rules! impl_arc_ffi {
($servo_type:ty => $gecko_type:ty [$addref:ident, $release:ident]) => {
diff --git a/components/style/lib.rs b/components/style/lib.rs
index 3c4241b0df9..4856610b9ed 100644
--- a/components/style/lib.rs
+++ b/components/style/lib.rs
@@ -101,7 +101,6 @@ pub mod context;
pub mod counter_style;
pub mod custom_properties;
pub mod data;
-pub mod document_condition;
pub mod dom;
pub mod element_state;
#[cfg(feature = "servo")] mod encoding_support;
@@ -111,7 +110,6 @@ pub mod font_metrics;
#[cfg(feature = "gecko")] #[allow(unsafe_code)] pub mod gecko;
#[cfg(feature = "gecko")] #[allow(unsafe_code)] pub mod gecko_bindings;
pub mod invalidation;
-pub mod keyframes;
#[allow(missing_docs)] // TODO.
pub mod logical_geometry;
pub mod matching;
@@ -134,14 +132,12 @@ pub mod style_adjuster;
pub mod stylearc;
pub mod stylesheet_set;
pub mod stylesheets;
-pub mod supports;
pub mod thread_state;
pub mod timer;
pub mod traversal;
#[macro_use]
#[allow(non_camel_case_types)]
pub mod values;
-pub mod viewport;
use std::fmt;
use style_traits::ToCss;
diff --git a/components/style/shared_lock.rs b/components/style/shared_lock.rs
index 360118d472e..c87008a85a0 100644
--- a/components/style/shared_lock.rs
+++ b/components/style/shared_lock.rs
@@ -226,6 +226,17 @@ pub trait ToCssWithGuard {
}
}
+/// A trait to do a deep clone of a given CSS type. Gets a lock and a read
+/// guard, in order to be able to read and clone nested structures.
+pub trait DeepCloneWithLock : Sized {
+ /// Deep clones this object.
+ fn deep_clone_with_lock(
+ &self,
+ lock: &SharedRwLock,
+ guard: &SharedRwLockReadGuard
+ ) -> Self;
+}
+
/// Guards for a document
#[derive(Clone)]
pub struct StylesheetGuards<'a> {
diff --git a/components/style/stylesheets.rs b/components/style/stylesheets.rs
deleted file mode 100644
index 7196bd35034..00000000000
--- a/components/style/stylesheets.rs
+++ /dev/null
@@ -1,1943 +0,0 @@
-/* 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/. */
-
-//! Style sheets and their CSS rules.
-
-#![deny(missing_docs)]
-
-use {Atom, Prefix, Namespace};
-use context::QuirksMode;
-use counter_style::{parse_counter_style_name, parse_counter_style_body};
-#[cfg(feature = "servo")]
-use counter_style::CounterStyleRuleData;
-use cssparser::{AtRuleParser, Parser, QualifiedRuleParser};
-use cssparser::{AtRuleType, RuleListParser, parse_one_rule, SourceLocation};
-use cssparser::ToCss as ParserToCss;
-use document_condition::DocumentCondition;
-use error_reporting::{ParseErrorReporter, NullReporter};
-#[cfg(feature = "servo")]
-use font_face::FontFaceRuleData;
-use font_face::parse_font_face_block;
-#[cfg(feature = "gecko")]
-pub use gecko::rules::{CounterStyleRule, FontFaceRule};
-#[cfg(feature = "gecko")]
-use gecko_bindings::structs::URLExtraData;
-#[cfg(feature = "gecko")]
-use gecko_bindings::sugar::refptr::RefPtr;
-use keyframes::{Keyframe, KeyframeSelector, parse_keyframe_list};
-use media_queries::{Device, MediaList, parse_media_query_list};
-use parking_lot::RwLock;
-use parser::{PARSING_MODE_DEFAULT, Parse, ParserContext, log_css_error};
-use properties::{PropertyDeclarationBlock, parse_property_declaration_list};
-use selector_parser::{SelectorImpl, SelectorParser};
-use selectors::parser::SelectorList;
-#[cfg(feature = "servo")]
-use servo_config::prefs::PREFS;
-#[cfg(not(feature = "gecko"))]
-use servo_url::ServoUrl;
-use shared_lock::{SharedRwLock, Locked, ToCssWithGuard, SharedRwLockReadGuard};
-use smallvec::SmallVec;
-use std::{fmt, mem};
-use std::borrow::Borrow;
-use std::mem::align_of;
-use std::os::raw::c_void;
-use std::slice;
-use std::sync::atomic::{AtomicBool, Ordering};
-use str::starts_with_ignore_ascii_case;
-use style_traits::ToCss;
-use stylearc::Arc;
-use stylist::FnvHashMap;
-use supports::SupportsCondition;
-use values::{CustomIdent, KeyframesName};
-use values::specified::NamespaceId;
-use values::specified::url::SpecifiedUrl;
-use viewport::ViewportRule;
-
-
-/// Extra data that the backend may need to resolve url values.
-#[cfg(not(feature = "gecko"))]
-pub type UrlExtraData = ServoUrl;
-
-/// Extra data that the backend may need to resolve url values.
-#[cfg(feature = "gecko")]
-pub type UrlExtraData = RefPtr<URLExtraData>;
-
-#[cfg(feature = "gecko")]
-impl UrlExtraData {
- /// Returns a string for the url.
- ///
- /// Unimplemented currently.
- pub fn as_str(&self) -> &str {
- // TODO
- "(stylo: not supported)"
- }
-}
-
-// XXX We probably need to figure out whether we should mark Eq here.
-// It is currently marked so because properties::UnparsedValue wants Eq.
-#[cfg(feature = "gecko")]
-impl Eq for UrlExtraData {}
-
-/// Each style rule has an origin, which determines where it enters the cascade.
-///
-/// http://dev.w3.org/csswg/css-cascade/#cascading-origins
-#[derive(Clone, PartialEq, Eq, Copy, Debug)]
-#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-pub enum Origin {
- /// http://dev.w3.org/csswg/css-cascade/#cascade-origin-ua
- UserAgent,
-
- /// http://dev.w3.org/csswg/css-cascade/#cascade-origin-author
- Author,
-
- /// http://dev.w3.org/csswg/css-cascade/#cascade-origin-user
- User,
-}
-
-/// A set of namespaces applying to a given stylesheet.
-///
-/// The namespace id is used in gecko
-#[derive(Clone, Default, Debug)]
-#[allow(missing_docs)]
-pub struct Namespaces {
- pub default: Option<(Namespace, NamespaceId)>,
- pub prefixes: FnvHashMap<Prefix, (Namespace, NamespaceId)>,
-}
-
-/// Like gecko_bindings::structs::MallocSizeOf, but without the Option<> wrapper. Note that
-/// functions of this type should not be called via do_malloc_size_of(), rather than directly.
-pub type MallocSizeOfFn = unsafe extern "C" fn(ptr: *const c_void) -> usize;
-
-/// Call malloc_size_of on ptr, first checking that the allocation isn't empty.
-pub unsafe fn do_malloc_size_of<T>(malloc_size_of: MallocSizeOfFn, ptr: *const T) -> usize {
- if ptr as usize <= align_of::<T>() {
- 0
- } else {
- malloc_size_of(ptr as *const c_void)
- }
-}
-
-/// Trait for measuring the size of heap data structures.
-pub trait MallocSizeOf {
- /// Measure the size of any heap-allocated structures that hang off this value, but not the
- /// space taken up by the value itself.
- fn malloc_size_of_children(&self, malloc_size_of: MallocSizeOfFn) -> usize;
-}
-
-/// Like MallocSizeOf, but operates with the global SharedRwLockReadGuard locked.
-pub trait MallocSizeOfWithGuard {
- /// Like MallocSizeOf::malloc_size_of_children, but with a |guard| argument.
- fn malloc_size_of_children(&self, guard: &SharedRwLockReadGuard,
- malloc_size_of: MallocSizeOfFn) -> usize;
-}
-
-impl<A: MallocSizeOf, B: MallocSizeOf> MallocSizeOf for (A, B) {
- fn malloc_size_of_children(&self, malloc_size_of: MallocSizeOfFn) -> usize {
- self.0.malloc_size_of_children(malloc_size_of) +
- self.1.malloc_size_of_children(malloc_size_of)
- }
-}
-
-impl<T: MallocSizeOf> MallocSizeOf for Vec<T> {
- fn malloc_size_of_children(&self, malloc_size_of: MallocSizeOfFn) -> usize {
- self.iter().fold(
- unsafe { do_malloc_size_of(malloc_size_of, self.as_ptr()) },
- |n, elem| n + elem.malloc_size_of_children(malloc_size_of))
- }
-}
-
-impl<T: MallocSizeOfWithGuard> MallocSizeOfWithGuard for Vec<T> {
- fn malloc_size_of_children(&self, guard: &SharedRwLockReadGuard,
- malloc_size_of: MallocSizeOfFn) -> usize {
- self.iter().fold(
- unsafe { do_malloc_size_of(malloc_size_of, self.as_ptr()) },
- |n, elem| n + elem.malloc_size_of_children(guard, malloc_size_of))
- }
-}
-
-/// A list of CSS rules.
-#[derive(Debug)]
-pub struct CssRules(pub Vec<CssRule>);
-
-impl CssRules {
- /// Whether this CSS rules is empty.
- pub fn is_empty(&self) -> bool {
- self.0.is_empty()
- }
-
- /// Creates a deep clone where each CssRule has also been cloned with
- /// the provided lock.
- fn deep_clone_with_lock(&self,
- lock: &SharedRwLock) -> CssRules {
- CssRules(
- self.0.iter().map(|ref x| x.deep_clone_with_lock(lock)).collect()
- )
- }
-}
-
-impl MallocSizeOfWithGuard for CssRules {
- fn malloc_size_of_children(&self, guard: &SharedRwLockReadGuard,
- malloc_size_of: MallocSizeOfFn) -> usize {
- self.0.malloc_size_of_children(guard, malloc_size_of)
- }
-}
-
-#[allow(missing_docs)]
-pub enum RulesMutateError {
- Syntax,
- IndexSize,
- HierarchyRequest,
- InvalidState,
-}
-
-impl From<SingleRuleParseError> for RulesMutateError {
- fn from(other: SingleRuleParseError) -> Self {
- match other {
- SingleRuleParseError::Syntax => RulesMutateError::Syntax,
- SingleRuleParseError::Hierarchy => RulesMutateError::HierarchyRequest,
- }
- }
-}
-
-impl CssRules {
- #[allow(missing_docs)]
- pub fn new(rules: Vec<CssRule>, shared_lock: &SharedRwLock) -> Arc<Locked<CssRules>> {
- Arc::new(shared_lock.wrap(CssRules(rules)))
- }
-
- fn only_ns_or_import(&self) -> bool {
- self.0.iter().all(|r| {
- match *r {
- CssRule::Namespace(..) |
- CssRule::Import(..) => true,
- _ => false
- }
- })
- }
-
- /// https://drafts.csswg.org/cssom/#remove-a-css-rule
- pub fn remove_rule(&mut self, index: usize) -> Result<(), RulesMutateError> {
- // Step 1, 2
- if index >= self.0.len() {
- return Err(RulesMutateError::IndexSize);
- }
-
- {
- // Step 3
- let ref rule = self.0[index];
-
- // Step 4
- if let CssRule::Namespace(..) = *rule {
- if !self.only_ns_or_import() {
- return Err(RulesMutateError::InvalidState);
- }
- }
- }
-
- // Step 5, 6
- self.0.remove(index);
- Ok(())
- }
-}
-
-/// A trait to implement helpers for `Arc<Locked<CssRules>>`.
-pub trait CssRulesHelpers {
- /// https://drafts.csswg.org/cssom/#insert-a-css-rule
- ///
- /// Written in this funky way because parsing an @import rule may cause us
- /// to clone a stylesheet from the same document due to caching in the CSS
- /// loader.
- ///
- /// TODO(emilio): We could also pass the write guard down into the loader
- /// instead, but that seems overkill.
- fn insert_rule(&self,
- lock: &SharedRwLock,
- rule: &str,
- parent_stylesheet: &Stylesheet,
- index: usize,
- nested: bool,
- loader: Option<&StylesheetLoader>)
- -> Result<CssRule, RulesMutateError>;
-}
-
-impl CssRulesHelpers for Arc<Locked<CssRules>> {
- fn insert_rule(&self,
- lock: &SharedRwLock,
- rule: &str,
- parent_stylesheet: &Stylesheet,
- index: usize,
- nested: bool,
- loader: Option<&StylesheetLoader>)
- -> Result<CssRule, RulesMutateError> {
- let state = {
- let read_guard = lock.read();
- let rules = self.read_with(&read_guard);
-
- // Step 1, 2
- if index > rules.0.len() {
- return Err(RulesMutateError::IndexSize);
- }
-
- // Computes the parser state at the given index
- if nested {
- None
- } else if index == 0 {
- Some(State::Start)
- } else {
- rules.0.get(index - 1).map(CssRule::rule_state)
- }
- };
-
- // Step 3, 4
- // XXXManishearth should we also store the namespace map?
- let (new_rule, new_state) =
- try!(CssRule::parse(&rule, parent_stylesheet, state, loader));
-
- {
- let mut write_guard = lock.write();
- let mut rules = self.write_with(&mut write_guard);
- // Step 5
- // Computes the maximum allowed parser state at a given index.
- let rev_state = rules.0.get(index).map_or(State::Body, CssRule::rule_state);
- if new_state > rev_state {
- // We inserted a rule too early, e.g. inserting
- // a regular style rule before @namespace rules
- return Err(RulesMutateError::HierarchyRequest);
- }
-
- // Step 6
- if let CssRule::Namespace(..) = new_rule {
- if !rules.only_ns_or_import() {
- return Err(RulesMutateError::InvalidState);
- }
- }
-
- rules.0.insert(index, new_rule.clone());
- }
-
- Ok(new_rule)
- }
-}
-
-
-/// The structure servo uses to represent a stylesheet.
-#[derive(Debug)]
-pub struct Stylesheet {
- /// List of rules in the order they were found (important for
- /// cascading order)
- pub rules: Arc<Locked<CssRules>>,
- /// List of media associated with the Stylesheet.
- pub media: Arc<Locked<MediaList>>,
- /// The origin of this stylesheet.
- pub origin: Origin,
- /// The url data this stylesheet should use.
- pub url_data: UrlExtraData,
- /// The lock used for objects inside this stylesheet
- pub shared_lock: SharedRwLock,
- /// The namespaces that apply to this stylesheet.
- pub namespaces: RwLock<Namespaces>,
- /// Whether this stylesheet would be dirty when the viewport size changes.
- pub dirty_on_viewport_size_change: AtomicBool,
- /// Whether this stylesheet should be disabled.
- pub disabled: AtomicBool,
- /// The quirks mode of this stylesheet.
- pub quirks_mode: QuirksMode,
-}
-
-impl MallocSizeOfWithGuard for Stylesheet {
- fn malloc_size_of_children(&self, guard: &SharedRwLockReadGuard,
- malloc_size_of: MallocSizeOfFn) -> usize {
- // Measurement of other fields may be added later.
- self.rules.read_with(guard).malloc_size_of_children(guard, malloc_size_of)
- }
-}
-
-/// 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>,
- /// The quirks mode stylesheet.
- pub quirks_mode_stylesheet: Stylesheet,
-}
-
-
-/// A CSS rule.
-///
-/// TODO(emilio): Lots of spec links should be around.
-#[derive(Debug, Clone)]
-#[allow(missing_docs)]
-pub enum CssRule {
- // No Charset here, CSSCharsetRule has been removed from CSSOM
- // https://drafts.csswg.org/cssom/#changes-from-5-december-2013
-
- Namespace(Arc<Locked<NamespaceRule>>),
- Import(Arc<Locked<ImportRule>>),
- Style(Arc<Locked<StyleRule>>),
- Media(Arc<Locked<MediaRule>>),
- FontFace(Arc<Locked<FontFaceRule>>),
- CounterStyle(Arc<Locked<CounterStyleRule>>),
- Viewport(Arc<Locked<ViewportRule>>),
- Keyframes(Arc<Locked<KeyframesRule>>),
- Supports(Arc<Locked<SupportsRule>>),
- Page(Arc<Locked<PageRule>>),
- Document(Arc<Locked<DocumentRule>>),
-}
-
-impl MallocSizeOfWithGuard for CssRule {
- fn malloc_size_of_children(&self, guard: &SharedRwLockReadGuard,
- malloc_size_of: MallocSizeOfFn) -> usize {
- match *self {
- CssRule::Style(ref lock) => {
- lock.read_with(guard).malloc_size_of_children(guard, malloc_size_of)
- },
- // Measurement of these fields may be added later.
- CssRule::Import(_) => 0,
- CssRule::Media(_) => 0,
- CssRule::FontFace(_) => 0,
- CssRule::CounterStyle(_) => 0,
- CssRule::Keyframes(_) => 0,
- CssRule::Namespace(_) => 0,
- CssRule::Viewport(_) => 0,
- CssRule::Supports(_) => 0,
- CssRule::Page(_) => 0,
- CssRule::Document(_) => 0,
- }
- }
-}
-
-#[allow(missing_docs)]
-#[derive(PartialEq, Eq, Copy, Clone)]
-pub enum CssRuleType {
- // https://drafts.csswg.org/cssom/#the-cssrule-interface
- Style = 1,
- Charset = 2,
- Import = 3,
- Media = 4,
- FontFace = 5,
- Page = 6,
- // https://drafts.csswg.org/css-animations-1/#interface-cssrule-idl
- Keyframes = 7,
- Keyframe = 8,
- // https://drafts.csswg.org/cssom/#the-cssrule-interface
- Margin = 9,
- Namespace = 10,
- // https://drafts.csswg.org/css-counter-styles-3/#extentions-to-cssrule-interface
- CounterStyle = 11,
- // https://drafts.csswg.org/css-conditional-3/#extentions-to-cssrule-interface
- Supports = 12,
- // https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#extentions-to-cssrule-interface
- Document = 13,
- // https://drafts.csswg.org/css-fonts-3/#om-fontfeaturevalues
- FontFeatureValues = 14,
- // https://drafts.csswg.org/css-device-adapt/#css-rule-interface
- Viewport = 15,
-}
-
-#[allow(missing_docs)]
-pub enum SingleRuleParseError {
- Syntax,
- Hierarchy,
-}
-
-impl CssRule {
- #[allow(missing_docs)]
- pub fn rule_type(&self) -> CssRuleType {
- match *self {
- CssRule::Style(_) => CssRuleType::Style,
- CssRule::Import(_) => CssRuleType::Import,
- CssRule::Media(_) => CssRuleType::Media,
- CssRule::FontFace(_) => CssRuleType::FontFace,
- CssRule::CounterStyle(_) => CssRuleType::CounterStyle,
- CssRule::Keyframes(_) => CssRuleType::Keyframes,
- CssRule::Namespace(_) => CssRuleType::Namespace,
- CssRule::Viewport(_) => CssRuleType::Viewport,
- CssRule::Supports(_) => CssRuleType::Supports,
- CssRule::Page(_) => CssRuleType::Page,
- CssRule::Document(_) => CssRuleType::Document,
- }
- }
-
- fn rule_state(&self) -> State {
- match *self {
- // CssRule::Charset(..) => State::Start,
- CssRule::Import(..) => State::Imports,
- CssRule::Namespace(..) => State::Namespaces,
- _ => State::Body,
- }
- }
-
- // input state is None for a nested rule
- // Returns a parsed CSS rule and the final state of the parser
- #[allow(missing_docs)]
- pub fn parse(css: &str,
- parent_stylesheet: &Stylesheet,
- state: Option<State>,
- loader: Option<&StylesheetLoader>)
- -> Result<(Self, State), SingleRuleParseError> {
- let error_reporter = NullReporter;
- let context = ParserContext::new(
- parent_stylesheet.origin,
- &parent_stylesheet.url_data,
- &error_reporter,
- None,
- PARSING_MODE_DEFAULT,
- parent_stylesheet.quirks_mode
- );
-
- let mut input = Parser::new(css);
-
- let mut guard = parent_stylesheet.namespaces.write();
-
- // nested rules are in the body state
- let state = state.unwrap_or(State::Body);
- let mut rule_parser = TopLevelRuleParser {
- stylesheet_origin: parent_stylesheet.origin,
- context: context,
- shared_lock: &parent_stylesheet.shared_lock,
- loader: loader,
- state: state,
- namespaces: Some(&mut *guard),
- };
- match parse_one_rule(&mut input, &mut rule_parser) {
- Ok(result) => Ok((result, rule_parser.state)),
- Err(_) => {
- if let State::Invalid = rule_parser.state {
- Err(SingleRuleParseError::Hierarchy)
- } else {
- Err(SingleRuleParseError::Syntax)
- }
- }
- }
- }
-
- /// Deep clones this CssRule.
- fn deep_clone_with_lock(
- &self,
- lock: &SharedRwLock
- ) -> CssRule {
- let guard = lock.read();
- match *self {
- CssRule::Namespace(ref arc) => {
- let rule = arc.read_with(&guard);
- CssRule::Namespace(Arc::new(lock.wrap(rule.clone())))
- },
- CssRule::Import(ref arc) => {
- let rule = arc.read_with(&guard);
- CssRule::Import(Arc::new(lock.wrap(rule.clone())))
- },
- CssRule::Style(ref arc) => {
- let rule = arc.read_with(&guard);
- CssRule::Style(Arc::new(
- lock.wrap(rule.deep_clone_with_lock(lock))))
- },
- CssRule::Media(ref arc) => {
- let rule = arc.read_with(&guard);
- CssRule::Media(Arc::new(
- lock.wrap(rule.deep_clone_with_lock(lock))))
- },
- CssRule::FontFace(ref arc) => {
- let rule = arc.read_with(&guard);
- CssRule::FontFace(Arc::new(lock.wrap(rule.clone())))
- },
- CssRule::CounterStyle(ref arc) => {
- let rule = arc.read_with(&guard);
- CssRule::CounterStyle(Arc::new(lock.wrap(rule.clone())))
- },
- CssRule::Viewport(ref arc) => {
- let rule = arc.read_with(&guard);
- CssRule::Viewport(Arc::new(lock.wrap(rule.clone())))
- },
- CssRule::Keyframes(ref arc) => {
- let rule = arc.read_with(&guard);
- CssRule::Keyframes(Arc::new(
- lock.wrap(rule.deep_clone_with_lock(lock))))
- },
- CssRule::Supports(ref arc) => {
- let rule = arc.read_with(&guard);
- CssRule::Supports(Arc::new(
- lock.wrap(rule.deep_clone_with_lock(lock))))
- },
- CssRule::Page(ref arc) => {
- let rule = arc.read_with(&guard);
- CssRule::Page(Arc::new(
- lock.wrap(rule.deep_clone_with_lock(lock))))
- },
- CssRule::Document(ref arc) => {
- let rule = arc.read_with(&guard);
- CssRule::Document(Arc::new(
- lock.wrap(rule.deep_clone_with_lock(lock))))
- },
- }
- }
-}
-
-impl ToCssWithGuard for CssRule {
- // https://drafts.csswg.org/cssom/#serialize-a-css-rule
- fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
- where W: fmt::Write {
- match *self {
- CssRule::Namespace(ref lock) => lock.read_with(guard).to_css(guard, dest),
- CssRule::Import(ref lock) => lock.read_with(guard).to_css(guard, dest),
- CssRule::Style(ref lock) => lock.read_with(guard).to_css(guard, dest),
- CssRule::FontFace(ref lock) => lock.read_with(guard).to_css(guard, dest),
- CssRule::CounterStyle(ref lock) => lock.read_with(guard).to_css(guard, dest),
- CssRule::Viewport(ref lock) => lock.read_with(guard).to_css(guard, dest),
- CssRule::Keyframes(ref lock) => lock.read_with(guard).to_css(guard, dest),
- CssRule::Media(ref lock) => lock.read_with(guard).to_css(guard, dest),
- CssRule::Supports(ref lock) => lock.read_with(guard).to_css(guard, dest),
- CssRule::Page(ref lock) => lock.read_with(guard).to_css(guard, dest),
- CssRule::Document(ref lock) => lock.read_with(guard).to_css(guard, dest),
- }
- }
-}
-
-/// Calculates the location of a rule's source given an offset.
-fn get_location_with_offset(location: SourceLocation, offset: u64)
- -> SourceLocation {
- SourceLocation {
- line: location.line + offset as usize - 1,
- column: location.column,
- }
-}
-
-#[derive(Clone, Debug, PartialEq)]
-#[allow(missing_docs)]
-pub struct NamespaceRule {
- /// `None` for the default Namespace
- pub prefix: Option<Prefix>,
- pub url: Namespace,
- pub source_location: SourceLocation,
-}
-
-impl ToCssWithGuard for NamespaceRule {
- // https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSNamespaceRule
- fn to_css<W>(&self, _guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
- where W: fmt::Write {
- try!(dest.write_str("@namespace "));
- if let Some(ref prefix) = self.prefix {
- try!(dest.write_str(&*prefix.to_string()));
- try!(dest.write_str(" "));
- }
- try!(dest.write_str("url(\""));
- try!(dest.write_str(&*self.url.to_string()));
- dest.write_str("\");")
- }
-}
-
-
-/// The [`@import`][import] at-rule.
-///
-/// [import]: https://drafts.csswg.org/css-cascade-3/#at-import
-#[derive(Debug)]
-pub struct ImportRule {
- /// The `<url>` this `@import` rule is loading.
- pub url: SpecifiedUrl,
-
- /// The stylesheet is always present.
- ///
- /// It contains an empty list of rules and namespace set that is updated
- /// when it loads.
- pub stylesheet: Arc<Stylesheet>,
-
- /// The line and column of the rule's source code.
- pub source_location: SourceLocation,
-}
-
-impl Clone for ImportRule {
- fn clone(&self) -> ImportRule {
- let stylesheet: &Stylesheet = self.stylesheet.borrow();
- ImportRule {
- url: self.url.clone(),
- stylesheet: Arc::new(stylesheet.clone()),
- source_location: self.source_location.clone(),
- }
- }
-}
-
-impl ToCssWithGuard for ImportRule {
- fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
- where W: fmt::Write {
- try!(dest.write_str("@import "));
- try!(self.url.to_css(dest));
- let media = self.stylesheet.media.read_with(guard);
- if !media.is_empty() {
- try!(dest.write_str(" "));
- try!(media.to_css(dest));
- }
- dest.write_str(";")
- }
-}
-
-/// A [`@keyframes`][keyframes] rule.
-///
-/// [keyframes]: https://drafts.csswg.org/css-animations/#keyframes
-#[derive(Debug)]
-pub struct KeyframesRule {
- /// The name of the current animation.
- pub name: KeyframesName,
- /// The keyframes specified for this CSS rule.
- pub keyframes: Vec<Arc<Locked<Keyframe>>>,
- /// Vendor prefix type the @keyframes has.
- pub vendor_prefix: Option<VendorPrefix>,
- /// The line and column of the rule's source code.
- pub source_location: SourceLocation,
-}
-
-impl ToCssWithGuard for KeyframesRule {
- // Serialization of KeyframesRule is not specced.
- fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
- where W: fmt::Write {
- try!(dest.write_str("@keyframes "));
- try!(self.name.to_css(dest));
- try!(dest.write_str(" {"));
- let iter = self.keyframes.iter();
- for lock in iter {
- try!(dest.write_str("\n"));
- let keyframe = lock.read_with(&guard);
- try!(keyframe.to_css(guard, dest));
- }
- dest.write_str("\n}")
- }
-}
-
-impl KeyframesRule {
- /// Returns the index of the last keyframe that matches the given selector.
- /// If the selector is not valid, or no keyframe is found, returns None.
- ///
- /// Related spec:
- /// https://drafts.csswg.org/css-animations-1/#interface-csskeyframesrule-findrule
- pub fn find_rule(&self, guard: &SharedRwLockReadGuard, selector: &str) -> Option<usize> {
- if let Ok(selector) = Parser::new(selector).parse_entirely(KeyframeSelector::parse) {
- for (i, keyframe) in self.keyframes.iter().enumerate().rev() {
- if keyframe.read_with(guard).selector == selector {
- return Some(i);
- }
- }
- }
- None
- }
-}
-
-impl KeyframesRule {
- /// Deep clones this KeyframesRule.
- fn deep_clone_with_lock(&self,
- lock: &SharedRwLock) -> KeyframesRule {
- let guard = lock.read();
- KeyframesRule {
- name: self.name.clone(),
- keyframes: self.keyframes.iter()
- .map(|ref x| Arc::new(lock.wrap(
- x.read_with(&guard).deep_clone_with_lock(lock))))
- .collect(),
- vendor_prefix: self.vendor_prefix.clone(),
- source_location: self.source_location.clone(),
- }
- }
-}
-
-#[allow(missing_docs)]
-#[derive(Debug)]
-pub struct MediaRule {
- pub media_queries: Arc<Locked<MediaList>>,
- pub rules: Arc<Locked<CssRules>>,
- pub source_location: SourceLocation,
-}
-
-impl ToCssWithGuard for MediaRule {
- // Serialization of MediaRule is not specced.
- // https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSMediaRule
- fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
- where W: fmt::Write {
- try!(dest.write_str("@media "));
- try!(self.media_queries.read_with(guard).to_css(dest));
- try!(dest.write_str(" {"));
- for rule in self.rules.read_with(guard).0.iter() {
- try!(dest.write_str(" "));
- try!(rule.to_css(guard, dest));
- }
- dest.write_str(" }")
- }
-}
-
-impl MediaRule {
- /// Deep clones this MediaRule.
- fn deep_clone_with_lock(&self,
- lock: &SharedRwLock) -> MediaRule {
- let guard = lock.read();
- let media_queries = self.media_queries.read_with(&guard);
- let rules = self.rules.read_with(&guard);
- MediaRule {
- media_queries: Arc::new(lock.wrap(media_queries.clone())),
- rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock))),
- source_location: self.source_location.clone(),
- }
- }
-}
-
-
-#[derive(Debug)]
-/// An @supports rule
-pub struct SupportsRule {
- /// The parsed condition
- pub condition: SupportsCondition,
- /// Child rules
- pub rules: Arc<Locked<CssRules>>,
- /// The result of evaluating the condition
- pub enabled: bool,
- /// The line and column of the rule's source code.
- pub source_location: SourceLocation,
-}
-
-impl ToCssWithGuard for SupportsRule {
- fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
- where W: fmt::Write {
- try!(dest.write_str("@supports "));
- try!(self.condition.to_css(dest));
- try!(dest.write_str(" {"));
- for rule in self.rules.read_with(guard).0.iter() {
- try!(dest.write_str(" "));
- try!(rule.to_css(guard, dest));
- }
- dest.write_str(" }")
- }
-}
-
-impl SupportsRule {
- fn deep_clone_with_lock(&self,
- lock: &SharedRwLock) -> SupportsRule {
- let guard = lock.read();
- let rules = self.rules.read_with(&guard);
- SupportsRule {
- condition: self.condition.clone(),
- rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock))),
- enabled: self.enabled,
- source_location: self.source_location.clone(),
- }
- }
-}
-
-/// A [`@page`][page] rule. This implements only a limited subset of the CSS 2.2 syntax. In this
-/// subset, [page selectors][page-selectors] are not implemented.
-///
-/// [page]: https://drafts.csswg.org/css2/page.html#page-box
-/// [page-selectors]: https://drafts.csswg.org/css2/page.html#page-selectors
-#[allow(missing_docs)]
-#[derive(Debug)]
-pub struct PageRule {
- pub block: Arc<Locked<PropertyDeclarationBlock>>,
- pub source_location: SourceLocation,
-}
-
-impl ToCssWithGuard for PageRule {
- // Serialization of PageRule is not specced, adapted from steps for StyleRule.
- fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
- where W: fmt::Write {
- dest.write_str("@page { ")?;
- let declaration_block = self.block.read_with(guard);
- declaration_block.to_css(dest)?;
- if declaration_block.declarations().len() > 0 {
- write!(dest, " ")?;
- }
- dest.write_str("}")
- }
-}
-
-impl PageRule {
- fn deep_clone_with_lock(&self,
- lock: &SharedRwLock) -> PageRule {
- let guard = lock.read();
- PageRule {
- block: Arc::new(lock.wrap(self.block.read_with(&guard).clone())),
- source_location: self.source_location.clone(),
- }
- }
-}
-
-#[allow(missing_docs)]
-#[derive(Debug)]
-pub struct StyleRule {
- pub selectors: SelectorList<SelectorImpl>,
- pub block: Arc<Locked<PropertyDeclarationBlock>>,
- pub source_location: SourceLocation,
-}
-
-impl MallocSizeOfWithGuard for StyleRule {
- fn malloc_size_of_children(&self, guard: &SharedRwLockReadGuard,
- malloc_size_of: MallocSizeOfFn) -> usize {
- // Measurement of other fields may be added later.
- self.block.read_with(guard).malloc_size_of_children(malloc_size_of)
- }
-}
-
-impl ToCssWithGuard for StyleRule {
- // https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSStyleRule
- fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
- where W: fmt::Write {
- // Step 1
- try!(self.selectors.to_css(dest));
- // Step 2
- try!(dest.write_str(" { "));
- // Step 3
- let declaration_block = self.block.read_with(guard);
- try!(declaration_block.to_css(dest));
- // Step 4
- if declaration_block.declarations().len() > 0 {
- try!(write!(dest, " "));
- }
- // Step 5
- try!(dest.write_str("}"));
- Ok(())
- }
-}
-
-impl StyleRule {
- /// Deep clones this StyleRule.
- fn deep_clone_with_lock(&self,
- lock: &SharedRwLock) -> StyleRule {
- let guard = lock.read();
- StyleRule {
- selectors: self.selectors.clone(),
- block: Arc::new(lock.wrap(self.block.read_with(&guard).clone())),
- source_location: self.source_location.clone(),
- }
- }
-}
-
-/// A @font-face rule
-#[cfg(feature = "servo")]
-pub type FontFaceRule = FontFaceRuleData;
-
-/// A @counter-style rule
-#[cfg(feature = "servo")]
-pub type CounterStyleRule = CounterStyleRuleData;
-
-#[derive(Debug)]
-/// A @-moz-document rule
-pub struct DocumentRule {
- /// The parsed condition
- pub condition: DocumentCondition,
- /// Child rules
- pub rules: Arc<Locked<CssRules>>,
- /// The line and column of the rule's source code.
- pub source_location: SourceLocation,
-}
-
-impl ToCssWithGuard for DocumentRule {
- fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
- where W: fmt::Write {
- try!(dest.write_str("@-moz-document "));
- try!(self.condition.to_css(dest));
- try!(dest.write_str(" {"));
- for rule in self.rules.read_with(guard).0.iter() {
- try!(dest.write_str(" "));
- try!(rule.to_css(guard, dest));
- }
- dest.write_str(" }")
- }
-}
-
-impl DocumentRule {
- /// Deep clones this DocumentRule.
- fn deep_clone_with_lock(&self,
- lock: &SharedRwLock) -> DocumentRule {
- let guard = lock.read();
- let rules = self.rules.read_with(&guard);
- DocumentRule {
- condition: self.condition.clone(),
- rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock))),
- source_location: self.source_location.clone(),
- }
- }
-}
-
-/// A trait that describes statically which rules are iterated for a given
-/// RulesIterator.
-pub trait NestedRuleIterationCondition {
- /// Whether we should process the nested rules in a given `@import` rule.
- fn process_import(
- guard: &SharedRwLockReadGuard,
- device: &Device,
- quirks_mode: QuirksMode,
- rule: &ImportRule)
- -> bool;
-
- /// Whether we should process the nested rules in a given `@media` rule.
- fn process_media(
- guard: &SharedRwLockReadGuard,
- device: &Device,
- quirks_mode: QuirksMode,
- rule: &MediaRule)
- -> bool;
-
- /// Whether we should process the nested rules in a given `@-moz-document` rule.
- fn process_document(
- guard: &SharedRwLockReadGuard,
- device: &Device,
- quirks_mode: QuirksMode,
- rule: &DocumentRule)
- -> bool;
-
- /// Whether we should process the nested rules in a given `@supports` rule.
- fn process_supports(
- guard: &SharedRwLockReadGuard,
- device: &Device,
- quirks_mode: QuirksMode,
- rule: &SupportsRule)
- -> bool;
-}
-
-/// A struct that represents the condition that a rule applies to the document.
-pub struct EffectiveRules;
-
-impl NestedRuleIterationCondition for EffectiveRules {
- fn process_import(
- guard: &SharedRwLockReadGuard,
- device: &Device,
- quirks_mode: QuirksMode,
- rule: &ImportRule)
- -> bool
- {
- rule.stylesheet.media.read_with(guard).evaluate(device, quirks_mode)
- }
-
- fn process_media(
- guard: &SharedRwLockReadGuard,
- device: &Device,
- quirks_mode: QuirksMode,
- rule: &MediaRule)
- -> bool
- {
- rule.media_queries.read_with(guard).evaluate(device, quirks_mode)
- }
-
- fn process_document(
- _: &SharedRwLockReadGuard,
- device: &Device,
- _: QuirksMode,
- rule: &DocumentRule)
- -> bool
- {
- rule.condition.evaluate(device)
- }
-
- fn process_supports(
- _: &SharedRwLockReadGuard,
- _: &Device,
- _: QuirksMode,
- rule: &SupportsRule)
- -> bool
- {
- rule.enabled
- }
-}
-
-/// A filter that processes all the rules in a rule list.
-pub struct AllRules;
-
-impl NestedRuleIterationCondition for AllRules {
- fn process_import(
- _: &SharedRwLockReadGuard,
- _: &Device,
- _: QuirksMode,
- _: &ImportRule)
- -> bool
- {
- true
- }
-
- fn process_media(
- _: &SharedRwLockReadGuard,
- _: &Device,
- _: QuirksMode,
- _: &MediaRule)
- -> bool
- {
- true
- }
-
- fn process_document(
- _: &SharedRwLockReadGuard,
- _: &Device,
- _: QuirksMode,
- _: &DocumentRule)
- -> bool
- {
- true
- }
-
- fn process_supports(
- _: &SharedRwLockReadGuard,
- _: &Device,
- _: QuirksMode,
- _: &SupportsRule)
- -> bool
- {
- true
- }
-}
-
-/// An iterator over all the effective rules of a stylesheet.
-///
-/// NOTE: This iterator recurses into `@import` rules.
-pub type EffectiveRulesIterator<'a, 'b> = RulesIterator<'a, 'b, EffectiveRules>;
-
-/// An iterator over a list of rules.
-pub struct RulesIterator<'a, 'b, C>
- where 'b: 'a,
- C: NestedRuleIterationCondition + 'static,
-{
- device: &'a Device,
- quirks_mode: QuirksMode,
- guard: &'a SharedRwLockReadGuard<'b>,
- stack: SmallVec<[slice::Iter<'a, CssRule>; 3]>,
- _phantom: ::std::marker::PhantomData<C>,
-}
-
-impl<'a, 'b, C> RulesIterator<'a, 'b, C>
- 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)
- -> Self
- {
- let mut stack = SmallVec::new();
- stack.push(rules.0.iter());
- Self {
- device: device,
- quirks_mode: quirks_mode,
- guard: guard,
- stack: stack,
- _phantom: ::std::marker::PhantomData,
- }
- }
-
- /// Skips all the remaining children of the last nested rule processed.
- pub fn skip_children(&mut self) {
- self.stack.pop();
- }
-}
-
-impl<'a, 'b, C> Iterator for RulesIterator<'a, 'b, C>
- where 'b: 'a,
- C: NestedRuleIterationCondition + 'static,
-{
- type Item = &'a CssRule;
-
- fn next(&mut self) -> Option<Self::Item> {
- let mut nested_iter_finished = false;
- while !self.stack.is_empty() {
- if nested_iter_finished {
- self.stack.pop();
- nested_iter_finished = false;
- continue;
- }
-
- let rule;
- let sub_iter;
- {
- let mut nested_iter = self.stack.last_mut().unwrap();
- rule = match nested_iter.next() {
- Some(r) => r,
- None => {
- nested_iter_finished = true;
- continue
- }
- };
-
- sub_iter = match *rule {
- CssRule::Import(ref import_rule) => {
- let import_rule = import_rule.read_with(self.guard);
- if !C::process_import(self.guard, self.device, self.quirks_mode, import_rule) {
- continue;
- }
- Some(import_rule.stylesheet.rules.read_with(self.guard).0.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;
- }
- Some(doc_rule.rules.read_with(self.guard).0.iter())
- }
- CssRule::Media(ref lock) => {
- let media_rule = lock.read_with(self.guard);
- if !C::process_media(self.guard, self.device, self.quirks_mode, media_rule) {
- continue;
- }
- Some(media_rule.rules.read_with(self.guard).0.iter())
- }
- CssRule::Supports(ref lock) => {
- let supports_rule = lock.read_with(self.guard);
- if !C::process_supports(self.guard, self.device, self.quirks_mode, supports_rule) {
- continue;
- }
- Some(supports_rule.rules.read_with(self.guard).0.iter())
- }
- CssRule::Namespace(_) |
- CssRule::Style(_) |
- CssRule::FontFace(_) |
- CssRule::CounterStyle(_) |
- CssRule::Viewport(_) |
- CssRule::Keyframes(_) |
- CssRule::Page(_) => None,
- };
- }
-
- if let Some(sub_iter) = sub_iter {
- self.stack.push(sub_iter);
- }
-
- return Some(rule);
- }
-
- None
- }
-}
-
-impl Stylesheet {
- /// Updates an empty stylesheet from a given string of text.
- pub fn update_from_str(existing: &Stylesheet,
- css: &str,
- url_data: &UrlExtraData,
- stylesheet_loader: Option<&StylesheetLoader>,
- error_reporter: &ParseErrorReporter,
- line_number_offset: u64) {
- let namespaces = RwLock::new(Namespaces::default());
- // FIXME: we really should update existing.url_data with the given url_data,
- // otherwise newly inserted rule may not have the right base url.
- let (rules, dirty_on_viewport_size_change) =
- Stylesheet::parse_rules(
- css,
- url_data,
- existing.origin,
- &mut *namespaces.write(),
- &existing.shared_lock,
- stylesheet_loader,
- error_reporter,
- existing.quirks_mode,
- line_number_offset
- );
-
- mem::swap(&mut *existing.namespaces.write(), &mut *namespaces.write());
- existing.dirty_on_viewport_size_change
- .store(dirty_on_viewport_size_change, Ordering::Release);
-
- // Acquire the lock *after* parsing, to minimize the exclusive section.
- let mut guard = existing.shared_lock.write();
- *existing.rules.write_with(&mut guard) = CssRules(rules);
- }
-
- fn parse_rules(
- css: &str,
- url_data: &UrlExtraData,
- origin: Origin,
- namespaces: &mut Namespaces,
- shared_lock: &SharedRwLock,
- stylesheet_loader: Option<&StylesheetLoader>,
- error_reporter: &ParseErrorReporter,
- quirks_mode: QuirksMode,
- line_number_offset: u64
- ) -> (Vec<CssRule>, bool) {
- let mut rules = Vec::new();
- let mut input = Parser::new(css);
-
- let context =
- ParserContext::new_with_line_number_offset(
- origin,
- url_data,
- error_reporter,
- line_number_offset,
- PARSING_MODE_DEFAULT,
- quirks_mode
- );
-
- let rule_parser = TopLevelRuleParser {
- stylesheet_origin: origin,
- shared_lock: shared_lock,
- loader: stylesheet_loader,
- context: context,
- state: State::Start,
- namespaces: Some(namespaces),
- };
-
- input.look_for_viewport_percentages();
-
- {
- let mut iter =
- RuleListParser::new_for_stylesheet(&mut input, rule_parser);
-
- while let Some(result) = iter.next() {
- match result {
- Ok(rule) => rules.push(rule),
- Err(range) => {
- let pos = range.start;
- let message = format!("Invalid rule: '{}'", iter.input.slice(range));
- log_css_error(iter.input, pos, &*message, &iter.parser.context);
- }
- }
- }
- }
-
- (rules, input.seen_viewport_percentages())
- }
-
- /// Creates an empty stylesheet and parses it with a given base url, origin
- /// and media.
- ///
- /// Effectively creates a new stylesheet and forwards the hard work to
- /// `Stylesheet::update_from_str`.
- pub fn from_str(css: &str,
- url_data: UrlExtraData,
- origin: Origin,
- media: Arc<Locked<MediaList>>,
- shared_lock: SharedRwLock,
- stylesheet_loader: Option<&StylesheetLoader>,
- error_reporter: &ParseErrorReporter,
- quirks_mode: QuirksMode,
- line_number_offset: u64)
- -> Stylesheet {
- let namespaces = RwLock::new(Namespaces::default());
- let (rules, dirty_on_viewport_size_change) = Stylesheet::parse_rules(
- css,
- &url_data,
- origin,
- &mut *namespaces.write(),
- &shared_lock,
- stylesheet_loader,
- error_reporter,
- quirks_mode,
- line_number_offset,
- );
-
- Stylesheet {
- origin: origin,
- url_data: url_data,
- namespaces: namespaces,
- rules: CssRules::new(rules, &shared_lock),
- media: media,
- shared_lock: shared_lock,
- dirty_on_viewport_size_change: AtomicBool::new(dirty_on_viewport_size_change),
- disabled: AtomicBool::new(false),
- quirks_mode: quirks_mode,
- }
- }
-
- /// Whether this stylesheet can be dirty on viewport size change.
- pub fn dirty_on_viewport_size_change(&self) -> bool {
- self.dirty_on_viewport_size_change.load(Ordering::SeqCst)
- }
-
- /// When CSSOM inserts a rule or declaration into this stylesheet, it needs to call this method
- /// with the return value of `cssparser::Parser::seen_viewport_percentages`.
- ///
- /// FIXME: actually make these calls
- ///
- /// Note: when *removing* a rule or declaration that contains a viewport percentage,
- /// to keep the flag accurate we’d need to iterator through the rest of the stylesheet to
- /// check for *other* such values.
- ///
- /// Instead, we conservatively assume there might be some.
- /// Restyling will some some more work than necessary, but give correct results.
- pub fn inserted_has_viewport_percentages(&self, has_viewport_percentages: bool) {
- self.dirty_on_viewport_size_change.fetch_or(has_viewport_percentages, Ordering::SeqCst);
- }
-
- /// Returns whether the style-sheet applies for the current device depending
- /// on the associated MediaList.
- ///
- /// Always true if no associated MediaList exists.
- pub fn is_effective_for_device(&self, device: &Device, guard: &SharedRwLockReadGuard) -> bool {
- self.media.read_with(guard).evaluate(device, self.quirks_mode)
- }
-
- /// Return an iterator over the effective rules within the style-sheet, as
- /// according to the supplied `Device`.
- #[inline]
- pub fn effective_rules<'a, 'b>(
- &'a self,
- device: &'a Device,
- guard: &'a SharedRwLockReadGuard<'b>)
- -> EffectiveRulesIterator<'a, 'b>
- {
- self.iter_rules::<'a, 'b, EffectiveRules>(device, guard)
- }
-
- /// Return an iterator using the condition `C`.
- #[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))
- }
-
- /// Returns whether the stylesheet has been explicitly disabled through the
- /// CSSOM.
- pub fn disabled(&self) -> bool {
- self.disabled.load(Ordering::SeqCst)
- }
-
- /// Records that the stylesheet has been explicitly disabled through the
- /// CSSOM.
- ///
- /// Returns whether the the call resulted in a change in disabled state.
- ///
- /// Disabled stylesheets remain in the document, but their rules are not
- /// added to the Stylist.
- pub fn set_disabled(&self, disabled: bool) -> bool {
- self.disabled.swap(disabled, Ordering::SeqCst) != disabled
- }
-}
-
-impl Clone for Stylesheet {
- fn clone(&self) -> Stylesheet {
- // Create a new lock for our clone.
- let lock = self.shared_lock.clone();
- let guard = self.shared_lock.read();
-
- // Make a deep clone of the rules, using the new lock.
- let rules = self.rules.read_with(&guard);
- let cloned_rules = rules.deep_clone_with_lock(&lock);
-
- // Make a deep clone of the media, using the new lock.
- let media = self.media.read_with(&guard);
- let cloned_media = media.clone();
-
- Stylesheet {
- rules: Arc::new(lock.wrap(cloned_rules)),
- media: Arc::new(lock.wrap(cloned_media)),
- origin: self.origin,
- url_data: self.url_data.clone(),
- shared_lock: lock,
- namespaces: RwLock::new((*self.namespaces.read()).clone()),
- dirty_on_viewport_size_change: AtomicBool::new(
- self.dirty_on_viewport_size_change.load(Ordering::SeqCst)),
- disabled: AtomicBool::new(self.disabled.load(Ordering::SeqCst)),
- quirks_mode: self.quirks_mode,
- }
- }
-}
-
-macro_rules! rule_filter {
- ($( $method: ident($variant:ident => $rule_type: ident), )+) => {
- impl Stylesheet {
- $(
- #[allow(missing_docs)]
- pub fn $method<F>(&self, device: &Device, guard: &SharedRwLockReadGuard, mut f: F)
- where F: FnMut(&$rule_type),
- {
- for rule in self.effective_rules(device, guard) {
- if let CssRule::$variant(ref lock) = *rule {
- let rule = lock.read_with(guard);
- f(&rule)
- }
- }
- }
- )+
- }
- }
-}
-
-rule_filter! {
- effective_style_rules(Style => StyleRule),
- effective_media_rules(Media => MediaRule),
- effective_font_face_rules(FontFace => FontFaceRule),
- effective_counter_style_rules(CounterStyle => CounterStyleRule),
- effective_viewport_rules(Viewport => ViewportRule),
- effective_keyframes_rules(Keyframes => KeyframesRule),
- effective_supports_rules(Supports => SupportsRule),
- effective_page_rules(Page => PageRule),
- effective_document_rules(Document => DocumentRule),
-}
-
-/// The stylesheet loader is the abstraction used to trigger network requests
-/// for `@import` rules.
-pub trait StylesheetLoader {
- /// Request a stylesheet after parsing a given `@import` rule.
- ///
- /// The called code is responsible to update the `stylesheet` rules field
- /// when the sheet is done loading.
- ///
- /// The convoluted signature allows impls to look at MediaList and ImportRule
- /// before they’re locked, while keeping the trait object-safe.
- fn request_stylesheet(
- &self,
- media: Arc<Locked<MediaList>>,
- make_import: &mut FnMut(Arc<Locked<MediaList>>) -> ImportRule,
- make_arc: &mut FnMut(ImportRule) -> Arc<Locked<ImportRule>>,
- ) -> Arc<Locked<ImportRule>>;
-}
-
-struct NoOpLoader;
-
-impl StylesheetLoader for NoOpLoader {
- fn request_stylesheet(
- &self,
- media: Arc<Locked<MediaList>>,
- make_import: &mut FnMut(Arc<Locked<MediaList>>) -> ImportRule,
- make_arc: &mut FnMut(ImportRule) -> Arc<Locked<ImportRule>>,
- ) -> Arc<Locked<ImportRule>> {
- make_arc(make_import(media))
- }
-}
-
-
-struct TopLevelRuleParser<'a> {
- stylesheet_origin: Origin,
- shared_lock: &'a SharedRwLock,
- loader: Option<&'a StylesheetLoader>,
- context: ParserContext<'a>,
- state: State,
- namespaces: Option<&'a mut Namespaces>,
-}
-
-impl<'b> TopLevelRuleParser<'b> {
- fn nested<'a: 'b>(&'a self) -> NestedRuleParser<'a, 'b> {
- NestedRuleParser {
- stylesheet_origin: self.stylesheet_origin,
- shared_lock: self.shared_lock,
- context: &self.context,
- }
- }
-}
-
-#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
-#[allow(missing_docs)]
-pub enum State {
- Start = 1,
- Imports = 2,
- Namespaces = 3,
- Body = 4,
- Invalid = 5,
-}
-
-#[derive(Clone, Debug)]
-#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-/// Vendor prefix.
-pub enum VendorPrefix {
- /// -moz prefix.
- Moz,
- /// -webkit prefix.
- WebKit,
-}
-
-enum AtRulePrelude {
- /// A @font-face rule prelude.
- FontFace(SourceLocation),
- /// A @counter-style rule prelude, with its counter style name.
- CounterStyle(CustomIdent),
- /// A @media rule prelude, with its media queries.
- Media(Arc<Locked<MediaList>>, SourceLocation),
- /// An @supports rule, with its conditional
- Supports(SupportsCondition, SourceLocation),
- /// A @viewport rule prelude.
- Viewport,
- /// A @keyframes rule, with its animation name and vendor prefix if exists.
- Keyframes(KeyframesName, Option<VendorPrefix>, SourceLocation),
- /// A @page rule prelude.
- Page(SourceLocation),
- /// A @document rule, with its conditional.
- Document(DocumentCondition, SourceLocation),
-}
-
-
-#[cfg(feature = "gecko")]
-fn register_namespace(ns: &Namespace) -> Result<i32, ()> {
- let id = unsafe { ::gecko_bindings::bindings::Gecko_RegisterNamespace(ns.0.as_ptr()) };
- if id == -1 {
- Err(())
- } else {
- Ok(id)
- }
-}
-
-#[cfg(feature = "servo")]
-fn register_namespace(_: &Namespace) -> Result<(), ()> {
- Ok(()) // servo doesn't use namespace ids
-}
-
-impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
- type Prelude = AtRulePrelude;
- type AtRule = CssRule;
-
- fn parse_prelude(
- &mut self,
- name: &str,
- input: &mut Parser
- ) -> Result<AtRuleType<AtRulePrelude, CssRule>, ()> {
- let location = get_location_with_offset(input.current_source_location(),
- self.context.line_number_offset);
- match_ignore_ascii_case! { name,
- "import" => {
- if self.state > State::Imports {
- self.state = State::Invalid;
- return Err(()) // "@import must be before any rule but @charset"
- }
-
- self.state = State::Imports;
- let url_string = input.expect_url_or_string()?;
- let specified_url = SpecifiedUrl::parse_from_string(url_string, &self.context)?;
-
- let media = parse_media_query_list(&self.context, input);
- let media = Arc::new(self.shared_lock.wrap(media));
-
- let noop_loader = NoOpLoader;
- let loader = if !specified_url.is_invalid() {
- self.loader.expect("Expected a stylesheet loader for @import")
- } else {
- &noop_loader
- };
-
- let mut specified_url = Some(specified_url);
- let arc = loader.request_stylesheet(media, &mut |media| {
- ImportRule {
- url: specified_url.take().unwrap(),
- stylesheet: Arc::new(Stylesheet {
- rules: CssRules::new(Vec::new(), self.shared_lock),
- media: media,
- shared_lock: self.shared_lock.clone(),
- origin: self.context.stylesheet_origin,
- url_data: self.context.url_data.clone(),
- namespaces: RwLock::new(Namespaces::default()),
- dirty_on_viewport_size_change: AtomicBool::new(false),
- disabled: AtomicBool::new(false),
- quirks_mode: self.context.quirks_mode,
- }),
- source_location: location,
- }
- }, &mut |import_rule| {
- Arc::new(self.shared_lock.wrap(import_rule))
- });
-
- return Ok(AtRuleType::WithoutBlock(CssRule::Import(arc)))
- },
- "namespace" => {
- if self.state > State::Namespaces {
- self.state = State::Invalid;
- return Err(()) // "@namespace must be before any rule but @charset and @import"
- }
- self.state = State::Namespaces;
-
- let prefix_result = input.try(|input| input.expect_ident());
- let url = Namespace::from(try!(input.expect_url_or_string()));
-
- let id = register_namespace(&url)?;
-
- let mut namespaces = self.namespaces.as_mut().unwrap();
-
- let opt_prefix = if let Ok(prefix) = prefix_result {
- let prefix = Prefix::from(prefix);
- namespaces
- .prefixes
- .insert(prefix.clone(), (url.clone(), id));
- Some(prefix)
- } else {
- namespaces.default = Some((url.clone(), id));
- None
- };
-
- return Ok(AtRuleType::WithoutBlock(CssRule::Namespace(Arc::new(
- self.shared_lock.wrap(NamespaceRule {
- prefix: opt_prefix,
- url: url,
- source_location: location,
- })
- ))))
- },
- // @charset is removed by rust-cssparser if it’s the first rule in the stylesheet
- // anything left is invalid.
- "charset" => return Err(()), // (insert appropriate error message)
- _ => {}
- }
- // Don't allow starting with an invalid state
- if self.state > State::Body {
- self.state = State::Invalid;
- return Err(());
- }
- self.state = State::Body;
-
- // "Freeze" the namespace map (no more namespace rules can be parsed
- // after this point), and stick it in the context.
- if self.namespaces.is_some() {
- let namespaces = &*self.namespaces.take().unwrap();
- self.context.namespaces = Some(namespaces);
- }
- AtRuleParser::parse_prelude(&mut self.nested(), name, input)
- }
-
- #[inline]
- fn parse_block(&mut self, prelude: AtRulePrelude, input: &mut Parser) -> Result<CssRule, ()> {
- AtRuleParser::parse_block(&mut self.nested(), prelude, input)
- }
-}
-
-
-impl<'a> QualifiedRuleParser for TopLevelRuleParser<'a> {
- type Prelude = SelectorList<SelectorImpl>;
- type QualifiedRule = CssRule;
-
- #[inline]
- fn parse_prelude(&mut self, input: &mut Parser) -> Result<SelectorList<SelectorImpl>, ()> {
- self.state = State::Body;
-
- // "Freeze" the namespace map (no more namespace rules can be parsed
- // after this point), and stick it in the context.
- if self.namespaces.is_some() {
- let namespaces = &*self.namespaces.take().unwrap();
- self.context.namespaces = Some(namespaces);
- }
-
- QualifiedRuleParser::parse_prelude(&mut self.nested(), input)
- }
-
- #[inline]
- fn parse_block(
- &mut self,
- prelude: SelectorList<SelectorImpl>,
- input: &mut Parser
- ) -> Result<CssRule, ()> {
- QualifiedRuleParser::parse_block(&mut self.nested(), prelude, input)
- }
-}
-
-#[derive(Clone)] // shallow, relatively cheap .clone
-struct NestedRuleParser<'a, 'b: 'a> {
- stylesheet_origin: Origin,
- shared_lock: &'a SharedRwLock,
- context: &'a ParserContext<'b>,
-}
-
-impl<'a, 'b> NestedRuleParser<'a, 'b> {
- fn parse_nested_rules(
- &mut self,
- input: &mut Parser,
- rule_type: CssRuleType
- ) -> Arc<Locked<CssRules>> {
- let context = ParserContext::new_with_rule_type(self.context, Some(rule_type));
-
- let nested_parser = NestedRuleParser {
- stylesheet_origin: self.stylesheet_origin,
- shared_lock: self.shared_lock,
- context: &context,
- };
-
- let mut iter = RuleListParser::new_for_nested_rule(input, nested_parser);
- let mut rules = Vec::new();
- while let Some(result) = iter.next() {
- match result {
- Ok(rule) => rules.push(rule),
- Err(range) => {
- let pos = range.start;
- let message = format!("Unsupported rule: '{}'", iter.input.slice(range));
- log_css_error(iter.input, pos, &*message, self.context);
- }
- }
- }
- CssRules::new(rules, self.shared_lock)
- }
-}
-
-#[cfg(feature = "servo")]
-fn is_viewport_enabled() -> bool {
- PREFS.get("layout.viewport.enabled").as_boolean().unwrap_or(false)
-}
-
-#[cfg(not(feature = "servo"))]
-fn is_viewport_enabled() -> bool {
- false // Gecko doesn't support @viewport
-}
-
-impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> {
- type Prelude = AtRulePrelude;
- type AtRule = CssRule;
-
- fn parse_prelude(
- &mut self,
- name: &str,
- input: &mut Parser
- ) -> Result<AtRuleType<AtRulePrelude, CssRule>, ()> {
- let location =
- get_location_with_offset(
- input.current_source_location(),
- self.context.line_number_offset
- );
-
- match_ignore_ascii_case! { name,
- "media" => {
- let media_queries = parse_media_query_list(self.context, input);
- let arc = Arc::new(self.shared_lock.wrap(media_queries));
- Ok(AtRuleType::WithBlock(AtRulePrelude::Media(arc, location)))
- },
- "supports" => {
- let cond = SupportsCondition::parse(input)?;
- Ok(AtRuleType::WithBlock(AtRulePrelude::Supports(cond, location)))
- },
- "font-face" => {
- Ok(AtRuleType::WithBlock(AtRulePrelude::FontFace(location)))
- },
- "counter-style" => {
- if !cfg!(feature = "gecko") {
- // Support for this rule is not fully implemented in Servo yet.
- return Err(())
- }
- let name = parse_counter_style_name(input)?;
- // ASCII-case-insensitive matches for "decimal" are already lower-cased
- // by `parse_counter_style_name`, so we can use == here.
- // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1359323 use atom!("decimal")
- if name.0 == Atom::from("decimal") {
- return Err(())
- }
- Ok(AtRuleType::WithBlock(AtRulePrelude::CounterStyle(name)))
- },
- "viewport" => {
- if is_viewport_enabled() {
- Ok(AtRuleType::WithBlock(AtRulePrelude::Viewport))
- } else {
- Err(())
- }
- },
- "keyframes" | "-webkit-keyframes" | "-moz-keyframes" => {
- let prefix = if starts_with_ignore_ascii_case(name, "-webkit-") {
- Some(VendorPrefix::WebKit)
- } else if starts_with_ignore_ascii_case(name, "-moz-") {
- Some(VendorPrefix::Moz)
- } else {
- None
- };
- if cfg!(feature = "servo") &&
- prefix.as_ref().map_or(false, |p| matches!(*p, VendorPrefix::Moz)) {
- // Servo should not support @-moz-keyframes.
- return Err(())
- }
- let name = KeyframesName::parse(self.context, input)?;
-
- Ok(AtRuleType::WithBlock(AtRulePrelude::Keyframes(name, prefix, location)))
- },
- "page" => {
- if cfg!(feature = "gecko") {
- Ok(AtRuleType::WithBlock(AtRulePrelude::Page(location)))
- } else {
- Err(())
- }
- },
- "-moz-document" => {
- if cfg!(feature = "gecko") {
- let cond = DocumentCondition::parse(self.context, input)?;
- Ok(AtRuleType::WithBlock(AtRulePrelude::Document(cond, location)))
- } else {
- Err(())
- }
- },
- _ => Err(())
- }
- }
-
- fn parse_block(&mut self, prelude: AtRulePrelude, input: &mut Parser) -> Result<CssRule, ()> {
- match prelude {
- AtRulePrelude::FontFace(location) => {
- let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::FontFace));
- Ok(CssRule::FontFace(Arc::new(self.shared_lock.wrap(
- parse_font_face_block(&context, input, location).into()))))
- }
- AtRulePrelude::CounterStyle(name) => {
- let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::CounterStyle));
- Ok(CssRule::CounterStyle(Arc::new(self.shared_lock.wrap(
- parse_counter_style_body(name, &context, input)?.into()))))
- }
- AtRulePrelude::Media(media_queries, location) => {
- Ok(CssRule::Media(Arc::new(self.shared_lock.wrap(MediaRule {
- media_queries: media_queries,
- rules: self.parse_nested_rules(input, CssRuleType::Media),
- source_location: location,
- }))))
- }
- AtRulePrelude::Supports(cond, location) => {
- let enabled = cond.eval(self.context);
- Ok(CssRule::Supports(Arc::new(self.shared_lock.wrap(SupportsRule {
- condition: cond,
- rules: self.parse_nested_rules(input, CssRuleType::Supports),
- enabled: enabled,
- source_location: location,
- }))))
- }
- AtRulePrelude::Viewport => {
- let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Viewport));
- Ok(CssRule::Viewport(Arc::new(self.shared_lock.wrap(
- try!(ViewportRule::parse(&context, input))))))
- }
- AtRulePrelude::Keyframes(name, prefix, location) => {
- let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Keyframes));
- Ok(CssRule::Keyframes(Arc::new(self.shared_lock.wrap(KeyframesRule {
- name: name,
- keyframes: parse_keyframe_list(&context, input, self.shared_lock),
- vendor_prefix: prefix,
- source_location: location,
- }))))
- }
- AtRulePrelude::Page(location) => {
- let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Page));
- let declarations = parse_property_declaration_list(&context, input);
- Ok(CssRule::Page(Arc::new(self.shared_lock.wrap(PageRule {
- block: Arc::new(self.shared_lock.wrap(declarations)),
- source_location: location,
- }))))
- }
- AtRulePrelude::Document(cond, location) => {
- if cfg!(feature = "gecko") {
- Ok(CssRule::Document(Arc::new(self.shared_lock.wrap(DocumentRule {
- condition: cond,
- rules: self.parse_nested_rules(input, CssRuleType::Document),
- source_location: location,
- }))))
- } else {
- unreachable!()
- }
- }
- }
- }
-}
-
-impl<'a, 'b> QualifiedRuleParser for NestedRuleParser<'a, 'b> {
- type Prelude = SelectorList<SelectorImpl>;
- type QualifiedRule = CssRule;
-
- fn parse_prelude(&mut self, input: &mut Parser) -> Result<SelectorList<SelectorImpl>, ()> {
- let selector_parser = SelectorParser {
- stylesheet_origin: self.stylesheet_origin,
- namespaces: self.context.namespaces.unwrap(),
- };
-
- SelectorList::parse(&selector_parser, input)
- }
-
- fn parse_block(
- &mut self,
- prelude: SelectorList<SelectorImpl>,
- input: &mut Parser
- ) -> Result<CssRule, ()> {
- let location = get_location_with_offset(input.current_source_location(),
- self.context.line_number_offset);
- let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Style));
- let declarations = parse_property_declaration_list(&context, input);
- Ok(CssRule::Style(Arc::new(self.shared_lock.wrap(StyleRule {
- selectors: prelude,
- block: Arc::new(self.shared_lock.wrap(declarations)),
- source_location: location,
- }))))
- }
-}
diff --git a/components/style/stylesheets/counter_style_rule.rs b/components/style/stylesheets/counter_style_rule.rs
new file mode 100644
index 00000000000..a6369be9395
--- /dev/null
+++ b/components/style/stylesheets/counter_style_rule.rs
@@ -0,0 +1,12 @@
+/* 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/. */
+
+// TODO(emilio): unify this, components/style/counter_style.rs, and
+// components/style/gecko/rules.rs
+#![allow(missing_docs)]
+
+#[cfg(feature = "servo")]
+pub use counter_style::CounterStyleRuleData as CounterStyleRule;
+#[cfg(feature = "gecko")]
+pub use gecko::rules::CounterStyleRule;
diff --git a/components/style/document_condition.rs b/components/style/stylesheets/document_rule.rs
index b4e89adcce7..0c79b0bfd8e 100644
--- a/components/style/document_condition.rs
+++ b/components/style/stylesheets/document_rule.rs
@@ -6,19 +6,57 @@
//! initially in CSS Conditional Rules Module Level 3, @document has been postponed to the level 4.
//! We implement the prefixed `@-moz-document`.
-use cssparser::{Parser, Token, serialize_string};
-#[cfg(feature = "gecko")]
-use gecko_bindings::bindings::Gecko_DocumentRule_UseForPresentation;
-#[cfg(feature = "gecko")]
-use gecko_bindings::structs::URLMatchingFunction as GeckoUrlMatchingFunction;
+use cssparser::{Parser, Token, SourceLocation, serialize_string};
use media_queries::Device;
-#[cfg(feature = "gecko")]
-use nsstring::nsCString;
use parser::{Parse, ParserContext};
+use shared_lock::{DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
use std::fmt;
use style_traits::ToCss;
+use stylearc::Arc;
+use stylesheets::CssRules;
use values::specified::url::SpecifiedUrl;
+#[derive(Debug)]
+/// A @-moz-document rule
+pub struct DocumentRule {
+ /// The parsed condition
+ pub condition: DocumentCondition,
+ /// Child rules
+ pub rules: Arc<Locked<CssRules>>,
+ /// The line and column of the rule's source code.
+ pub source_location: SourceLocation,
+}
+
+impl ToCssWithGuard for DocumentRule {
+ fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
+ where W: fmt::Write {
+ try!(dest.write_str("@-moz-document "));
+ try!(self.condition.to_css(dest));
+ try!(dest.write_str(" {"));
+ for rule in self.rules.read_with(guard).0.iter() {
+ try!(dest.write_str(" "));
+ try!(rule.to_css(guard, dest));
+ }
+ dest.write_str(" }")
+ }
+}
+
+impl DeepCloneWithLock for DocumentRule {
+ /// Deep clones this DocumentRule.
+ fn deep_clone_with_lock(
+ &self,
+ lock: &SharedRwLock,
+ guard: &SharedRwLockReadGuard,
+ ) -> Self {
+ let rules = self.rules.read_with(guard);
+ DocumentRule {
+ condition: self.condition.clone(),
+ rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard))),
+ source_location: self.source_location.clone(),
+ }
+ }
+}
+
/// A URL matching function for a `@document` rule's condition.
#[derive(Clone, Debug)]
pub enum UrlMatchingFunction {
@@ -84,12 +122,17 @@ impl UrlMatchingFunction {
#[cfg(feature = "gecko")]
/// Evaluate a URL matching function.
pub fn evaluate(&self, device: &Device) -> bool {
+ use gecko_bindings::bindings::Gecko_DocumentRule_UseForPresentation;
+ use gecko_bindings::structs::URLMatchingFunction as GeckoUrlMatchingFunction;
+ use nsstring::nsCString;
+
let func = match *self {
UrlMatchingFunction::Url(_) => GeckoUrlMatchingFunction::eURL,
UrlMatchingFunction::UrlPrefix(_) => GeckoUrlMatchingFunction::eURLPrefix,
UrlMatchingFunction::Domain(_) => GeckoUrlMatchingFunction::eDomain,
UrlMatchingFunction::RegExp(_) => GeckoUrlMatchingFunction::eRegExp,
};
+
let pattern = nsCString::from(match *self {
UrlMatchingFunction::Url(ref url) => url.as_str(),
UrlMatchingFunction::UrlPrefix(ref pat) |
diff --git a/components/style/stylesheets/font_face_rule.rs b/components/style/stylesheets/font_face_rule.rs
new file mode 100644
index 00000000000..5e178e88e55
--- /dev/null
+++ b/components/style/stylesheets/font_face_rule.rs
@@ -0,0 +1,12 @@
+/* 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/. */
+
+// TODO(emilio): unify this, components/style/font_face.rs, and
+// components/style/gecko/rules.rs
+#![allow(missing_docs)]
+
+#[cfg(feature = "servo")]
+pub use font_face::FontFaceRuleData as FontFaceRule;
+#[cfg(feature = "gecko")]
+pub use gecko::rules::FontFaceRule;
diff --git a/components/style/stylesheets/import_rule.rs b/components/style/stylesheets/import_rule.rs
new file mode 100644
index 00000000000..96edfca2314
--- /dev/null
+++ b/components/style/stylesheets/import_rule.rs
@@ -0,0 +1,59 @@
+/* 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 cssparser::SourceLocation;
+use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
+use std::fmt;
+use style_traits::ToCss;
+use stylearc::Arc;
+use stylesheets::stylesheet::Stylesheet;
+use values::specified::url::SpecifiedUrl;
+
+/// The [`@import`][import] at-rule.
+///
+/// [import]: https://drafts.csswg.org/css-cascade-3/#at-import
+#[derive(Debug)]
+pub struct ImportRule {
+ /// The `<url>` this `@import` rule is loading.
+ pub url: SpecifiedUrl,
+
+ /// The stylesheet is always present.
+ ///
+ /// It contains an empty list of rules and namespace set that is updated
+ /// when it loads.
+ pub stylesheet: Arc<Stylesheet>,
+
+ /// The line and column of the rule's source code.
+ pub source_location: SourceLocation,
+}
+
+impl Clone for ImportRule {
+ fn clone(&self) -> ImportRule {
+ let stylesheet: &Stylesheet = &*self.stylesheet;
+ ImportRule {
+ url: self.url.clone(),
+ stylesheet: Arc::new(stylesheet.clone()),
+ source_location: self.source_location.clone(),
+ }
+ }
+}
+
+impl ToCssWithGuard for ImportRule {
+ fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
+ where W: fmt::Write,
+ {
+ dest.write_str("@import ")?;
+ self.url.to_css(dest)?;
+ let media = self.stylesheet.media.read_with(guard);
+ if !media.is_empty() {
+ dest.write_str(" ")?;
+ media.to_css(dest)?;
+ }
+ dest.write_str(";")
+ }
+}
diff --git a/components/style/keyframes.rs b/components/style/stylesheets/keyframes_rule.rs
index e0c589a1c32..254793c79cc 100644
--- a/components/style/keyframes.rs
+++ b/components/style/stylesheets/keyframes_rule.rs
@@ -4,10 +4,8 @@
//! Keyframes: https://drafts.csswg.org/css-animations/#keyframes
-#![deny(missing_docs)]
-
use cssparser::{AtRuleParser, Parser, QualifiedRuleParser, RuleListParser};
-use cssparser::{DeclarationListParser, DeclarationParser, parse_one_rule};
+use cssparser::{DeclarationListParser, DeclarationParser, parse_one_rule, SourceLocation};
use error_reporting::NullReporter;
use parser::{PARSING_MODE_DEFAULT, ParserContext, log_css_error};
use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock, PropertyId};
@@ -15,11 +13,82 @@ use properties::{PropertyDeclarationId, LonghandId, SourcePropertyDeclaration};
use properties::LonghandIdSet;
use properties::animated_properties::TransitionProperty;
use properties::longhands::transition_timing_function::single_value::SpecifiedValue as SpecifiedTimingFunction;
-use shared_lock::{SharedRwLock, SharedRwLockReadGuard, Locked, ToCssWithGuard};
+use shared_lock::{DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard, Locked, ToCssWithGuard};
use std::fmt;
use style_traits::ToCss;
use stylearc::Arc;
-use stylesheets::{CssRuleType, Stylesheet, VendorPrefix};
+use stylesheets::{CssRuleType, Stylesheet};
+use stylesheets::rule_parser::VendorPrefix;
+use values::KeyframesName;
+
+/// A [`@keyframes`][keyframes] rule.
+///
+/// [keyframes]: https://drafts.csswg.org/css-animations/#keyframes
+#[derive(Debug)]
+pub struct KeyframesRule {
+ /// The name of the current animation.
+ pub name: KeyframesName,
+ /// The keyframes specified for this CSS rule.
+ pub keyframes: Vec<Arc<Locked<Keyframe>>>,
+ /// Vendor prefix type the @keyframes has.
+ pub vendor_prefix: Option<VendorPrefix>,
+ /// The line and column of the rule's source code.
+ pub source_location: SourceLocation,
+}
+
+impl ToCssWithGuard for KeyframesRule {
+ // Serialization of KeyframesRule is not specced.
+ fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
+ where W: fmt::Write,
+ {
+ dest.write_str("@keyframes ")?;
+ self.name.to_css(dest)?;
+ dest.write_str(" {")?;
+ let iter = self.keyframes.iter();
+ for lock in iter {
+ dest.write_str("\n")?;
+ let keyframe = lock.read_with(&guard);
+ keyframe.to_css(guard, dest)?;
+ }
+ dest.write_str("\n}")
+ }
+}
+
+impl KeyframesRule {
+ /// Returns the index of the last keyframe that matches the given selector.
+ /// If the selector is not valid, or no keyframe is found, returns None.
+ ///
+ /// Related spec:
+ /// https://drafts.csswg.org/css-animations-1/#interface-csskeyframesrule-findrule
+ pub fn find_rule(&self, guard: &SharedRwLockReadGuard, selector: &str) -> Option<usize> {
+ if let Ok(selector) = Parser::new(selector).parse_entirely(KeyframeSelector::parse) {
+ for (i, keyframe) in self.keyframes.iter().enumerate().rev() {
+ if keyframe.read_with(guard).selector == selector {
+ return Some(i);
+ }
+ }
+ }
+ None
+ }
+}
+
+impl DeepCloneWithLock for KeyframesRule {
+ fn deep_clone_with_lock(
+ &self,
+ lock: &SharedRwLock,
+ guard: &SharedRwLockReadGuard
+ ) -> Self {
+ KeyframesRule {
+ name: self.name.clone(),
+ keyframes: self.keyframes.iter()
+ .map(|ref x| Arc::new(lock.wrap(
+ x.read_with(guard).deep_clone_with_lock(lock, guard))))
+ .collect(),
+ vendor_prefix: self.vendor_prefix.clone(),
+ source_location: self.source_location.clone(),
+ }
+ }
+}
/// A number from 0 to 1, indicating the percentage of the animation when this
/// keyframe should run.
@@ -150,14 +219,18 @@ impl Keyframe {
};
parse_one_rule(&mut input, &mut rule_parser)
}
+}
+impl DeepCloneWithLock for Keyframe {
/// Deep clones this Keyframe.
- pub fn deep_clone_with_lock(&self,
- lock: &SharedRwLock) -> Keyframe {
- let guard = lock.read();
+ fn deep_clone_with_lock(
+ &self,
+ lock: &SharedRwLock,
+ guard: &SharedRwLockReadGuard
+ ) -> Keyframe {
Keyframe {
selector: self.selector.clone(),
- block: Arc::new(lock.wrap(self.block.read_with(&guard).clone())),
+ block: Arc::new(lock.wrap(self.block.read_with(guard).clone())),
}
}
}
diff --git a/components/style/stylesheets/loader.rs b/components/style/stylesheets/loader.rs
new file mode 100644
index 00000000000..ff38ff3ed2a
--- /dev/null
+++ b/components/style/stylesheets/loader.rs
@@ -0,0 +1,43 @@
+/* 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 stylesheet loader is the abstraction used to trigger network requests
+//! for `@import` rules.
+
+use media_queries::MediaList;
+use shared_lock::Locked;
+use stylearc::Arc;
+use stylesheets::ImportRule;
+
+/// The stylesheet loader is the abstraction used to trigger network requests
+/// for `@import` rules.
+pub trait StylesheetLoader {
+ /// Request a stylesheet after parsing a given `@import` rule.
+ ///
+ /// The called code is responsible to update the `stylesheet` rules field
+ /// when the sheet is done loading.
+ ///
+ /// The convoluted signature allows impls to look at MediaList and
+ /// ImportRule before they’re locked, while keeping the trait object-safe.
+ fn request_stylesheet(
+ &self,
+ media: Arc<Locked<MediaList>>,
+ make_import: &mut FnMut(Arc<Locked<MediaList>>) -> ImportRule,
+ make_arc: &mut FnMut(ImportRule) -> Arc<Locked<ImportRule>>,
+ ) -> Arc<Locked<ImportRule>>;
+}
+
+/// A dummy loader that just creates the import rule with the empty stylesheet.
+pub struct NoOpLoader;
+
+impl StylesheetLoader for NoOpLoader {
+ fn request_stylesheet(
+ &self,
+ media: Arc<Locked<MediaList>>,
+ make_import: &mut FnMut(Arc<Locked<MediaList>>) -> ImportRule,
+ make_arc: &mut FnMut(ImportRule) -> Arc<Locked<ImportRule>>,
+ ) -> Arc<Locked<ImportRule>> {
+ make_arc(make_import(media))
+ }
+}
diff --git a/components/style/stylesheets/media_rule.rs b/components/style/stylesheets/media_rule.rs
new file mode 100644
index 00000000000..13938513d60
--- /dev/null
+++ b/components/style/stylesheets/media_rule.rs
@@ -0,0 +1,60 @@
+/* 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/. */
+
+//! An [`@media`][media] urle.
+//!
+//! [media]: https://drafts.csswg.org/css-conditional/#at-ruledef-media
+
+use cssparser::SourceLocation;
+use media_queries::MediaList;
+use shared_lock::{DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
+use std::fmt;
+use style_traits::ToCss;
+use stylearc::Arc;
+use stylesheets::CssRules;
+
+/// An [`@media`][media] urle.
+///
+/// [media]: https://drafts.csswg.org/css-conditional/#at-ruledef-media
+#[derive(Debug)]
+pub struct MediaRule {
+ /// The list of media queries used by this media rule.
+ pub media_queries: Arc<Locked<MediaList>>,
+ /// The nested rules to this media rule.
+ pub rules: Arc<Locked<CssRules>>,
+ /// The source position where this media rule was found.
+ pub source_location: SourceLocation,
+}
+
+impl ToCssWithGuard for MediaRule {
+ // Serialization of MediaRule is not specced.
+ // https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSMediaRule
+ fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
+ where W: fmt::Write {
+ dest.write_str("@media ")?;
+ self.media_queries.read_with(guard).to_css(dest)?;
+ dest.write_str(" {")?;
+ for rule in self.rules.read_with(guard).0.iter() {
+ dest.write_str(" ")?;
+ rule.to_css(guard, dest)?;
+ }
+ dest.write_str(" }")
+ }
+}
+
+impl DeepCloneWithLock for MediaRule {
+ fn deep_clone_with_lock(
+ &self,
+ lock: &SharedRwLock,
+ guard: &SharedRwLockReadGuard
+ ) -> Self {
+ let media_queries = self.media_queries.read_with(guard);
+ let rules = self.rules.read_with(guard);
+ MediaRule {
+ media_queries: Arc::new(lock.wrap(media_queries.clone())),
+ rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard))),
+ source_location: self.source_location.clone(),
+ }
+ }
+}
diff --git a/components/style/stylesheets/memory.rs b/components/style/stylesheets/memory.rs
new file mode 100644
index 00000000000..44a2e429d5b
--- /dev/null
+++ b/components/style/stylesheets/memory.rs
@@ -0,0 +1,71 @@
+/* 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/. */
+
+//! Memory reporting for the style system when running inside of Gecko.
+
+use shared_lock::SharedRwLockReadGuard;
+use std::os::raw::c_void;
+
+/// Like gecko_bindings::structs::MallocSizeOf, but without the Option<>
+/// wrapper.
+///
+/// Note that functions of this type should not be called via
+/// do_malloc_size_of(), rather than directly.
+pub type MallocSizeOfFn = unsafe extern "C" fn(ptr: *const c_void) -> usize;
+
+/// Call malloc_size_of on ptr, first checking that the allocation isn't empty.
+pub unsafe fn do_malloc_size_of<T>(malloc_size_of: MallocSizeOfFn, ptr: *const T) -> usize {
+ use std::mem::align_of;
+
+ if ptr as usize <= align_of::<T>() {
+ 0
+ } else {
+ malloc_size_of(ptr as *const c_void)
+ }
+}
+
+/// Trait for measuring the size of heap data structures.
+pub trait MallocSizeOf {
+ /// Measure the size of any heap-allocated structures that hang off this
+ /// value, but not the space taken up by the value itself.
+ fn malloc_size_of_children(&self, malloc_size_of: MallocSizeOfFn) -> usize;
+}
+
+/// Like MallocSizeOf, but operates with the global SharedRwLockReadGuard
+/// locked.
+pub trait MallocSizeOfWithGuard {
+ /// Like MallocSizeOf::malloc_size_of_children, but with a |guard| argument.
+ fn malloc_size_of_children(
+ &self,
+ guard: &SharedRwLockReadGuard,
+ malloc_size_of: MallocSizeOfFn
+ ) -> usize;
+}
+
+impl<A: MallocSizeOf, B: MallocSizeOf> MallocSizeOf for (A, B) {
+ fn malloc_size_of_children(&self, malloc_size_of: MallocSizeOfFn) -> usize {
+ self.0.malloc_size_of_children(malloc_size_of) +
+ self.1.malloc_size_of_children(malloc_size_of)
+ }
+}
+
+impl<T: MallocSizeOf> MallocSizeOf for Vec<T> {
+ fn malloc_size_of_children(&self, malloc_size_of: MallocSizeOfFn) -> usize {
+ self.iter().fold(
+ unsafe { do_malloc_size_of(malloc_size_of, self.as_ptr()) },
+ |n, elem| n + elem.malloc_size_of_children(malloc_size_of))
+ }
+}
+
+impl<T: MallocSizeOfWithGuard> MallocSizeOfWithGuard for Vec<T> {
+ fn malloc_size_of_children(
+ &self,
+ guard: &SharedRwLockReadGuard,
+ malloc_size_of: MallocSizeOfFn,
+ ) -> usize {
+ self.iter().fold(
+ unsafe { do_malloc_size_of(malloc_size_of, self.as_ptr()) },
+ |n, elem| n + elem.malloc_size_of_children(guard, malloc_size_of))
+ }
+}
diff --git a/components/style/stylesheets/mod.rs b/components/style/stylesheets/mod.rs
new file mode 100644
index 00000000000..bcc40f1243b
--- /dev/null
+++ b/components/style/stylesheets/mod.rs
@@ -0,0 +1,344 @@
+/* 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/. */
+
+//! Style sheets and their CSS rules.
+
+mod counter_style_rule;
+mod document_rule;
+mod font_face_rule;
+mod import_rule;
+pub mod keyframes_rule;
+mod loader;
+mod media_rule;
+mod memory;
+mod namespace_rule;
+mod page_rule;
+mod rule_list;
+mod rule_parser;
+mod rules_iterator;
+mod style_rule;
+mod stylesheet;
+pub mod supports_rule;
+pub mod viewport_rule;
+
+use cssparser::{parse_one_rule, Parser};
+use error_reporting::NullReporter;
+use parser::{ParserContext, PARSING_MODE_DEFAULT};
+use shared_lock::{DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
+use std::fmt;
+use stylearc::Arc;
+
+pub use self::counter_style_rule::CounterStyleRule;
+pub use self::document_rule::DocumentRule;
+pub use self::font_face_rule::FontFaceRule;
+pub use self::import_rule::ImportRule;
+pub use self::keyframes_rule::KeyframesRule;
+pub use self::loader::StylesheetLoader;
+pub use self::media_rule::MediaRule;
+pub use self::memory::{MallocSizeOf, MallocSizeOfFn, MallocSizeOfWithGuard};
+pub use self::namespace_rule::NamespaceRule;
+pub use self::page_rule::PageRule;
+pub use self::rule_parser::{State, TopLevelRuleParser};
+pub use self::rule_list::{CssRules, CssRulesHelpers};
+pub use self::rules_iterator::{AllRules, EffectiveRules, NestedRuleIterationCondition, RulesIterator};
+pub use self::stylesheet::{Namespaces, Stylesheet, UserAgentStylesheets};
+pub use self::style_rule::StyleRule;
+pub use self::supports_rule::SupportsRule;
+pub use self::viewport_rule::ViewportRule;
+
+/// Extra data that the backend may need to resolve url values.
+#[cfg(not(feature = "gecko"))]
+pub type UrlExtraData = ::servo_url::ServoUrl;
+
+/// Extra data that the backend may need to resolve url values.
+#[cfg(feature = "gecko")]
+pub type UrlExtraData =
+ ::gecko_bindings::sugar::refptr::RefPtr<::gecko_bindings::structs::URLExtraData>;
+
+#[cfg(feature = "gecko")]
+impl UrlExtraData {
+ /// Returns a string for the url.
+ ///
+ /// Unimplemented currently.
+ pub fn as_str(&self) -> &str {
+ // TODO
+ "(stylo: not supported)"
+ }
+}
+
+// XXX We probably need to figure out whether we should mark Eq here.
+// It is currently marked so because properties::UnparsedValue wants Eq.
+#[cfg(feature = "gecko")]
+impl Eq for UrlExtraData {}
+
+/// Each style rule has an origin, which determines where it enters the cascade.
+///
+/// http://dev.w3.org/csswg/css-cascade/#cascading-origins
+#[derive(Clone, PartialEq, Eq, Copy, Debug)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub enum Origin {
+ /// http://dev.w3.org/csswg/css-cascade/#cascade-origin-ua
+ UserAgent,
+
+ /// http://dev.w3.org/csswg/css-cascade/#cascade-origin-author
+ Author,
+
+ /// http://dev.w3.org/csswg/css-cascade/#cascade-origin-user
+ User,
+}
+
+/// A CSS rule.
+///
+/// TODO(emilio): Lots of spec links should be around.
+#[derive(Debug, Clone)]
+#[allow(missing_docs)]
+pub enum CssRule {
+ // No Charset here, CSSCharsetRule has been removed from CSSOM
+ // https://drafts.csswg.org/cssom/#changes-from-5-december-2013
+
+ Namespace(Arc<Locked<NamespaceRule>>),
+ Import(Arc<Locked<ImportRule>>),
+ Style(Arc<Locked<StyleRule>>),
+ Media(Arc<Locked<MediaRule>>),
+ FontFace(Arc<Locked<FontFaceRule>>),
+ CounterStyle(Arc<Locked<CounterStyleRule>>),
+ Viewport(Arc<Locked<ViewportRule>>),
+ Keyframes(Arc<Locked<KeyframesRule>>),
+ Supports(Arc<Locked<SupportsRule>>),
+ Page(Arc<Locked<PageRule>>),
+ Document(Arc<Locked<DocumentRule>>),
+}
+
+impl MallocSizeOfWithGuard for CssRule {
+ fn malloc_size_of_children(
+ &self,
+ guard: &SharedRwLockReadGuard,
+ malloc_size_of: MallocSizeOfFn
+ ) -> usize {
+ match *self {
+ CssRule::Style(ref lock) => {
+ lock.read_with(guard).malloc_size_of_children(guard, malloc_size_of)
+ },
+ // Measurement of these fields may be added later.
+ CssRule::Import(_) => 0,
+ CssRule::Media(_) => 0,
+ CssRule::FontFace(_) => 0,
+ CssRule::CounterStyle(_) => 0,
+ CssRule::Keyframes(_) => 0,
+ CssRule::Namespace(_) => 0,
+ CssRule::Viewport(_) => 0,
+ CssRule::Supports(_) => 0,
+ CssRule::Page(_) => 0,
+ CssRule::Document(_) => 0,
+ }
+ }
+}
+
+#[allow(missing_docs)]
+#[derive(PartialEq, Eq, Copy, Clone)]
+pub enum CssRuleType {
+ // https://drafts.csswg.org/cssom/#the-cssrule-interface
+ Style = 1,
+ Charset = 2,
+ Import = 3,
+ Media = 4,
+ FontFace = 5,
+ Page = 6,
+ // https://drafts.csswg.org/css-animations-1/#interface-cssrule-idl
+ Keyframes = 7,
+ Keyframe = 8,
+ // https://drafts.csswg.org/cssom/#the-cssrule-interface
+ Margin = 9,
+ Namespace = 10,
+ // https://drafts.csswg.org/css-counter-styles-3/#extentions-to-cssrule-interface
+ CounterStyle = 11,
+ // https://drafts.csswg.org/css-conditional-3/#extentions-to-cssrule-interface
+ Supports = 12,
+ // https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#extentions-to-cssrule-interface
+ Document = 13,
+ // https://drafts.csswg.org/css-fonts-3/#om-fontfeaturevalues
+ FontFeatureValues = 14,
+ // https://drafts.csswg.org/css-device-adapt/#css-rule-interface
+ Viewport = 15,
+}
+
+#[allow(missing_docs)]
+pub enum SingleRuleParseError {
+ Syntax,
+ Hierarchy,
+}
+
+#[allow(missing_docs)]
+pub enum RulesMutateError {
+ Syntax,
+ IndexSize,
+ HierarchyRequest,
+ InvalidState,
+}
+
+impl From<SingleRuleParseError> for RulesMutateError {
+ fn from(other: SingleRuleParseError) -> Self {
+ match other {
+ SingleRuleParseError::Syntax => RulesMutateError::Syntax,
+ SingleRuleParseError::Hierarchy => RulesMutateError::HierarchyRequest,
+ }
+ }
+}
+
+impl CssRule {
+ /// Returns the CSSOM rule type of this rule.
+ pub fn rule_type(&self) -> CssRuleType {
+ match *self {
+ CssRule::Style(_) => CssRuleType::Style,
+ CssRule::Import(_) => CssRuleType::Import,
+ CssRule::Media(_) => CssRuleType::Media,
+ CssRule::FontFace(_) => CssRuleType::FontFace,
+ CssRule::CounterStyle(_) => CssRuleType::CounterStyle,
+ CssRule::Keyframes(_) => CssRuleType::Keyframes,
+ CssRule::Namespace(_) => CssRuleType::Namespace,
+ CssRule::Viewport(_) => CssRuleType::Viewport,
+ CssRule::Supports(_) => CssRuleType::Supports,
+ CssRule::Page(_) => CssRuleType::Page,
+ CssRule::Document(_) => CssRuleType::Document,
+ }
+ }
+
+ fn rule_state(&self) -> State {
+ match *self {
+ // CssRule::Charset(..) => State::Start,
+ CssRule::Import(..) => State::Imports,
+ CssRule::Namespace(..) => State::Namespaces,
+ _ => State::Body,
+ }
+ }
+
+ /// Parse a CSS rule.
+ ///
+ /// Returns a parsed CSS rule and the final state of the parser.
+ ///
+ /// Input state is None for a nested rule
+ pub fn parse(
+ css: &str,
+ parent_stylesheet: &Stylesheet,
+ state: Option<State>,
+ loader: Option<&StylesheetLoader>
+ ) -> Result<(Self, State), SingleRuleParseError> {
+ let error_reporter = NullReporter;
+ let context = ParserContext::new(
+ parent_stylesheet.origin,
+ &parent_stylesheet.url_data,
+ &error_reporter,
+ None,
+ PARSING_MODE_DEFAULT,
+ parent_stylesheet.quirks_mode
+ );
+
+ let mut input = Parser::new(css);
+
+ let mut guard = parent_stylesheet.namespaces.write();
+
+ // nested rules are in the body state
+ let state = state.unwrap_or(State::Body);
+ let mut rule_parser = TopLevelRuleParser {
+ stylesheet_origin: parent_stylesheet.origin,
+ context: context,
+ shared_lock: &parent_stylesheet.shared_lock,
+ loader: loader,
+ state: state,
+ namespaces: Some(&mut *guard),
+ };
+
+ match parse_one_rule(&mut input, &mut rule_parser) {
+ Ok(result) => Ok((result, rule_parser.state)),
+ Err(_) => {
+ Err(match rule_parser.state {
+ State::Invalid => SingleRuleParseError::Hierarchy,
+ _ => SingleRuleParseError::Syntax,
+ })
+ }
+ }
+ }
+}
+
+impl DeepCloneWithLock for CssRule {
+ /// Deep clones this CssRule.
+ fn deep_clone_with_lock(
+ &self,
+ lock: &SharedRwLock,
+ guard: &SharedRwLockReadGuard,
+ ) -> CssRule {
+ match *self {
+ CssRule::Namespace(ref arc) => {
+ let rule = arc.read_with(guard);
+ CssRule::Namespace(Arc::new(lock.wrap(rule.clone())))
+ },
+ CssRule::Import(ref arc) => {
+ let rule = arc.read_with(guard);
+ CssRule::Import(Arc::new(lock.wrap(rule.clone())))
+ },
+ CssRule::Style(ref arc) => {
+ let rule = arc.read_with(guard);
+ CssRule::Style(Arc::new(
+ lock.wrap(rule.deep_clone_with_lock(lock, guard))))
+ },
+ CssRule::Media(ref arc) => {
+ let rule = arc.read_with(guard);
+ CssRule::Media(Arc::new(
+ lock.wrap(rule.deep_clone_with_lock(lock, guard))))
+ },
+ CssRule::FontFace(ref arc) => {
+ let rule = arc.read_with(guard);
+ CssRule::FontFace(Arc::new(lock.wrap(rule.clone())))
+ },
+ CssRule::CounterStyle(ref arc) => {
+ let rule = arc.read_with(guard);
+ CssRule::CounterStyle(Arc::new(lock.wrap(rule.clone())))
+ },
+ CssRule::Viewport(ref arc) => {
+ let rule = arc.read_with(guard);
+ CssRule::Viewport(Arc::new(lock.wrap(rule.clone())))
+ },
+ CssRule::Keyframes(ref arc) => {
+ let rule = arc.read_with(guard);
+ CssRule::Keyframes(Arc::new(
+ lock.wrap(rule.deep_clone_with_lock(lock, guard))))
+ },
+ CssRule::Supports(ref arc) => {
+ let rule = arc.read_with(guard);
+ CssRule::Supports(Arc::new(
+ lock.wrap(rule.deep_clone_with_lock(lock, guard))))
+ },
+ CssRule::Page(ref arc) => {
+ let rule = arc.read_with(guard);
+ CssRule::Page(Arc::new(
+ lock.wrap(rule.deep_clone_with_lock(lock, guard))))
+ },
+ CssRule::Document(ref arc) => {
+ let rule = arc.read_with(guard);
+ CssRule::Document(Arc::new(
+ lock.wrap(rule.deep_clone_with_lock(lock, guard))))
+ },
+ }
+ }
+}
+
+impl ToCssWithGuard for CssRule {
+ // https://drafts.csswg.org/cssom/#serialize-a-css-rule
+ fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
+ where W: fmt::Write {
+ match *self {
+ CssRule::Namespace(ref lock) => lock.read_with(guard).to_css(guard, dest),
+ CssRule::Import(ref lock) => lock.read_with(guard).to_css(guard, dest),
+ CssRule::Style(ref lock) => lock.read_with(guard).to_css(guard, dest),
+ CssRule::FontFace(ref lock) => lock.read_with(guard).to_css(guard, dest),
+ CssRule::CounterStyle(ref lock) => lock.read_with(guard).to_css(guard, dest),
+ CssRule::Viewport(ref lock) => lock.read_with(guard).to_css(guard, dest),
+ CssRule::Keyframes(ref lock) => lock.read_with(guard).to_css(guard, dest),
+ CssRule::Media(ref lock) => lock.read_with(guard).to_css(guard, dest),
+ CssRule::Supports(ref lock) => lock.read_with(guard).to_css(guard, dest),
+ CssRule::Page(ref lock) => lock.read_with(guard).to_css(guard, dest),
+ CssRule::Document(ref lock) => lock.read_with(guard).to_css(guard, dest),
+ }
+ }
+}
diff --git a/components/style/stylesheets/namespace_rule.rs b/components/style/stylesheets/namespace_rule.rs
new file mode 100644
index 00000000000..6cfba1d7234
--- /dev/null
+++ b/components/style/stylesheets/namespace_rule.rs
@@ -0,0 +1,39 @@
+/* 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 `@namespace` at-rule.
+
+use {Namespace, Prefix};
+use cssparser::SourceLocation;
+use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
+use std::fmt;
+
+/// A `@namespace` rule.
+#[derive(Clone, Debug, PartialEq)]
+#[allow(missing_docs)]
+pub struct NamespaceRule {
+ /// The namespace prefix, and `None` if it's the default Namespace
+ pub prefix: Option<Prefix>,
+ /// The actual namespace url.
+ pub url: Namespace,
+ /// The source location this rule was found at.
+ pub source_location: SourceLocation,
+}
+
+impl ToCssWithGuard for NamespaceRule {
+ // https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSNamespaceRule
+ fn to_css<W>(&self, _guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
+ where W: fmt::Write {
+ dest.write_str("@namespace ")?;
+ if let Some(ref prefix) = self.prefix {
+ dest.write_str(&*prefix.to_string())?;
+ dest.write_str(" ")?;
+ }
+
+ // FIXME(emilio): Pretty sure this needs some escaping, or something?
+ dest.write_str("url(\"")?;
+ dest.write_str(&*self.url.to_string())?;
+ dest.write_str("\");")
+ }
+}
diff --git a/components/style/stylesheets/page_rule.rs b/components/style/stylesheets/page_rule.rs
new file mode 100644
index 00000000000..4ec3a5f6aaf
--- /dev/null
+++ b/components/style/stylesheets/page_rule.rs
@@ -0,0 +1,60 @@
+/* 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/. */
+
+//! A [`@page`][page] rule.
+//!
+//! [page]: https://drafts.csswg.org/css2/page.html#page-box
+
+use cssparser::SourceLocation;
+use properties::PropertyDeclarationBlock;
+use shared_lock::{DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
+use std::fmt;
+use style_traits::ToCss;
+use stylearc::Arc;
+
+/// A [`@page`][page] rule.
+///
+/// This implements only a limited subset of the CSS
+/// 2.2 syntax.
+///
+/// In this subset, [page selectors][page-selectors] are not implemented.
+///
+/// [page]: https://drafts.csswg.org/css2/page.html#page-box
+/// [page-selectors]: https://drafts.csswg.org/css2/page.html#page-selectors
+#[derive(Debug)]
+pub struct PageRule {
+ /// The declaration block this page rule contains.
+ pub block: Arc<Locked<PropertyDeclarationBlock>>,
+ /// The source position this rule was found at.
+ pub source_location: SourceLocation,
+}
+
+impl ToCssWithGuard for PageRule {
+ /// Serialization of PageRule is not specced, adapted from steps for
+ /// StyleRule.
+ fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
+ where W: fmt::Write,
+ {
+ dest.write_str("@page { ")?;
+ let declaration_block = self.block.read_with(guard);
+ declaration_block.to_css(dest)?;
+ if !declaration_block.declarations().is_empty() {
+ dest.write_str(" ")?;
+ }
+ dest.write_str("}")
+ }
+}
+
+impl DeepCloneWithLock for PageRule {
+ fn deep_clone_with_lock(
+ &self,
+ lock: &SharedRwLock,
+ guard: &SharedRwLockReadGuard,
+ ) -> Self {
+ PageRule {
+ block: Arc::new(lock.wrap(self.block.read_with(&guard).clone())),
+ source_location: self.source_location.clone(),
+ }
+ }
+}
diff --git a/components/style/stylesheets/rule_list.rs b/components/style/stylesheets/rule_list.rs
new file mode 100644
index 00000000000..7067760f44a
--- /dev/null
+++ b/components/style/stylesheets/rule_list.rs
@@ -0,0 +1,168 @@
+/* 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/. */
+
+//! A list of CSS rules.
+
+use shared_lock::{DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard};
+use stylearc::Arc;
+use stylesheets::{CssRule, RulesMutateError};
+use stylesheets::loader::StylesheetLoader;
+use stylesheets::memory::{MallocSizeOfFn, MallocSizeOfWithGuard};
+use stylesheets::rule_parser::State;
+use stylesheets::stylesheet::Stylesheet;
+
+/// A list of CSS rules.
+#[derive(Debug)]
+pub struct CssRules(pub Vec<CssRule>);
+
+impl CssRules {
+ /// Whether this CSS rules is empty.
+ pub fn is_empty(&self) -> bool {
+ self.0.is_empty()
+ }
+}
+
+impl DeepCloneWithLock for CssRules {
+ fn deep_clone_with_lock(
+ &self,
+ lock: &SharedRwLock,
+ guard: &SharedRwLockReadGuard
+ ) -> Self {
+ CssRules(
+ self.0.iter().map(|ref x| x.deep_clone_with_lock(lock, guard)).collect()
+ )
+ }
+}
+
+impl MallocSizeOfWithGuard for CssRules {
+ fn malloc_size_of_children(
+ &self,
+ guard: &SharedRwLockReadGuard,
+ malloc_size_of: MallocSizeOfFn
+ ) -> usize {
+ self.0.malloc_size_of_children(guard, malloc_size_of)
+ }
+}
+
+impl CssRules {
+ /// Trivially construct a new set of CSS rules.
+ pub fn new(rules: Vec<CssRule>, shared_lock: &SharedRwLock) -> Arc<Locked<CssRules>> {
+ Arc::new(shared_lock.wrap(CssRules(rules)))
+ }
+
+ /// Returns whether all the rules in this list are namespace or import
+ /// rules.
+ fn only_ns_or_import(&self) -> bool {
+ self.0.iter().all(|r| {
+ match *r {
+ CssRule::Namespace(..) |
+ CssRule::Import(..) => true,
+ _ => false
+ }
+ })
+ }
+
+ /// https://drafts.csswg.org/cssom/#remove-a-css-rule
+ pub fn remove_rule(&mut self, index: usize) -> Result<(), RulesMutateError> {
+ // Step 1, 2
+ if index >= self.0.len() {
+ return Err(RulesMutateError::IndexSize);
+ }
+
+ {
+ // Step 3
+ let ref rule = self.0[index];
+
+ // Step 4
+ if let CssRule::Namespace(..) = *rule {
+ if !self.only_ns_or_import() {
+ return Err(RulesMutateError::InvalidState);
+ }
+ }
+ }
+
+ // Step 5, 6
+ self.0.remove(index);
+ Ok(())
+ }
+}
+
+/// A trait to implement helpers for `Arc<Locked<CssRules>>`.
+pub trait CssRulesHelpers {
+ /// https://drafts.csswg.org/cssom/#insert-a-css-rule
+ ///
+ /// Written in this funky way because parsing an @import rule may cause us
+ /// to clone a stylesheet from the same document due to caching in the CSS
+ /// loader.
+ ///
+ /// TODO(emilio): We could also pass the write guard down into the loader
+ /// instead, but that seems overkill.
+ fn insert_rule(&self,
+ lock: &SharedRwLock,
+ rule: &str,
+ parent_stylesheet: &Stylesheet,
+ index: usize,
+ nested: bool,
+ loader: Option<&StylesheetLoader>)
+ -> Result<CssRule, RulesMutateError>;
+}
+
+impl CssRulesHelpers for Arc<Locked<CssRules>> {
+ fn insert_rule(&self,
+ lock: &SharedRwLock,
+ rule: &str,
+ parent_stylesheet: &Stylesheet,
+ index: usize,
+ nested: bool,
+ loader: Option<&StylesheetLoader>)
+ -> Result<CssRule, RulesMutateError> {
+ let state = {
+ let read_guard = lock.read();
+ let rules = self.read_with(&read_guard);
+
+ // Step 1, 2
+ if index > rules.0.len() {
+ return Err(RulesMutateError::IndexSize);
+ }
+
+ // Computes the parser state at the given index
+ if nested {
+ None
+ } else if index == 0 {
+ Some(State::Start)
+ } else {
+ rules.0.get(index - 1).map(CssRule::rule_state)
+ }
+ };
+
+ // Step 3, 4
+ // XXXManishearth should we also store the namespace map?
+ let (new_rule, new_state) =
+ CssRule::parse(&rule, parent_stylesheet, state, loader)?;
+
+ {
+ let mut write_guard = lock.write();
+ let mut rules = self.write_with(&mut write_guard);
+ // Step 5
+ // Computes the maximum allowed parser state at a given index.
+ let rev_state = rules.0.get(index).map_or(State::Body, CssRule::rule_state);
+ if new_state > rev_state {
+ // We inserted a rule too early, e.g. inserting
+ // a regular style rule before @namespace rules
+ return Err(RulesMutateError::HierarchyRequest);
+ }
+
+ // Step 6
+ if let CssRule::Namespace(..) = new_rule {
+ if !rules.only_ns_or_import() {
+ return Err(RulesMutateError::InvalidState);
+ }
+ }
+
+ rules.0.insert(index, new_rule.clone());
+ }
+
+ Ok(new_rule)
+ }
+}
diff --git a/components/style/stylesheets/rule_parser.rs b/components/style/stylesheets/rule_parser.rs
new file mode 100644
index 00000000000..b1d250ab51a
--- /dev/null
+++ b/components/style/stylesheets/rule_parser.rs
@@ -0,0 +1,520 @@
+/* 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/. */
+
+//! Parsing of the stylesheet contents.
+
+use {Namespace, Prefix};
+use counter_style::{parse_counter_style_body, parse_counter_style_name};
+use cssparser::{AtRuleParser, AtRuleType, Parser, QualifiedRuleParser, RuleListParser, SourceLocation};
+use font_face::parse_font_face_block;
+use media_queries::{parse_media_query_list, MediaList};
+use parking_lot::RwLock;
+use parser::{Parse, ParserContext, log_css_error};
+use properties::parse_property_declaration_list;
+use selector_parser::{SelectorImpl, SelectorParser};
+use selectors::SelectorList;
+use shared_lock::{Locked, SharedRwLock};
+use std::sync::atomic::AtomicBool;
+use str::starts_with_ignore_ascii_case;
+use stylearc::Arc;
+use stylesheets::{CssRule, CssRules, CssRuleType, Origin, StylesheetLoader};
+use stylesheets::{DocumentRule, ImportRule, KeyframesRule, MediaRule, NamespaceRule, PageRule};
+use stylesheets::{StyleRule, SupportsRule, ViewportRule};
+use stylesheets::document_rule::DocumentCondition;
+use stylesheets::keyframes_rule::parse_keyframe_list;
+use stylesheets::loader::NoOpLoader;
+use stylesheets::stylesheet::{Namespaces, Stylesheet};
+use stylesheets::supports_rule::SupportsCondition;
+use values::CustomIdent;
+use values::KeyframesName;
+use values::specified::url::SpecifiedUrl;
+
+/// The parser for the top-level rules in a stylesheet.
+pub struct TopLevelRuleParser<'a> {
+ /// The origin of the stylesheet we're parsing.
+ pub stylesheet_origin: Origin,
+ /// A reference to the lock we need to use to create rules.
+ pub shared_lock: &'a SharedRwLock,
+ /// A reference to a stylesheet loader if applicable, for `@import` rules.
+ pub loader: Option<&'a StylesheetLoader>,
+ /// The parser context. This initially won't contain any namespaces, but
+ /// will be populated after parsing namespace rules, if any.
+ pub context: ParserContext<'a>,
+ /// The current state of the parser.
+ pub state: State,
+ /// The namespace map we use for parsing. Needs to start as `Some()`, and
+ /// will be taken out after parsing namespace rules, and that reference will
+ /// be moved to `ParserContext`.
+ pub namespaces: Option<&'a mut Namespaces>,
+}
+
+impl<'b> TopLevelRuleParser<'b> {
+ fn nested<'a: 'b>(&'a self) -> NestedRuleParser<'a, 'b> {
+ NestedRuleParser {
+ stylesheet_origin: self.stylesheet_origin,
+ shared_lock: self.shared_lock,
+ context: &self.context,
+ }
+ }
+
+ /// Returns the associated parser context with this rule parser.
+ pub fn context(&self) -> &ParserContext {
+ &self.context
+ }
+
+ /// Returns the current state of the parser.
+ pub fn state(&self) -> State {
+ self.state
+ }
+}
+
+/// The current state of the parser.
+#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
+pub enum State {
+ /// We haven't started parsing rules.
+ Start = 1,
+ /// We're parsing `@import` rules.
+ Imports = 2,
+ /// We're parsing `@namespace` rules.
+ Namespaces = 3,
+ /// We're parsing the main body of the stylesheet.
+ Body = 4,
+ /// We've found an invalid state (as, a namespace rule after style rules),
+ /// and the rest of the stylesheet should be ignored.
+ Invalid = 5,
+}
+
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+/// Vendor prefix.
+pub enum VendorPrefix {
+ /// -moz prefix.
+ Moz,
+ /// -webkit prefix.
+ WebKit,
+}
+
+/// A rule prelude for a given at-rule.
+pub enum AtRulePrelude {
+ /// A @font-face rule prelude.
+ FontFace(SourceLocation),
+ /// A @counter-style rule prelude, with its counter style name.
+ CounterStyle(CustomIdent),
+ /// A @media rule prelude, with its media queries.
+ Media(Arc<Locked<MediaList>>, SourceLocation),
+ /// An @supports rule, with its conditional
+ Supports(SupportsCondition, SourceLocation),
+ /// A @viewport rule prelude.
+ Viewport,
+ /// A @keyframes rule, with its animation name and vendor prefix if exists.
+ Keyframes(KeyframesName, Option<VendorPrefix>, SourceLocation),
+ /// A @page rule prelude.
+ Page(SourceLocation),
+ /// A @document rule, with its conditional.
+ Document(DocumentCondition, SourceLocation),
+}
+
+
+#[cfg(feature = "gecko")]
+fn register_namespace(ns: &Namespace) -> Result<i32, ()> {
+ use gecko_bindings::bindings;
+ let id = unsafe { bindings::Gecko_RegisterNamespace(ns.0.as_ptr()) };
+ if id == -1 {
+ Err(())
+ } else {
+ Ok(id)
+ }
+}
+
+#[cfg(feature = "servo")]
+fn register_namespace(_: &Namespace) -> Result<(), ()> {
+ Ok(()) // servo doesn't use namespace ids
+}
+
+impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
+ type Prelude = AtRulePrelude;
+ type AtRule = CssRule;
+
+ fn parse_prelude(
+ &mut self,
+ name: &str,
+ input: &mut Parser
+ ) -> Result<AtRuleType<AtRulePrelude, CssRule>, ()> {
+ let location = get_location_with_offset(input.current_source_location(),
+ self.context.line_number_offset);
+ match_ignore_ascii_case! { name,
+ "import" => {
+ if self.state > State::Imports {
+ self.state = State::Invalid;
+ return Err(()) // "@import must be before any rule but @charset"
+ }
+
+ self.state = State::Imports;
+ let url_string = input.expect_url_or_string()?;
+ let specified_url = SpecifiedUrl::parse_from_string(url_string, &self.context)?;
+
+ let media = parse_media_query_list(&self.context, input);
+ let media = Arc::new(self.shared_lock.wrap(media));
+
+ let noop_loader = NoOpLoader;
+ let loader = if !specified_url.is_invalid() {
+ self.loader.expect("Expected a stylesheet loader for @import")
+ } else {
+ &noop_loader
+ };
+
+ let mut specified_url = Some(specified_url);
+ let arc = loader.request_stylesheet(media, &mut |media| {
+ ImportRule {
+ url: specified_url.take().unwrap(),
+ stylesheet: Arc::new(Stylesheet {
+ rules: CssRules::new(Vec::new(), self.shared_lock),
+ media: media,
+ shared_lock: self.shared_lock.clone(),
+ origin: self.context.stylesheet_origin,
+ url_data: self.context.url_data.clone(),
+ namespaces: RwLock::new(Namespaces::default()),
+ dirty_on_viewport_size_change: AtomicBool::new(false),
+ disabled: AtomicBool::new(false),
+ quirks_mode: self.context.quirks_mode,
+ }),
+ source_location: location,
+ }
+ }, &mut |import_rule| {
+ Arc::new(self.shared_lock.wrap(import_rule))
+ });
+
+ return Ok(AtRuleType::WithoutBlock(CssRule::Import(arc)))
+ },
+ "namespace" => {
+ if self.state > State::Namespaces {
+ self.state = State::Invalid;
+ return Err(()) // "@namespace must be before any rule but @charset and @import"
+ }
+ self.state = State::Namespaces;
+
+ let prefix_result = input.try(|input| input.expect_ident());
+ let url = Namespace::from(try!(input.expect_url_or_string()));
+
+ let id = register_namespace(&url)?;
+
+ let mut namespaces = self.namespaces.as_mut().unwrap();
+
+ let opt_prefix = if let Ok(prefix) = prefix_result {
+ let prefix = Prefix::from(prefix);
+ namespaces
+ .prefixes
+ .insert(prefix.clone(), (url.clone(), id));
+ Some(prefix)
+ } else {
+ namespaces.default = Some((url.clone(), id));
+ None
+ };
+
+ return Ok(AtRuleType::WithoutBlock(CssRule::Namespace(Arc::new(
+ self.shared_lock.wrap(NamespaceRule {
+ prefix: opt_prefix,
+ url: url,
+ source_location: location,
+ })
+ ))))
+ },
+ // @charset is removed by rust-cssparser if it’s the first rule in the stylesheet
+ // anything left is invalid.
+ "charset" => return Err(()), // (insert appropriate error message)
+ _ => {}
+ }
+ // Don't allow starting with an invalid state
+ if self.state > State::Body {
+ self.state = State::Invalid;
+ return Err(());
+ }
+ self.state = State::Body;
+
+ // "Freeze" the namespace map (no more namespace rules can be parsed
+ // after this point), and stick it in the context.
+ if self.namespaces.is_some() {
+ let namespaces = &*self.namespaces.take().unwrap();
+ self.context.namespaces = Some(namespaces);
+ }
+ AtRuleParser::parse_prelude(&mut self.nested(), name, input)
+ }
+
+ #[inline]
+ fn parse_block(&mut self, prelude: AtRulePrelude, input: &mut Parser) -> Result<CssRule, ()> {
+ AtRuleParser::parse_block(&mut self.nested(), prelude, input)
+ }
+}
+
+
+impl<'a> QualifiedRuleParser for TopLevelRuleParser<'a> {
+ type Prelude = SelectorList<SelectorImpl>;
+ type QualifiedRule = CssRule;
+
+ #[inline]
+ fn parse_prelude(&mut self, input: &mut Parser) -> Result<SelectorList<SelectorImpl>, ()> {
+ self.state = State::Body;
+
+ // "Freeze" the namespace map (no more namespace rules can be parsed
+ // after this point), and stick it in the context.
+ if self.namespaces.is_some() {
+ let namespaces = &*self.namespaces.take().unwrap();
+ self.context.namespaces = Some(namespaces);
+ }
+
+ QualifiedRuleParser::parse_prelude(&mut self.nested(), input)
+ }
+
+ #[inline]
+ fn parse_block(
+ &mut self,
+ prelude: SelectorList<SelectorImpl>,
+ input: &mut Parser
+ ) -> Result<CssRule, ()> {
+ QualifiedRuleParser::parse_block(&mut self.nested(), prelude, input)
+ }
+}
+
+#[derive(Clone)] // shallow, relatively cheap .clone
+struct NestedRuleParser<'a, 'b: 'a> {
+ stylesheet_origin: Origin,
+ shared_lock: &'a SharedRwLock,
+ context: &'a ParserContext<'b>,
+}
+
+impl<'a, 'b> NestedRuleParser<'a, 'b> {
+ fn parse_nested_rules(
+ &mut self,
+ input: &mut Parser,
+ rule_type: CssRuleType
+ ) -> Arc<Locked<CssRules>> {
+ let context = ParserContext::new_with_rule_type(self.context, Some(rule_type));
+
+ let nested_parser = NestedRuleParser {
+ stylesheet_origin: self.stylesheet_origin,
+ shared_lock: self.shared_lock,
+ context: &context,
+ };
+
+ let mut iter = RuleListParser::new_for_nested_rule(input, nested_parser);
+ let mut rules = Vec::new();
+ while let Some(result) = iter.next() {
+ match result {
+ Ok(rule) => rules.push(rule),
+ Err(range) => {
+ let pos = range.start;
+ let message = format!("Unsupported rule: '{}'", iter.input.slice(range));
+ log_css_error(iter.input, pos, &*message, self.context);
+ }
+ }
+ }
+ CssRules::new(rules, self.shared_lock)
+ }
+}
+
+#[cfg(feature = "servo")]
+fn is_viewport_enabled() -> bool {
+ use servo_config::prefs::PREFS;
+ PREFS.get("layout.viewport.enabled").as_boolean().unwrap_or(false)
+}
+
+#[cfg(not(feature = "servo"))]
+fn is_viewport_enabled() -> bool {
+ false // Gecko doesn't support @viewport.
+}
+
+impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> {
+ type Prelude = AtRulePrelude;
+ type AtRule = CssRule;
+
+ fn parse_prelude(
+ &mut self,
+ name: &str,
+ input: &mut Parser
+ ) -> Result<AtRuleType<AtRulePrelude, CssRule>, ()> {
+ let location =
+ get_location_with_offset(
+ input.current_source_location(),
+ self.context.line_number_offset
+ );
+
+ match_ignore_ascii_case! { name,
+ "media" => {
+ let media_queries = parse_media_query_list(self.context, input);
+ let arc = Arc::new(self.shared_lock.wrap(media_queries));
+ Ok(AtRuleType::WithBlock(AtRulePrelude::Media(arc, location)))
+ },
+ "supports" => {
+ let cond = SupportsCondition::parse(input)?;
+ Ok(AtRuleType::WithBlock(AtRulePrelude::Supports(cond, location)))
+ },
+ "font-face" => {
+ Ok(AtRuleType::WithBlock(AtRulePrelude::FontFace(location)))
+ },
+ "counter-style" => {
+ if !cfg!(feature = "gecko") {
+ // Support for this rule is not fully implemented in Servo yet.
+ return Err(())
+ }
+ let name = parse_counter_style_name(input)?;
+ // ASCII-case-insensitive matches for "decimal" are already
+ // lower-cased by `parse_counter_style_name`, so we can use ==
+ // here.
+ if name.0 == atom!("decimal") {
+ return Err(())
+ }
+ Ok(AtRuleType::WithBlock(AtRulePrelude::CounterStyle(name)))
+ },
+ "viewport" => {
+ if is_viewport_enabled() {
+ Ok(AtRuleType::WithBlock(AtRulePrelude::Viewport))
+ } else {
+ Err(())
+ }
+ },
+ "keyframes" | "-webkit-keyframes" | "-moz-keyframes" => {
+ let prefix = if starts_with_ignore_ascii_case(name, "-webkit-") {
+ Some(VendorPrefix::WebKit)
+ } else if starts_with_ignore_ascii_case(name, "-moz-") {
+ Some(VendorPrefix::Moz)
+ } else {
+ None
+ };
+ if cfg!(feature = "servo") &&
+ prefix.as_ref().map_or(false, |p| matches!(*p, VendorPrefix::Moz)) {
+ // Servo should not support @-moz-keyframes.
+ return Err(())
+ }
+ let name = KeyframesName::parse(self.context, input)?;
+
+ Ok(AtRuleType::WithBlock(AtRulePrelude::Keyframes(name, prefix, location)))
+ },
+ "page" => {
+ if cfg!(feature = "gecko") {
+ Ok(AtRuleType::WithBlock(AtRulePrelude::Page(location)))
+ } else {
+ Err(())
+ }
+ },
+ "-moz-document" => {
+ if cfg!(feature = "gecko") {
+ let cond = DocumentCondition::parse(self.context, input)?;
+ Ok(AtRuleType::WithBlock(AtRulePrelude::Document(cond, location)))
+ } else {
+ Err(())
+ }
+ },
+ _ => Err(())
+ }
+ }
+
+ fn parse_block(
+ &mut self,
+ prelude: AtRulePrelude,
+ input: &mut Parser
+ ) -> Result<CssRule, ()> {
+ match prelude {
+ AtRulePrelude::FontFace(location) => {
+ let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::FontFace));
+ Ok(CssRule::FontFace(Arc::new(self.shared_lock.wrap(
+ parse_font_face_block(&context, input, location).into()))))
+ }
+ AtRulePrelude::CounterStyle(name) => {
+ let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::CounterStyle));
+ Ok(CssRule::CounterStyle(Arc::new(self.shared_lock.wrap(
+ parse_counter_style_body(name, &context, input)?.into()))))
+ }
+ AtRulePrelude::Media(media_queries, location) => {
+ Ok(CssRule::Media(Arc::new(self.shared_lock.wrap(MediaRule {
+ media_queries: media_queries,
+ rules: self.parse_nested_rules(input, CssRuleType::Media),
+ source_location: location,
+ }))))
+ }
+ AtRulePrelude::Supports(cond, location) => {
+ let enabled = cond.eval(self.context);
+ Ok(CssRule::Supports(Arc::new(self.shared_lock.wrap(SupportsRule {
+ condition: cond,
+ rules: self.parse_nested_rules(input, CssRuleType::Supports),
+ enabled: enabled,
+ source_location: location,
+ }))))
+ }
+ AtRulePrelude::Viewport => {
+ let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Viewport));
+ Ok(CssRule::Viewport(Arc::new(self.shared_lock.wrap(
+ try!(ViewportRule::parse(&context, input))))))
+ }
+ AtRulePrelude::Keyframes(name, prefix, location) => {
+ let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Keyframes));
+ Ok(CssRule::Keyframes(Arc::new(self.shared_lock.wrap(KeyframesRule {
+ name: name,
+ keyframes: parse_keyframe_list(&context, input, self.shared_lock),
+ vendor_prefix: prefix,
+ source_location: location,
+ }))))
+ }
+ AtRulePrelude::Page(location) => {
+ let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Page));
+ let declarations = parse_property_declaration_list(&context, input);
+ Ok(CssRule::Page(Arc::new(self.shared_lock.wrap(PageRule {
+ block: Arc::new(self.shared_lock.wrap(declarations)),
+ source_location: location,
+ }))))
+ }
+ AtRulePrelude::Document(cond, location) => {
+ if cfg!(feature = "gecko") {
+ Ok(CssRule::Document(Arc::new(self.shared_lock.wrap(DocumentRule {
+ condition: cond,
+ rules: self.parse_nested_rules(input, CssRuleType::Document),
+ source_location: location,
+ }))))
+ } else {
+ unreachable!()
+ }
+ }
+ }
+ }
+}
+
+impl<'a, 'b> QualifiedRuleParser for NestedRuleParser<'a, 'b> {
+ type Prelude = SelectorList<SelectorImpl>;
+ type QualifiedRule = CssRule;
+
+ fn parse_prelude(&mut self, input: &mut Parser) -> Result<SelectorList<SelectorImpl>, ()> {
+ let selector_parser = SelectorParser {
+ stylesheet_origin: self.stylesheet_origin,
+ namespaces: self.context.namespaces.unwrap(),
+ };
+
+ SelectorList::parse(&selector_parser, input)
+ }
+
+ fn parse_block(
+ &mut self,
+ prelude: SelectorList<SelectorImpl>,
+ input: &mut Parser
+ ) -> Result<CssRule, ()> {
+ let location = get_location_with_offset(input.current_source_location(),
+ self.context.line_number_offset);
+ let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Style));
+ let declarations = parse_property_declaration_list(&context, input);
+ Ok(CssRule::Style(Arc::new(self.shared_lock.wrap(StyleRule {
+ selectors: prelude,
+ block: Arc::new(self.shared_lock.wrap(declarations)),
+ source_location: location,
+ }))))
+ }
+}
+
+/// Calculates the location of a rule's source given an offset.
+fn get_location_with_offset(
+ location: SourceLocation,
+ offset: u64
+) -> SourceLocation {
+ SourceLocation {
+ line: location.line + offset as usize - 1,
+ column: location.column,
+ }
+}
diff --git a/components/style/stylesheets/rules_iterator.rs b/components/style/stylesheets/rules_iterator.rs
new file mode 100644
index 00000000000..cbcaf9e0123
--- /dev/null
+++ b/components/style/stylesheets/rules_iterator.rs
@@ -0,0 +1,273 @@
+/* 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/. */
+
+//! 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};
+
+/// An iterator over a list of rules.
+pub struct RulesIterator<'a, 'b, C>
+ where 'b: 'a,
+ C: NestedRuleIterationCondition + 'static,
+{
+ device: &'a Device,
+ quirks_mode: QuirksMode,
+ guard: &'a SharedRwLockReadGuard<'b>,
+ stack: SmallVec<[slice::Iter<'a, CssRule>; 3]>,
+ _phantom: ::std::marker::PhantomData<C>,
+}
+
+impl<'a, 'b, C> RulesIterator<'a, 'b, C>
+ 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)
+ -> Self
+ {
+ let mut stack = SmallVec::new();
+ stack.push(rules.0.iter());
+ Self {
+ device: device,
+ quirks_mode: quirks_mode,
+ guard: guard,
+ stack: stack,
+ _phantom: ::std::marker::PhantomData,
+ }
+ }
+
+ /// Skips all the remaining children of the last nested rule processed.
+ pub fn skip_children(&mut self) {
+ self.stack.pop();
+ }
+}
+
+impl<'a, 'b, C> Iterator for RulesIterator<'a, 'b, C>
+ where 'b: 'a,
+ C: NestedRuleIterationCondition + 'static,
+{
+ type Item = &'a CssRule;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let mut nested_iter_finished = false;
+ while !self.stack.is_empty() {
+ if nested_iter_finished {
+ self.stack.pop();
+ nested_iter_finished = false;
+ continue;
+ }
+
+ let rule;
+ let sub_iter;
+ {
+ let mut nested_iter = self.stack.last_mut().unwrap();
+ rule = match nested_iter.next() {
+ Some(r) => r,
+ None => {
+ nested_iter_finished = true;
+ continue
+ }
+ };
+
+ sub_iter = match *rule {
+ CssRule::Import(ref import_rule) => {
+ let import_rule = import_rule.read_with(self.guard);
+ if !C::process_import(self.guard,
+ self.device,
+ self.quirks_mode,
+ import_rule) {
+ continue;
+ }
+ Some(import_rule.stylesheet.rules.read_with(self.guard).0.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;
+ }
+ Some(doc_rule.rules.read_with(self.guard).0.iter())
+ }
+ CssRule::Media(ref lock) => {
+ let media_rule = lock.read_with(self.guard);
+ if !C::process_media(self.guard,
+ self.device,
+ self.quirks_mode,
+ media_rule) {
+ continue;
+ }
+ Some(media_rule.rules.read_with(self.guard).0.iter())
+ }
+ CssRule::Supports(ref lock) => {
+ let supports_rule = lock.read_with(self.guard);
+ if !C::process_supports(self.guard,
+ self.device,
+ self.quirks_mode,
+ supports_rule) {
+ continue;
+ }
+ Some(supports_rule.rules.read_with(self.guard).0.iter())
+ }
+ CssRule::Namespace(_) |
+ CssRule::Style(_) |
+ CssRule::FontFace(_) |
+ CssRule::CounterStyle(_) |
+ CssRule::Viewport(_) |
+ CssRule::Keyframes(_) |
+ CssRule::Page(_) => None,
+ };
+ }
+
+ if let Some(sub_iter) = sub_iter {
+ self.stack.push(sub_iter);
+ }
+
+ return Some(rule);
+ }
+
+ None
+ }
+}
+
+/// RulesIterator.
+pub trait NestedRuleIterationCondition {
+ /// Whether we should process the nested rules in a given `@import` rule.
+ fn process_import(
+ guard: &SharedRwLockReadGuard,
+ device: &Device,
+ quirks_mode: QuirksMode,
+ rule: &ImportRule)
+ -> bool;
+
+ /// Whether we should process the nested rules in a given `@media` rule.
+ fn process_media(
+ guard: &SharedRwLockReadGuard,
+ device: &Device,
+ quirks_mode: QuirksMode,
+ rule: &MediaRule)
+ -> bool;
+
+ /// Whether we should process the nested rules in a given `@-moz-document`
+ /// rule.
+ fn process_document(
+ guard: &SharedRwLockReadGuard,
+ device: &Device,
+ quirks_mode: QuirksMode,
+ rule: &DocumentRule)
+ -> bool;
+
+ /// Whether we should process the nested rules in a given `@supports` rule.
+ fn process_supports(
+ guard: &SharedRwLockReadGuard,
+ device: &Device,
+ quirks_mode: QuirksMode,
+ rule: &SupportsRule)
+ -> bool;
+}
+
+/// A struct that represents the condition that a rule applies to the document.
+pub struct EffectiveRules;
+
+impl NestedRuleIterationCondition for EffectiveRules {
+ fn process_import(
+ guard: &SharedRwLockReadGuard,
+ device: &Device,
+ quirks_mode: QuirksMode,
+ rule: &ImportRule)
+ -> bool
+ {
+ rule.stylesheet.media.read_with(guard).evaluate(device, quirks_mode)
+ }
+
+ fn process_media(
+ guard: &SharedRwLockReadGuard,
+ device: &Device,
+ quirks_mode: QuirksMode,
+ rule: &MediaRule)
+ -> bool
+ {
+ rule.media_queries.read_with(guard).evaluate(device, quirks_mode)
+ }
+
+ fn process_document(
+ _: &SharedRwLockReadGuard,
+ device: &Device,
+ _: QuirksMode,
+ rule: &DocumentRule)
+ -> bool
+ {
+ rule.condition.evaluate(device)
+ }
+
+ fn process_supports(
+ _: &SharedRwLockReadGuard,
+ _: &Device,
+ _: QuirksMode,
+ rule: &SupportsRule)
+ -> bool
+ {
+ rule.enabled
+ }
+}
+
+/// A filter that processes all the rules in a rule list.
+pub struct AllRules;
+
+impl NestedRuleIterationCondition for AllRules {
+ fn process_import(
+ _: &SharedRwLockReadGuard,
+ _: &Device,
+ _: QuirksMode,
+ _: &ImportRule)
+ -> bool
+ {
+ true
+ }
+
+ fn process_media(
+ _: &SharedRwLockReadGuard,
+ _: &Device,
+ _: QuirksMode,
+ _: &MediaRule)
+ -> bool
+ {
+ true
+ }
+
+ fn process_document(
+ _: &SharedRwLockReadGuard,
+ _: &Device,
+ _: QuirksMode,
+ _: &DocumentRule)
+ -> bool
+ {
+ true
+ }
+
+ fn process_supports(
+ _: &SharedRwLockReadGuard,
+ _: &Device,
+ _: QuirksMode,
+ _: &SupportsRule)
+ -> bool
+ {
+ true
+ }
+}
+
+/// An iterator over all the effective rules of a stylesheet.
+///
+/// NOTE: This iterator recurses into `@import` rules.
+pub type EffectiveRulesIterator<'a, 'b> = RulesIterator<'a, 'b, EffectiveRules>;
diff --git a/components/style/stylesheets/style_rule.rs b/components/style/stylesheets/style_rule.rs
new file mode 100644
index 00000000000..c8a3da00063
--- /dev/null
+++ b/components/style/stylesheets/style_rule.rs
@@ -0,0 +1,76 @@
+/* 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/. */
+
+//! A style rule.
+
+use cssparser::SourceLocation;
+use properties::PropertyDeclarationBlock;
+use selector_parser::SelectorImpl;
+use selectors::SelectorList;
+use shared_lock::{DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
+use std::fmt;
+use style_traits::ToCss;
+use stylearc::Arc;
+use stylesheets::{MallocSizeOf, MallocSizeOfFn, MallocSizeOfWithGuard};
+
+/// A style rule, with selectors and declarations.
+#[derive(Debug)]
+pub struct StyleRule {
+ /// The list of selectors in this rule.
+ pub selectors: SelectorList<SelectorImpl>,
+ /// The declaration block with the properties it contains.
+ pub block: Arc<Locked<PropertyDeclarationBlock>>,
+ /// The location in the sheet where it was found.
+ pub source_location: SourceLocation,
+}
+
+impl DeepCloneWithLock for StyleRule {
+ /// Deep clones this StyleRule.
+ fn deep_clone_with_lock(
+ &self,
+ lock: &SharedRwLock,
+ guard: &SharedRwLockReadGuard,
+ ) -> StyleRule {
+ StyleRule {
+ selectors: self.selectors.clone(),
+ block: Arc::new(lock.wrap(self.block.read_with(guard).clone())),
+ source_location: self.source_location.clone(),
+ }
+ }
+}
+
+impl MallocSizeOfWithGuard for StyleRule {
+ fn malloc_size_of_children(
+ &self,
+ guard: &SharedRwLockReadGuard,
+ malloc_size_of: MallocSizeOfFn
+ ) -> usize {
+ // Measurement of other fields may be added later.
+ self.block.read_with(guard).malloc_size_of_children(malloc_size_of)
+ }
+}
+
+impl ToCssWithGuard for StyleRule {
+ /// https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSStyleRule
+ fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
+ where W: fmt::Write,
+ {
+ use cssparser::ToCss;
+
+ // Step 1
+ self.selectors.to_css(dest)?;
+ // Step 2
+ dest.write_str(" { ")?;
+ // Step 3
+ let declaration_block = self.block.read_with(guard);
+ declaration_block.to_css(dest)?;
+ // Step 4
+ if !declaration_block.declarations().is_empty() {
+ dest.write_str(" ")?;
+ }
+ // Step 5
+ dest.write_str("}")
+ }
+}
+
diff --git a/components/style/stylesheets/stylesheet.rs b/components/style/stylesheets/stylesheet.rs
new file mode 100644
index 00000000000..5e1e9bb5bad
--- /dev/null
+++ b/components/style/stylesheets/stylesheet.rs
@@ -0,0 +1,342 @@
+/* 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/. */
+
+use {Prefix, Namespace};
+use context::QuirksMode;
+use cssparser::{Parser, RuleListParser};
+use error_reporting::ParseErrorReporter;
+use fnv::FnvHashMap;
+use media_queries::{MediaList, Device};
+use parking_lot::RwLock;
+use parser::{PARSING_MODE_DEFAULT, ParserContext, log_css_error};
+use shared_lock::{DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard};
+use std::mem;
+use std::sync::atomic::{AtomicBool, Ordering};
+use stylearc::Arc;
+use stylesheets::{CssRule, CssRules, Origin, UrlExtraData};
+use stylesheets::loader::StylesheetLoader;
+use stylesheets::memory::{MallocSizeOfFn, MallocSizeOfWithGuard};
+use stylesheets::rule_parser::{State, TopLevelRuleParser};
+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>,
+ /// The quirks mode stylesheet.
+ pub quirks_mode_stylesheet: Stylesheet,
+}
+
+/// A set of namespaces applying to a given stylesheet.
+///
+/// The namespace id is used in gecko
+#[derive(Clone, Default, Debug)]
+#[allow(missing_docs)]
+pub struct Namespaces {
+ pub default: Option<(Namespace, NamespaceId)>,
+ pub prefixes: FnvHashMap<Prefix, (Namespace, NamespaceId)>,
+}
+
+/// The structure servo uses to represent a stylesheet.
+#[derive(Debug)]
+pub struct Stylesheet {
+ /// List of rules in the order they were found (important for
+ /// cascading order)
+ pub rules: Arc<Locked<CssRules>>,
+ /// List of media associated with the Stylesheet.
+ pub media: Arc<Locked<MediaList>>,
+ /// The origin of this stylesheet.
+ pub origin: Origin,
+ /// The url data this stylesheet should use.
+ pub url_data: UrlExtraData,
+ /// The lock used for objects inside this stylesheet
+ pub shared_lock: SharedRwLock,
+ /// The namespaces that apply to this stylesheet.
+ pub namespaces: RwLock<Namespaces>,
+ /// Whether this stylesheet would be dirty when the viewport size changes.
+ pub dirty_on_viewport_size_change: AtomicBool,
+ /// Whether this stylesheet should be disabled.
+ pub disabled: AtomicBool,
+ /// The quirks mode of this stylesheet.
+ pub quirks_mode: QuirksMode,
+}
+
+impl Stylesheet {
+ /// Updates an empty stylesheet from a given string of text.
+ pub fn update_from_str(existing: &Stylesheet,
+ css: &str,
+ url_data: &UrlExtraData,
+ stylesheet_loader: Option<&StylesheetLoader>,
+ error_reporter: &ParseErrorReporter,
+ line_number_offset: u64) {
+ let namespaces = RwLock::new(Namespaces::default());
+ // FIXME: we really should update existing.url_data with the given url_data,
+ // otherwise newly inserted rule may not have the right base url.
+ let (rules, dirty_on_viewport_size_change) =
+ Stylesheet::parse_rules(
+ css,
+ url_data,
+ existing.origin,
+ &mut *namespaces.write(),
+ &existing.shared_lock,
+ stylesheet_loader,
+ error_reporter,
+ existing.quirks_mode,
+ line_number_offset
+ );
+
+ mem::swap(&mut *existing.namespaces.write(), &mut *namespaces.write());
+ existing.dirty_on_viewport_size_change
+ .store(dirty_on_viewport_size_change, Ordering::Release);
+
+ // Acquire the lock *after* parsing, to minimize the exclusive section.
+ let mut guard = existing.shared_lock.write();
+ *existing.rules.write_with(&mut guard) = CssRules(rules);
+ }
+
+ fn parse_rules(
+ css: &str,
+ url_data: &UrlExtraData,
+ origin: Origin,
+ namespaces: &mut Namespaces,
+ shared_lock: &SharedRwLock,
+ stylesheet_loader: Option<&StylesheetLoader>,
+ error_reporter: &ParseErrorReporter,
+ quirks_mode: QuirksMode,
+ line_number_offset: u64
+ ) -> (Vec<CssRule>, bool) {
+ let mut rules = Vec::new();
+ let mut input = Parser::new(css);
+
+ let context =
+ ParserContext::new_with_line_number_offset(
+ origin,
+ url_data,
+ error_reporter,
+ line_number_offset,
+ PARSING_MODE_DEFAULT,
+ quirks_mode
+ );
+
+ let rule_parser = TopLevelRuleParser {
+ stylesheet_origin: origin,
+ shared_lock: shared_lock,
+ loader: stylesheet_loader,
+ context: context,
+ state: State::Start,
+ namespaces: Some(namespaces),
+ };
+
+ input.look_for_viewport_percentages();
+
+ {
+ let mut iter =
+ RuleListParser::new_for_stylesheet(&mut input, rule_parser);
+
+ while let Some(result) = iter.next() {
+ match result {
+ Ok(rule) => rules.push(rule),
+ Err(range) => {
+ let pos = range.start;
+ let message = format!("Invalid rule: '{}'", iter.input.slice(range));
+ log_css_error(iter.input, pos, &*message, iter.parser.context());
+ }
+ }
+ }
+ }
+
+ (rules, input.seen_viewport_percentages())
+ }
+
+ /// Creates an empty stylesheet and parses it with a given base url, origin
+ /// and media.
+ ///
+ /// Effectively creates a new stylesheet and forwards the hard work to
+ /// `Stylesheet::update_from_str`.
+ pub fn from_str(css: &str,
+ url_data: UrlExtraData,
+ origin: Origin,
+ media: Arc<Locked<MediaList>>,
+ shared_lock: SharedRwLock,
+ stylesheet_loader: Option<&StylesheetLoader>,
+ error_reporter: &ParseErrorReporter,
+ quirks_mode: QuirksMode,
+ line_number_offset: u64)
+ -> Stylesheet {
+ let namespaces = RwLock::new(Namespaces::default());
+ let (rules, dirty_on_viewport_size_change) = Stylesheet::parse_rules(
+ css,
+ &url_data,
+ origin,
+ &mut *namespaces.write(),
+ &shared_lock,
+ stylesheet_loader,
+ error_reporter,
+ quirks_mode,
+ line_number_offset,
+ );
+
+ Stylesheet {
+ origin: origin,
+ url_data: url_data,
+ namespaces: namespaces,
+ rules: CssRules::new(rules, &shared_lock),
+ media: media,
+ shared_lock: shared_lock,
+ dirty_on_viewport_size_change: AtomicBool::new(dirty_on_viewport_size_change),
+ disabled: AtomicBool::new(false),
+ quirks_mode: quirks_mode,
+ }
+ }
+
+ /// Whether this stylesheet can be dirty on viewport size change.
+ pub fn dirty_on_viewport_size_change(&self) -> bool {
+ self.dirty_on_viewport_size_change.load(Ordering::SeqCst)
+ }
+
+ /// When CSSOM inserts a rule or declaration into this stylesheet, it needs to call this method
+ /// with the return value of `cssparser::Parser::seen_viewport_percentages`.
+ ///
+ /// FIXME: actually make these calls
+ ///
+ /// Note: when *removing* a rule or declaration that contains a viewport percentage,
+ /// to keep the flag accurate we’d need to iterator through the rest of the stylesheet to
+ /// check for *other* such values.
+ ///
+ /// Instead, we conservatively assume there might be some.
+ /// Restyling will some some more work than necessary, but give correct results.
+ pub fn inserted_has_viewport_percentages(&self, has_viewport_percentages: bool) {
+ self.dirty_on_viewport_size_change.fetch_or(has_viewport_percentages, Ordering::SeqCst);
+ }
+
+ /// Returns whether the style-sheet applies for the current device depending
+ /// on the associated MediaList.
+ ///
+ /// Always true if no associated MediaList exists.
+ pub fn is_effective_for_device(&self, device: &Device, guard: &SharedRwLockReadGuard) -> bool {
+ self.media.read_with(guard).evaluate(device, self.quirks_mode)
+ }
+
+ /// Return an iterator over the effective rules within the style-sheet, as
+ /// according to the supplied `Device`.
+ #[inline]
+ pub fn effective_rules<'a, 'b>(
+ &'a self,
+ device: &'a Device,
+ guard: &'a SharedRwLockReadGuard<'b>)
+ -> EffectiveRulesIterator<'a, 'b>
+ {
+ self.iter_rules::<'a, 'b, EffectiveRules>(device, guard)
+ }
+
+ /// Return an iterator using the condition `C`.
+ #[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))
+ }
+
+ /// Returns whether the stylesheet has been explicitly disabled through the
+ /// CSSOM.
+ pub fn disabled(&self) -> bool {
+ self.disabled.load(Ordering::SeqCst)
+ }
+
+ /// Records that the stylesheet has been explicitly disabled through the
+ /// CSSOM.
+ ///
+ /// Returns whether the the call resulted in a change in disabled state.
+ ///
+ /// Disabled stylesheets remain in the document, but their rules are not
+ /// added to the Stylist.
+ pub fn set_disabled(&self, disabled: bool) -> bool {
+ self.disabled.swap(disabled, Ordering::SeqCst) != disabled
+ }
+}
+
+impl Clone for Stylesheet {
+ fn clone(&self) -> Stylesheet {
+ // Create a new lock for our clone.
+ let lock = self.shared_lock.clone();
+ let guard = self.shared_lock.read();
+
+ // Make a deep clone of the rules, using the new lock.
+ let rules = self.rules.read_with(&guard);
+ let cloned_rules = rules.deep_clone_with_lock(&lock, &guard);
+
+ // Make a deep clone of the media, using the new lock.
+ let media = self.media.read_with(&guard);
+ let cloned_media = media.clone();
+
+ Stylesheet {
+ rules: Arc::new(lock.wrap(cloned_rules)),
+ media: Arc::new(lock.wrap(cloned_media)),
+ origin: self.origin,
+ url_data: self.url_data.clone(),
+ shared_lock: lock,
+ namespaces: RwLock::new((*self.namespaces.read()).clone()),
+ dirty_on_viewport_size_change: AtomicBool::new(
+ self.dirty_on_viewport_size_change.load(Ordering::SeqCst)),
+ disabled: AtomicBool::new(self.disabled.load(Ordering::SeqCst)),
+ quirks_mode: self.quirks_mode,
+ }
+ }
+}
+
+impl MallocSizeOfWithGuard for Stylesheet {
+ fn malloc_size_of_children(
+ &self,
+ guard: &SharedRwLockReadGuard,
+ malloc_size_of: MallocSizeOfFn
+ ) -> usize {
+ // Measurement of other fields may be added later.
+ self.rules.read_with(guard).malloc_size_of_children(guard, malloc_size_of)
+ }
+}
+
+macro_rules! rule_filter {
+ ($( $method: ident($variant:ident => $rule_type: ident), )+) => {
+ impl Stylesheet {
+ $(
+ #[allow(missing_docs)]
+ pub fn $method<F>(&self, device: &Device, guard: &SharedRwLockReadGuard, mut f: F)
+ where F: FnMut(&::stylesheets::$rule_type),
+ {
+ use stylesheets::CssRule;
+
+ for rule in self.effective_rules(device, guard) {
+ if let CssRule::$variant(ref lock) = *rule {
+ let rule = lock.read_with(guard);
+ f(&rule)
+ }
+ }
+ }
+ )+
+ }
+ }
+}
+
+rule_filter! {
+ effective_style_rules(Style => StyleRule),
+ effective_media_rules(Media => MediaRule),
+ effective_font_face_rules(FontFace => FontFaceRule),
+ effective_counter_style_rules(CounterStyle => CounterStyleRule),
+ effective_viewport_rules(Viewport => ViewportRule),
+ effective_keyframes_rules(Keyframes => KeyframesRule),
+ effective_supports_rules(Supports => SupportsRule),
+ effective_page_rules(Page => PageRule),
+ effective_document_rules(Document => DocumentRule),
+}
diff --git a/components/style/supports.rs b/components/style/stylesheets/supports_rule.rs
index 9b297963056..2049bbce7ec 100644
--- a/components/style/supports.rs
+++ b/components/style/stylesheets/supports_rule.rs
@@ -4,17 +4,64 @@
//! [@supports rules](https://drafts.csswg.org/css-conditional-3/#at-supports)
-use cssparser::{parse_important, Parser, Token};
+use cssparser::{parse_important, Parser, SourceLocation, Token};
use parser::ParserContext;
use properties::{PropertyId, PropertyDeclaration, SourcePropertyDeclaration};
+use shared_lock::{DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
use std::fmt;
use style_traits::ToCss;
-use stylesheets::CssRuleType;
+use stylearc::Arc;
+use stylesheets::{CssRuleType, CssRules};
+
+/// An [`@supports`][supports] rule.
+///
+/// [supports]: https://drafts.csswg.org/css-conditional-3/#at-supports
+#[derive(Debug)]
+pub struct SupportsRule {
+ /// The parsed condition
+ pub condition: SupportsCondition,
+ /// Child rules
+ pub rules: Arc<Locked<CssRules>>,
+ /// The result of evaluating the condition
+ pub enabled: bool,
+ /// The line and column of the rule's source code.
+ pub source_location: SourceLocation,
+}
+
+impl ToCssWithGuard for SupportsRule {
+ fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
+ where W: fmt::Write {
+ dest.write_str("@supports ")?;
+ self.condition.to_css(dest)?;
+ dest.write_str(" {")?;
+ for rule in self.rules.read_with(guard).0.iter() {
+ dest.write_str(" ")?;
+ rule.to_css(guard, dest)?;
+ }
+ dest.write_str(" }")
+ }
+}
+
+impl DeepCloneWithLock for SupportsRule {
+ fn deep_clone_with_lock(
+ &self,
+ lock: &SharedRwLock,
+ guard: &SharedRwLockReadGuard
+ ) -> Self {
+ let rules = self.rules.read_with(guard);
+ SupportsRule {
+ condition: self.condition.clone(),
+ rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard))),
+ enabled: self.enabled,
+ source_location: self.source_location.clone(),
+ }
+ }
+}
-#[derive(Clone, Debug)]
/// An @supports condition
///
/// https://drafts.csswg.org/css-conditional-3/#at-supports
+#[derive(Clone, Debug)]
pub enum SupportsCondition {
/// `not (condition)`
Not(Box<SupportsCondition>),
@@ -119,7 +166,8 @@ pub fn parse_condition_or_declaration(input: &mut Parser) -> Result<SupportsCond
impl ToCss for SupportsCondition {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
- where W: fmt::Write {
+ where W: fmt::Write,
+ {
match *self {
SupportsCondition::Not(ref cond) => {
dest.write_str("not ")?;
@@ -173,7 +221,8 @@ pub struct Declaration {
impl ToCss for Declaration {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
- where W: fmt::Write {
+ where W: fmt::Write
+ {
dest.write_str(&self.prop)?;
dest.write_str(":")?;
// no space, the `val` already contains any possible spaces
@@ -188,7 +237,7 @@ fn parse_anything(input: &mut Parser) -> String {
input.slice_from(pos).to_owned()
}
-/// consume input till done
+/// Consume input till done
fn consume_all(input: &mut Parser) {
while let Ok(_) = input.next() {}
}
diff --git a/components/style/viewport.rs b/components/style/stylesheets/viewport_rule.rs
index 68ea6a3033f..a510ea5f27c 100644
--- a/components/style/viewport.rs
+++ b/components/style/stylesheets/viewport_rule.rs
@@ -7,8 +7,6 @@
//! [at]: https://drafts.csswg.org/css-device-adapt/#atviewport-rule
//! [meta]: https://drafts.csswg.org/css-device-adapt/#viewport-meta
-#![deny(missing_docs)]
-
use app_units::Au;
use context::QuirksMode;
use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser, parse_important};
diff --git a/components/style/stylist.rs b/components/style/stylist.rs
index a9087b18812..3a5a2489522 100644
--- a/components/style/stylist.rs
+++ b/components/style/stylist.rs
@@ -15,7 +15,6 @@ use font_metrics::FontMetricsProvider;
#[cfg(feature = "gecko")]
use gecko_bindings::structs::{nsIAtom, StyleRuleInclusion};
use invalidation::media_queries::EffectiveMediaQueryResults;
-use keyframes::KeyframesAnimation;
use media_queries::Device;
use properties::{self, CascadeFlags, ComputedValues};
use properties::{AnimationRules, PropertyDeclarationBlock};
@@ -42,8 +41,9 @@ use stylearc::Arc;
use stylesheets::{CounterStyleRule, FontFaceRule};
use stylesheets::{CssRule, StyleRule};
use stylesheets::{Stylesheet, Origin, UserAgentStylesheets};
+use stylesheets::keyframes_rule::KeyframesAnimation;
+use stylesheets::viewport_rule::{self, MaybeNew, ViewportRule};
use thread_state;
-use viewport::{self, MaybeNew, ViewportRule};
pub use ::fnv::FnvHashMap;
@@ -356,7 +356,7 @@ impl Stylist {
self.num_rebuilds += 1;
let cascaded_rule = ViewportRule {
- declarations: viewport::Cascade::from_stylesheets(
+ declarations: viewport_rule::Cascade::from_stylesheets(
doc_stylesheets.clone(), guards.author, &self.device
).finish(),
};
@@ -769,7 +769,7 @@ impl Stylist {
guard: &SharedRwLockReadGuard,
stylesheets: &[Arc<Stylesheet>]) {
let cascaded_rule = ViewportRule {
- declarations: viewport::Cascade::from_stylesheets(stylesheets.iter(), guard, &device).finish(),
+ declarations: viewport_rule::Cascade::from_stylesheets(stylesheets.iter(), guard, &device).finish(),
};
self.viewport_constraints =
diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs
index 6383a2f3fba..4ef66b422c1 100644
--- a/ports/geckolib/glue.rs
+++ b/ports/geckolib/glue.rs
@@ -83,7 +83,6 @@ 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::{self, style_structs};
-use style::keyframes::{Keyframe, KeyframeSelector, KeyframesStepValue};
use style::media_queries::{MediaList, parse_media_query_list};
use style::parallel;
use style::parser::{PARSING_MODE_DEFAULT, ParserContext};
@@ -104,8 +103,9 @@ use style::stylesheets::{CssRule, CssRules, CssRuleType, CssRulesHelpers, Docume
use style::stylesheets::{ImportRule, KeyframesRule, MallocSizeOfWithGuard, MediaRule};
use style::stylesheets::{NamespaceRule, Origin, PageRule, Stylesheet, StyleRule, SupportsRule};
use style::stylesheets::StylesheetLoader as StyleStylesheetLoader;
+use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframesStepValue};
+use style::stylesheets::supports_rule::parse_condition_or_declaration;
use style::stylist::RuleInclusion;
-use style::supports::parse_condition_or_declaration;
use style::thread_state;
use style::timer::Timer;
use style::traversal::{ANIMATION_ONLY, DomTraversal, FOR_CSS_RULE_CHANGES, FOR_RECONSTRUCT};
diff --git a/tests/unit/style/keyframes.rs b/tests/unit/style/keyframes.rs
index 1f3947c5815..8f179fc4999 100644
--- a/tests/unit/style/keyframes.rs
+++ b/tests/unit/style/keyframes.rs
@@ -2,12 +2,12 @@
* 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/. */
-use style::keyframes::{Keyframe, KeyframesAnimation, KeyframePercentage, KeyframeSelector};
-use style::keyframes::{KeyframesStep, KeyframesStepValue};
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, Importance};
use style::properties::animated_properties::TransitionProperty;
use style::shared_lock::SharedRwLock;
use style::stylearc::Arc;
+use style::stylesheets::keyframes_rule::{Keyframe, KeyframesAnimation, KeyframePercentage, KeyframeSelector};
+use style::stylesheets::keyframes_rule::{KeyframesStep, KeyframesStepValue};
use style::values::specified::{LengthOrPercentageOrAuto, NoCalcLength};
#[test]
diff --git a/tests/unit/style/parsing/supports.rs b/tests/unit/style/parsing/supports.rs
index b38f1b09bc0..1521a9977c6 100644
--- a/tests/unit/style/parsing/supports.rs
+++ b/tests/unit/style/parsing/supports.rs
@@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use cssparser::Parser;
-use style::supports::SupportsCondition;
+use style::stylesheets::supports_rule::SupportsCondition;
use style_traits::ToCss;
#[test]
diff --git a/tests/unit/style/stylesheets.rs b/tests/unit/style/stylesheets.rs
index a161c41cabe..ce465b0f1ac 100644
--- a/tests/unit/style/stylesheets.rs
+++ b/tests/unit/style/stylesheets.rs
@@ -15,7 +15,6 @@ use std::sync::Mutex;
use std::sync::atomic::AtomicBool;
use style::context::QuirksMode;
use style::error_reporting::ParseErrorReporter;
-use style::keyframes::{Keyframe, KeyframeSelector, KeyframePercentage};
use style::media_queries::MediaList;
use style::properties::Importance;
use style::properties::{CSSWideKeyword, DeclaredValueOwned, PropertyDeclaration, PropertyDeclarationBlock};
@@ -25,6 +24,7 @@ use style::shared_lock::SharedRwLock;
use style::stylearc::Arc;
use style::stylesheets::{Origin, Namespaces};
use style::stylesheets::{Stylesheet, NamespaceRule, CssRule, CssRules, StyleRule, KeyframesRule};
+use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframePercentage};
use style::values::{KeyframesName, CustomIdent};
use style::values::specified::{LengthOrPercentageOrAuto, Percentage, PositionComponent};
diff --git a/tests/unit/style/viewport.rs b/tests/unit/style/viewport.rs
index 61199660ef8..3d9c5ece244 100644
--- a/tests/unit/style/viewport.rs
+++ b/tests/unit/style/viewport.rs
@@ -13,10 +13,10 @@ use style::parser::{PARSING_MODE_DEFAULT, Parse, ParserContext};
use style::shared_lock::SharedRwLock;
use style::stylearc::Arc;
use style::stylesheets::{CssRuleType, Stylesheet, 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::viewport::*;
use style_traits::PinchZoomFactor;
use style_traits::viewport::*;