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:
Emilio Cobos Álvarez 2017-06-04 14:26:30 -07:00
Родитель d5b32dba16
Коммит b152f9ab2e
36 изменённых файлов: 2298 добавлений и 1995 удалений

Просмотреть файл

@ -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 theyre 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 its 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 wed 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::*;