зеркало из https://github.com/mozilla/gecko-dev.git
servo: Merge #17154 - style: Split stylesheets.rs (from emilio:split); r=Manishearth
This file has become quite bloated lately. This commit deletes that file in favor of a set of submodules. The only noticeable change apart from code move, is converting deep_clone_foo methods into a trait. It also unifies logic related to different style rules in the same place. There's some missing work, specially related to font-face and counter-style, but I think this is worth landing in the meantime. Source-Repo: https://github.com/servo/servo Source-Revision: c260c6ede0988f331011f33485964775e1f3a7f9 --HG-- rename : servo/components/style/document_condition.rs => servo/components/style/stylesheets/document_rule.rs rename : servo/components/style/keyframes.rs => servo/components/style/stylesheets/keyframes_rule.rs rename : servo/components/style/supports.rs => servo/components/style/stylesheets/supports_rule.rs rename : servo/components/style/viewport.rs => servo/components/style/stylesheets/viewport_rule.rs extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear extra : subtree_revision : 21cebaaa756b33956e197c7aeb537aecb8938f2a
This commit is contained in:
Родитель
d5b32dba16
Коммит
b152f9ab2e
|
@ -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};
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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]) => {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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> {
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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;
|
|
@ -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) |
|
|
@ -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;
|
|
@ -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(";")
|
||||
}
|
||||
}
|
|
@ -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())),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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("\");")
|
||||
}
|
||||
}
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
|
@ -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>;
|
|
@ -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("}")
|
||||
}
|
||||
}
|
||||
|
|
@ -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),
|
||||
}
|
|
@ -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() {}
|
||||
}
|
|
@ -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};
|
|
@ -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 =
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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};
|
||||
|
||||
|
|
|
@ -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::*;
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче