--- a/servo/components/style/counter_style/mod.rs
+++ b/servo/components/style/counter_style/mod.rs
@@ -137,17 +137,17 @@ macro_rules! accessor {
}
}
macro_rules! counter_style_descriptors {
(
$( #[$doc: meta] $name: tt $ident: ident / $gecko_ident: ident: $ty: ty = $initial: tt )+
) => {
/// An @counter-style rule
- #[derive(Debug)]
+ #[derive(Clone, Debug)]
pub struct CounterStyleRuleData {
name: CustomIdent,
$(
#[$doc]
$ident: Option<$ty>,
)+
}
--- a/servo/components/style/document_condition.rs
+++ b/servo/components/style/document_condition.rs
@@ -15,17 +15,17 @@ use media_queries::Device;
#[cfg(feature = "gecko")]
use nsstring::nsCString;
use parser::{Parse, ParserContext};
use std::fmt;
use style_traits::ToCss;
use values::specified::url::SpecifiedUrl;
/// A URL matching function for a `@document` rule's condition.
-#[derive(Debug)]
+#[derive(Clone, Debug)]
pub enum UrlMatchingFunction {
/// Exact URL matching function. It evaluates to true whenever the
/// URL of the document being styled is exactly the URL given.
Url(SpecifiedUrl),
/// URL prefix matching function. It evaluates to true whenever the
/// URL of the document being styled has the argument to the
/// function as an initial substring (which is true when the two
/// strings are equal). When the argument is the empty string,
@@ -136,17 +136,17 @@ impl ToCss for UrlMatchingFunction {
/// A `@document` rule's condition.
///
/// https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#at-document
///
/// The `@document` rule's condition is written as a comma-separated list of
/// URL matching functions, and the condition evaluates to true whenever any
/// one of those functions evaluates to true.
-#[derive(Debug)]
+#[derive(Clone, Debug)]
pub struct DocumentCondition(Vec<UrlMatchingFunction>);
impl DocumentCondition {
/// Parse a document condition.
pub fn parse(context: &ParserContext, input: &mut Parser)
-> Result<Self, ()> {
input.parse_comma_separated(|input| UrlMatchingFunction::parse(context, input))
.map(DocumentCondition)
--- a/servo/components/style/font_face.rs
+++ b/servo/components/style/font_face.rs
@@ -189,17 +189,17 @@ impl Parse for Source {
macro_rules! font_face_descriptors_common {
(
$( #[$doc: meta] $name: tt $ident: ident / $gecko_ident: ident: $ty: ty, )*
) => {
/// Data inside a `@font-face` rule.
///
/// https://drafts.csswg.org/css-fonts/#font-face-rule
- #[derive(Debug, PartialEq, Eq)]
+ #[derive(Clone, Debug, PartialEq, Eq)]
pub struct FontFaceRuleData {
$(
#[$doc]
pub $ident: Option<$ty>,
)*
/// Line and column of the @font-face rule source code.
pub source_location: SourceLocation,
}
--- a/servo/components/style/keyframes.rs
+++ b/servo/components/style/keyframes.rs
@@ -66,17 +66,17 @@ impl KeyframePercentage {
};
Ok(percentage)
}
}
/// A keyframes selector is a list of percentages or from/to symbols, which are
/// converted at parse time to percentages.
-#[derive(Debug, PartialEq, Eq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct KeyframeSelector(Vec<KeyframePercentage>);
impl ToCss for KeyframeSelector {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
let mut iter = self.0.iter();
iter.next().unwrap().to_css(dest)?;
for percentage in iter {
write!(dest, ", ")?;
@@ -145,16 +145,26 @@ impl Keyframe {
let mut declarations = SourcePropertyDeclaration::new();
let mut rule_parser = KeyframeListParser {
context: &context,
shared_lock: &parent_stylesheet.shared_lock,
declarations: &mut declarations,
};
parse_one_rule(&mut input, &mut rule_parser)
}
+
+ /// Deep clones this Keyframe.
+ pub fn deep_clone_with_lock(&self,
+ lock: &SharedRwLock) -> Keyframe {
+ let guard = lock.read();
+ Keyframe {
+ selector: self.selector.clone(),
+ block: Arc::new(lock.wrap(self.block.read_with(&guard).clone())),
+ }
+ }
}
/// A keyframes step value. This can be a synthetised keyframes animation, that
/// is, one autogenerated from the current computed values, or a list of
/// declarations to apply.
///
/// TODO: Find a better name for this?
#[derive(Debug)]
--- a/servo/components/style/stylesheets.rs
+++ b/servo/components/style/stylesheets.rs
@@ -32,16 +32,17 @@ use parser::{PARSING_MODE_DEFAULT, Parse
use properties::{PropertyDeclarationBlock, parse_property_declaration_list};
use selector_parser::{SelectorImpl, SelectorParser};
use selectors::parser::SelectorList;
#[cfg(feature = "servo")]
use servo_config::prefs::PREFS;
#[cfg(not(feature = "gecko"))]
use servo_url::ServoUrl;
use shared_lock::{SharedRwLock, Locked, ToCssWithGuard, SharedRwLockReadGuard};
+use std::borrow::Borrow;
use std::cell::Cell;
use std::fmt;
use std::sync::atomic::{AtomicBool, Ordering};
use str::starts_with_ignore_ascii_case;
use style_traits::ToCss;
use stylearc::Arc;
use stylist::FnvHashMap;
use supports::SupportsCondition;
@@ -102,16 +103,25 @@ pub struct Namespaces {
#[derive(Debug)]
pub struct CssRules(pub Vec<CssRule>);
impl CssRules {
/// Whether this CSS rules is empty.
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
+
+ /// Creates a deep clone where each CssRule has also been cloned with
+ /// the provided lock.
+ fn deep_clone_with_lock(&self,
+ lock: &SharedRwLock) -> CssRules {
+ CssRules(
+ self.0.iter().map(|ref x| x.deep_clone_with_lock(lock)).collect()
+ )
+ }
}
#[allow(missing_docs)]
pub enum RulesMutateError {
Syntax,
IndexSize,
HierarchyRequest,
InvalidState,
@@ -464,16 +474,74 @@ impl CssRule {
if let State::Invalid = rule_parser.state.get() {
Err(SingleRuleParseError::Hierarchy)
} else {
Err(SingleRuleParseError::Syntax)
}
}
}
}
+
+ /// Deep clones this CssRule.
+ fn deep_clone_with_lock(&self,
+ lock: &SharedRwLock) -> CssRule {
+ let guard = lock.read();
+ match *self {
+ CssRule::Namespace(ref arc) => {
+ let rule = arc.read_with(&guard);
+ CssRule::Namespace(Arc::new(lock.wrap(rule.clone())))
+ },
+ CssRule::Import(ref arc) => {
+ let rule = arc.read_with(&guard);
+ CssRule::Import(Arc::new(lock.wrap(rule.clone())))
+ },
+ CssRule::Style(ref arc) => {
+ let rule = arc.read_with(&guard);
+ CssRule::Style(Arc::new(
+ lock.wrap(rule.deep_clone_with_lock(lock))))
+ },
+ CssRule::Media(ref arc) => {
+ let rule = arc.read_with(&guard);
+ CssRule::Media(Arc::new(
+ lock.wrap(rule.deep_clone_with_lock(lock))))
+ },
+ CssRule::FontFace(ref arc) => {
+ let rule = arc.read_with(&guard);
+ CssRule::FontFace(Arc::new(lock.wrap(rule.clone())))
+ },
+ CssRule::CounterStyle(ref arc) => {
+ let rule = arc.read_with(&guard);
+ CssRule::CounterStyle(Arc::new(lock.wrap(rule.clone())))
+ },
+ CssRule::Viewport(ref arc) => {
+ let rule = arc.read_with(&guard);
+ CssRule::Viewport(Arc::new(lock.wrap(rule.clone())))
+ },
+ CssRule::Keyframes(ref arc) => {
+ let rule = arc.read_with(&guard);
+ CssRule::Keyframes(Arc::new(
+ lock.wrap(rule.deep_clone_with_lock(lock))))
+ },
+ CssRule::Supports(ref arc) => {
+ let rule = arc.read_with(&guard);
+ CssRule::Supports(Arc::new(
+ lock.wrap(rule.deep_clone_with_lock(lock))))
+ },
+ CssRule::Page(ref arc) => {
+ let rule = arc.read_with(&guard);
+ CssRule::Page(Arc::new(
+ lock.wrap(rule.deep_clone_with_lock(lock))))
+ },
+ CssRule::Document(ref arc) => {
+ let rule = arc.read_with(&guard);
+ CssRule::Document(Arc::new(
+ lock.wrap(rule.deep_clone_with_lock(lock))))
+ },
+ }
+ }
}
impl ToCssWithGuard for CssRule {
// https://drafts.csswg.org/cssom/#serialize-a-css-rule
fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
where W: fmt::Write {
match *self {
CssRule::Namespace(ref lock) => lock.read_with(guard).to_css(guard, dest),
@@ -495,17 +563,17 @@ impl ToCssWithGuard for CssRule {
fn get_location_with_offset(location: SourceLocation, offset: u64)
-> SourceLocation {
SourceLocation {
line: location.line + offset as usize - 1,
column: location.column,
}
}
-#[derive(Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq)]
#[allow(missing_docs)]
pub struct NamespaceRule {
/// `None` for the default Namespace
pub prefix: Option<Prefix>,
pub url: Namespace,
pub source_location: SourceLocation,
}
@@ -535,16 +603,26 @@ pub struct ImportRule {
/// 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>,
}
+impl Clone for ImportRule {
+ fn clone(&self) -> ImportRule {
+ let stylesheet: &Stylesheet = self.stylesheet.borrow();
+ ImportRule {
+ url: self.url.clone(),
+ stylesheet: Arc::new(stylesheet.clone()),
+ }
+ }
+}
+
impl ToCssWithGuard for ImportRule {
fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
where W: fmt::Write {
try!(dest.write_str("@import "));
try!(self.url.to_css(dest));
let media = self.stylesheet.media.read_with(guard);
if !media.is_empty() {
try!(dest.write_str(" "));
@@ -599,16 +677,33 @@ impl KeyframesRule {
return Some(i);
}
}
}
None
}
}
+impl KeyframesRule {
+ /// Deep clones this KeyframesRule.
+ fn deep_clone_with_lock(&self,
+ lock: &SharedRwLock) -> KeyframesRule {
+ let guard = lock.read();
+ KeyframesRule {
+ name: self.name.clone(),
+ keyframes: self.keyframes.iter()
+ .map(|ref x| Arc::new(lock.wrap(
+ x.read_with(&guard).deep_clone_with_lock(lock))))
+ .collect(),
+ vendor_prefix: self.vendor_prefix.clone(),
+ source_location: self.source_location.clone(),
+ }
+ }
+}
+
#[allow(missing_docs)]
#[derive(Debug)]
pub struct MediaRule {
pub media_queries: Arc<Locked<MediaList>>,
pub rules: Arc<Locked<CssRules>>,
pub source_location: SourceLocation,
}
@@ -623,16 +718,31 @@ impl ToCssWithGuard for MediaRule {
for rule in self.rules.read_with(guard).0.iter() {
try!(dest.write_str(" "));
try!(rule.to_css(guard, dest));
}
dest.write_str(" }")
}
}
+impl MediaRule {
+ /// Deep clones this MediaRule.
+ fn deep_clone_with_lock(&self,
+ lock: &SharedRwLock) -> MediaRule {
+ let guard = lock.read();
+ let media_queries = self.media_queries.read_with(&guard);
+ let rules = self.rules.read_with(&guard);
+ MediaRule {
+ media_queries: Arc::new(lock.wrap(media_queries.clone())),
+ rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock))),
+ source_location: self.source_location.clone(),
+ }
+ }
+}
+
#[derive(Debug)]
/// An @supports rule
pub struct SupportsRule {
/// The parsed condition
pub condition: SupportsCondition,
/// Child rules
pub rules: Arc<Locked<CssRules>>,
@@ -651,16 +761,30 @@ impl ToCssWithGuard for SupportsRule {
for rule in self.rules.read_with(guard).0.iter() {
try!(dest.write_str(" "));
try!(rule.to_css(guard, dest));
}
dest.write_str(" }")
}
}
+impl SupportsRule {
+ fn deep_clone_with_lock(&self,
+ lock: &SharedRwLock) -> SupportsRule {
+ let guard = lock.read();
+ let rules = self.rules.read_with(&guard);
+ SupportsRule {
+ condition: self.condition.clone(),
+ rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock))),
+ enabled: self.enabled,
+ source_location: self.source_location.clone(),
+ }
+ }
+}
+
/// A [`@page`][page] rule. This implements only a limited subset of the CSS 2.2 syntax. In this
/// subset, [page selectors][page-selectors] are not implemented.
///
/// [page]: https://drafts.csswg.org/css2/page.html#page-box
/// [page-selectors]: https://drafts.csswg.org/css2/page.html#page-selectors
#[allow(missing_docs)]
#[derive(Debug)]
pub struct PageRule {
@@ -677,16 +801,27 @@ impl ToCssWithGuard for PageRule {
declaration_block.to_css(dest)?;
if declaration_block.declarations().len() > 0 {
write!(dest, " ")?;
}
dest.write_str("}")
}
}
+impl PageRule {
+ fn deep_clone_with_lock(&self,
+ lock: &SharedRwLock) -> PageRule {
+ let guard = lock.read();
+ PageRule {
+ block: Arc::new(lock.wrap(self.block.read_with(&guard).clone())),
+ source_location: self.source_location.clone(),
+ }
+ }
+}
+
#[allow(missing_docs)]
#[derive(Debug)]
pub struct StyleRule {
pub selectors: SelectorList<SelectorImpl>,
pub block: Arc<Locked<PropertyDeclarationBlock>>,
pub source_location: SourceLocation,
}
@@ -706,16 +841,29 @@ impl ToCssWithGuard for StyleRule {
try!(write!(dest, " "));
}
// Step 5
try!(dest.write_str("}"));
Ok(())
}
}
+impl StyleRule {
+ /// Deep clones this StyleRule.
+ fn deep_clone_with_lock(&self,
+ lock: &SharedRwLock) -> StyleRule {
+ let guard = lock.read();
+ StyleRule {
+ selectors: self.selectors.clone(),
+ block: Arc::new(lock.wrap(self.block.read_with(&guard).clone())),
+ source_location: self.source_location.clone(),
+ }
+ }
+}
+
/// A @font-face rule
#[cfg(feature = "servo")]
pub type FontFaceRule = FontFaceRuleData;
/// A @counter-style rule
#[cfg(feature = "servo")]
pub type CounterStyleRule = CounterStyleRuleData;
@@ -739,16 +887,30 @@ impl ToCssWithGuard for DocumentRule {
for rule in self.rules.read_with(guard).0.iter() {
try!(dest.write_str(" "));
try!(rule.to_css(guard, dest));
}
dest.write_str(" }")
}
}
+impl DocumentRule {
+ /// Deep clones this DocumentRule.
+ fn deep_clone_with_lock(&self,
+ lock: &SharedRwLock) -> DocumentRule {
+ let guard = lock.read();
+ let rules = self.rules.read_with(&guard);
+ DocumentRule {
+ condition: self.condition.clone(),
+ rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock))),
+ source_location: self.source_location.clone(),
+ }
+ }
+}
+
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) {
@@ -898,22 +1060,34 @@ impl Stylesheet {
/// added to the Stylist.
pub fn set_disabled(&self, disabled: bool) -> bool {
self.disabled.swap(disabled, Ordering::SeqCst) != disabled
}
}
impl Clone for Stylesheet {
fn clone(&self) -> Stylesheet {
+ // Create a new lock for our clone.
+ let lock = self.shared_lock.clone();
+ let guard = self.shared_lock.read();
+
+ // Make a deep clone of the rules, using the new lock.
+ let rules = self.rules.read_with(&guard);
+ let cloned_rules = rules.deep_clone_with_lock(&lock);
+
+ // Make a deep clone of the media, using the new lock.
+ let media = self.media.read_with(&guard);
+ let cloned_media = media.clone();
+
Stylesheet {
- rules: self.rules.clone(),
- media: self.media.clone(),
+ 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: self.shared_lock.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,
}
}
}
--- a/servo/components/style/supports.rs
+++ b/servo/components/style/supports.rs
@@ -6,17 +6,17 @@
use cssparser::{parse_important, Parser, Token};
use parser::ParserContext;
use properties::{PropertyId, PropertyDeclaration, SourcePropertyDeclaration};
use std::fmt;
use style_traits::ToCss;
use stylesheets::CssRuleType;
-#[derive(Debug)]
+#[derive(Clone, Debug)]
/// An @supports condition
///
/// https://drafts.csswg.org/css-conditional-3/#at-supports
pub enum SupportsCondition {
/// `not (condition)`
Not(Box<SupportsCondition>),
/// `(condition)`
Parenthesized(Box<SupportsCondition>),
@@ -157,17 +157,17 @@ impl ToCss for SupportsCondition {
decl.to_css(dest)?;
dest.write_str(")")
}
SupportsCondition::FutureSyntax(ref s) => dest.write_str(&s),
}
}
}
-#[derive(Debug)]
+#[derive(Clone, Debug)]
/// A possibly-invalid property declaration
pub struct Declaration {
/// The property name
pub prop: String,
/// The property value
pub val: String,
}
--- a/servo/components/style/viewport.rs
+++ b/servo/components/style/viewport.rs
@@ -304,17 +304,17 @@ impl<'a, 'b> DeclarationParser for Viewp
"user-zoom" => ok!(UserZoom(UserZoom::parse)),
"orientation" => ok!(Orientation(Orientation::parse)),
_ => Err(()),
}
}
}
/// A `@viewport` rule.
-#[derive(Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct ViewportRule {
/// The declarations contained in this @viewport rule.
pub declarations: Vec<ViewportDescriptorDeclaration>
}
/// Whitespace as defined by DEVICE-ADAPT ยง 9.2
// TODO: should we just use whitespace as defined by HTML5?