From 444fef164ed172de18fc6f2daf47c494fb4aae1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Fri, 16 Dec 2016 12:05:19 +0100 Subject: [PATCH 01/10] style: Add a struct to represent import rules, and parse them correctly. --- components/style/stylesheets.rs | 76 ++++++++++++++++++++++-- components/style/stylist.rs | 24 ++++++++ components/style/values/specified/url.rs | 10 +++- 3 files changed, 103 insertions(+), 7 deletions(-) diff --git a/components/style/stylesheets.rs b/components/style/stylesheets.rs index 247c153c46b..37dd578f3ad 100644 --- a/components/style/stylesheets.rs +++ b/components/style/stylesheets.rs @@ -26,6 +26,7 @@ use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use style_traits::ToCss; use stylist::FnvHashMap; +use values::specified::url::SpecifiedUrl; use viewport::ViewportRule; @@ -79,7 +80,8 @@ impl CssRules { fn only_ns_or_import(&self) -> bool { self.0.iter().all(|r| { match *r { - CssRule::Namespace(..) /* | CssRule::Import(..) */ => true, + CssRule::Namespace(..) | + CssRule::Import(..) => true, _ => false } }) @@ -180,6 +182,7 @@ pub enum CssRule { // https://drafts.csswg.org/cssom/#changes-from-5-december-2013 Namespace(Arc>), + Import(Arc>), Style(Arc>), Media(Arc>), FontFace(Arc>), @@ -235,6 +238,7 @@ impl CssRule { 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::Keyframes(_) => CssRuleType::Keyframes, @@ -246,7 +250,7 @@ impl CssRule { fn rule_state(&self) -> State { match *self { // CssRule::Charset(..) => State::Start, - // CssRule::Import(..) => State::Imports, + CssRule::Import(..) => State::Imports, CssRule::Namespace(..) => State::Namespaces, _ => State::Body, } @@ -254,10 +258,19 @@ impl CssRule { /// Call `f` with the slice of rules directly contained inside this rule. /// - /// Note that only some types of rules can contain rules. An empty slice is used for others. + /// Note that only some types of rules can contain rules. An empty slice is + /// used for others. pub fn with_nested_rules_and_mq(&self, mut f: F) -> R where F: FnMut(&[CssRule], Option<&MediaList>) -> R { match *self { + CssRule::Import(ref lock) => { + let rule = lock.read(); + let media = rule.stylesheet.media.read(); + let rules = rule.stylesheet.rules.read(); + // FIXME(emilio): Include the nested rules if the stylesheet is + // loaded. + f(&rules.0, Some(&media)) + } CssRule::Namespace(_) | CssRule::Style(_) | CssRule::FontFace(_) | @@ -315,6 +328,7 @@ impl ToCss for CssRule { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { match *self { CssRule::Namespace(ref lock) => lock.read().to_css(dest), + CssRule::Import(ref lock) => lock.read().to_css(dest), CssRule::Style(ref lock) => lock.read().to_css(dest), CssRule::FontFace(ref lock) => lock.read().to_css(dest), CssRule::Viewport(ref lock) => lock.read().to_css(dest), @@ -346,6 +360,34 @@ impl ToCss for NamespaceRule { } } + +/// The [`@import`][import] at-rule. +/// +/// [import]: https://drafts.csswg.org/css-cascade-3/#at-import +#[derive(Debug)] +pub struct ImportRule { + 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, +} + +impl ToCss for ImportRule { + fn to_css(&self, 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(); + if !media.is_empty() { + try!(dest.write_str(" ")); + try!(media.to_css(dest)); + } + Ok(()) + } +} + #[derive(Debug)] pub struct KeyframesRule { pub name: Atom, @@ -612,8 +654,32 @@ impl<'a> AtRuleParser for TopLevelRuleParser<'a> { "import" => { if self.state.get() <= State::Imports { self.state.set(State::Imports); - // TODO: support @import - return Err(()) // "@import is not supported yet" + let url = try!(input.expect_url_or_string()); + let url = + try!(SpecifiedUrl::parse_from_string(url, + &self.context)); + + let media = + Arc::new(RwLock::new(parse_media_query_list(input))); + + let is_valid_url = url.url().is_some(); + + let import_rule = Arc::new(RwLock::new( + ImportRule { + url: url, + stylesheet: Arc::new(Stylesheet { + rules: Arc::new(RwLock::new(CssRules(vec![]))), + media: media, + origin: self.context.stylesheet_origin, + base_url: self.context.base_url.clone(), + namespaces: RwLock::new(Namespaces::default()), + dirty_on_viewport_size_change: AtomicBool::new(false), + disabled: AtomicBool::new(false), + }) + } + )); + + return Ok(AtRuleType::WithoutBlock(CssRule::Import(import_rule))) } else { self.state.set(State::Invalid); return Err(()) // "@import must be before any rule but @charset" diff --git a/components/style/stylist.rs b/components/style/stylist.rs index 33f49f08c3e..4cb4a65b081 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -165,6 +165,25 @@ impl Stylist { self.add_stylesheet(stylesheet); } + debug!("Stylist stats:"); + debug!(" - Got {} sibling-affecting selectors", + self.sibling_affecting_selectors.len()); + debug!(" - Got {} non-common-style-attribute-affecting selectors", + self.non_common_style_affecting_attributes_selectors.len()); + debug!(" - Got {} deps for style-hint calculation", + self.state_deps.len()); + + SelectorImpl::each_precomputed_pseudo_element(|pseudo| { + // TODO: Consider not doing this and just getting the rules on the + // fly. It should be a bit slower, but we'd take rid of the + // extra field, and avoid this precomputation entirely. + if let Some(map) = self.pseudos_map.remove(&pseudo) { + let mut declarations = vec![]; + map.user_agent.get_universal_rules(&mut declarations); + self.precomputed_pseudo_element_decls.insert(pseudo, declarations); + } + }); + self.is_device_dirty = false; true } @@ -211,6 +230,10 @@ impl Stylist { } } } + CssRule::Import(ref import) => { + let import = import.read(); + self.add_stylesheet(&import.stylesheet) + } CssRule::Keyframes(ref keyframes_rule) => { let keyframes_rule = keyframes_rule.read(); debug!("Found valid keyframes rule: {:?}", *keyframes_rule); @@ -250,6 +273,7 @@ impl Stylist { }); } + /// Computes the style for a given "precomputed" pseudo-element, taking the /// universal rules and applying them. /// diff --git a/components/style/values/specified/url.rs b/components/style/values/specified/url.rs index d3100586c52..878fbc018b8 100644 --- a/components/style/values/specified/url.rs +++ b/components/style/values/specified/url.rs @@ -11,6 +11,7 @@ use parser::{Parse, ParserContext}; #[cfg(feature = "gecko")] use parser::ParserContextExtraData; use servo_url::ServoUrl; +use std::borrow::Cow; use std::fmt::{self, Write}; use std::ptr; use std::sync::Arc; @@ -76,7 +77,14 @@ pub struct SpecifiedUrl { impl Parse for SpecifiedUrl { fn parse(context: &ParserContext, input: &mut Parser) -> Result { let url = try!(input.expect_url()); + Self::parse_from_string(url, context) + } +} +impl SpecifiedUrl { + pub fn parse_from_string<'a>(url: Cow<'a, str>, + context: &ParserContext) + -> Result { let extra_data = match UrlExtraData::make_from(context) { Some(extra_data) => extra_data, None => { @@ -96,9 +104,7 @@ impl Parse for SpecifiedUrl { extra_data: extra_data, }) } -} -impl SpecifiedUrl { pub fn extra_data(&self) -> &UrlExtraData { &self.extra_data } From a42cfae15385537451c6c70fc693fec24a0a5535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Fri, 16 Dec 2016 12:10:05 +0100 Subject: [PATCH 02/10] style: Add a StylesheetLoader abstraction, and make it a no-op on Geckolib. Servo doesn't compile at this stage. --- components/layout_thread/lib.rs | 4 +++- components/style/stylesheets.rs | 18 ++++++++++++++++++ ports/geckolib/glue.rs | 9 +++++++-- ports/geckolib/lib.rs | 1 + ports/geckolib/stylesheet_loader.rs | 21 +++++++++++++++++++++ tests/unit/style/media_queries.rs | 6 ++++-- tests/unit/style/stylesheets.rs | 5 ++++- tests/unit/style/viewport.rs | 1 + tests/unit/stylo/lib.rs | 3 +++ 9 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 ports/geckolib/stylesheet_loader.rs diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index 20ef9ff6cb4..342f8195cb6 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -1542,6 +1542,7 @@ fn get_ua_stylesheets() -> Result { None, Origin::UserAgent, Default::default(), + None, Box::new(StdoutErrorReporter), ParserContextExtraData::default())) } @@ -1555,7 +1556,8 @@ fn get_ua_stylesheets() -> Result { for &(ref contents, ref url) in &opts::get().user_stylesheets { user_or_user_agent_stylesheets.push(Stylesheet::from_bytes( &contents, url.clone(), None, None, Origin::User, Default::default(), - Box::new(StdoutErrorReporter), ParserContextExtraData::default())); + None, Box::new(StdoutErrorReporter), + ParserContextExtraData::default())); } let quirks_mode_stylesheet = try!(parse_ua_stylesheet("quirks-mode.css")); diff --git a/components/style/stylesheets.rs b/components/style/stylesheets.rs index 37dd578f3ad..1933d98e583 100644 --- a/components/style/stylesheets.rs +++ b/components/style/stylesheets.rs @@ -307,6 +307,7 @@ impl CssRule { let mut rule_parser = TopLevelRuleParser { stylesheet_origin: parent_stylesheet.origin, context: context, + loader: None, state: Cell::new(state), namespaces: &mut namespaces, }; @@ -605,9 +606,20 @@ rule_filter! { effective_keyframes_rules(Keyframes => KeyframesRule), } +/// 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. + fn request_stylesheet(&self, import: &Arc>); +} + struct TopLevelRuleParser<'a> { stylesheet_origin: Origin, namespaces: &'a mut Namespaces, + loader: Option<&'a StylesheetLoader>, context: ParserContext<'a>, state: Cell, } @@ -679,6 +691,12 @@ impl<'a> AtRuleParser for TopLevelRuleParser<'a> { } )); + if is_valid_url { + let loader = self.loader + .expect("Expected a stylesheet loader for @import"); + loader.request_stylesheet(&import_rule); + } + return Ok(AtRuleType::WithoutBlock(CssRule::Import(import_rule))) } else { self.state.set(State::Invalid); diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index 394653afd8e..dcec9a5c1ac 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -60,6 +60,7 @@ use style::thread_state; use style::timer::Timer; use style::traversal::{recalc_style_at, DomTraversalContext, PerLevelTraversalData}; use style_traits::ToCss; +use stylesheet_loader::StylesheetLoader; /* * For Gecko->Servo function calls, we need to redeclare the same signature that was declared in @@ -232,8 +233,10 @@ pub extern "C" fn Servo_StyleSheet_Empty(mode: SheetParsingMode) -> RawServoStyl SheetParsingMode::eUserSheetFeatures => Origin::User, SheetParsingMode::eAgentSheetFeatures => Origin::UserAgent, }; + let loader = StylesheetLoader::new(); let sheet = Arc::new(Stylesheet::from_str( - "", url, origin, Default::default(), Box::new(StdoutErrorReporter), extra_data)); + "", url, origin, Default::default(), Some(&loader), + Box::new(StdoutErrorReporter), extra_data)); unsafe { transmute(sheet) } @@ -262,8 +265,10 @@ pub extern "C" fn Servo_StyleSheet_FromUTF8Bytes(data: *const nsACString, referrer: Some(GeckoArcURI::new(referrer)), principal: Some(GeckoArcPrincipal::new(principal)), }}; + let loader = StylesheetLoader::new(); let sheet = Arc::new(Stylesheet::from_str( - input, url, origin, Default::default(), Box::new(StdoutErrorReporter), extra_data)); + input, url, origin, Default::default(), Some(&loader), + Box::new(StdoutErrorReporter), extra_data)); unsafe { transmute(sheet) } diff --git a/ports/geckolib/lib.rs b/ports/geckolib/lib.rs index 0087e6aece6..1225045e45c 100644 --- a/ports/geckolib/lib.rs +++ b/ports/geckolib/lib.rs @@ -18,6 +18,7 @@ extern crate style_traits; #[allow(non_snake_case)] pub mod glue; +mod stylesheet_loader; // FIXME(bholley): This should probably go away once we harmonize the allocators. #[no_mangle] diff --git a/ports/geckolib/stylesheet_loader.rs b/ports/geckolib/stylesheet_loader.rs new file mode 100644 index 00000000000..9db89333747 --- /dev/null +++ b/ports/geckolib/stylesheet_loader.rs @@ -0,0 +1,21 @@ +/* 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 parking_lot::RwLock; +use std::sync::Arc; +use style::stylesheets::{ImportRule, StylesheetLoader as StyleStylesheetLoader}; + +pub struct StylesheetLoader; + +impl StylesheetLoader { + pub fn new() -> Self { + StylesheetLoader + } +} + +impl StyleStylesheetLoader for StylesheetLoader { + fn request_stylesheet(&self, _import: &Arc>) { + // FIXME(emilio): Implement `@import` in stylo. + } +} diff --git a/tests/unit/style/media_queries.rs b/tests/unit/style/media_queries.rs index 8c91a5b52bb..ac5a9b501c2 100644 --- a/tests/unit/style/media_queries.rs +++ b/tests/unit/style/media_queries.rs @@ -29,7 +29,8 @@ fn test_media_rule(css: &str, callback: F) where F: Fn(&MediaList, &str) { let url = ServoUrl::parse("http://localhost").unwrap(); let stylesheet = Stylesheet::from_str( css, url, Origin::Author, Default::default(), - Box::new(CSSErrorReporterTest), ParserContextExtraData::default()); + None, Box::new(CSSErrorReporterTest), + ParserContextExtraData::default()); let mut rule_count = 0; media_queries(&stylesheet.rules.read().0, &mut |mq| { rule_count += 1; @@ -53,7 +54,8 @@ fn media_query_test(device: &Device, css: &str, expected_rule_count: usize) { let url = ServoUrl::parse("http://localhost").unwrap(); let ss = Stylesheet::from_str( css, url, Origin::Author, Default::default(), - Box::new(CSSErrorReporterTest), ParserContextExtraData::default()); + None, Box::new(CSSErrorReporterTest), + ParserContextExtraData::default()); let mut rule_count = 0; ss.effective_style_rules(device, |_| rule_count += 1); assert!(rule_count == expected_rule_count, css.to_owned()); diff --git a/tests/unit/style/stylesheets.rs b/tests/unit/style/stylesheets.rs index d4c8bcc28a1..40ad06d8e4f 100644 --- a/tests/unit/style/stylesheets.rs +++ b/tests/unit/style/stylesheets.rs @@ -52,6 +52,7 @@ fn test_parse_stylesheet() { }"; let url = ServoUrl::parse("about::test").unwrap(); let stylesheet = Stylesheet::from_str(css, url.clone(), Origin::UserAgent, Default::default(), + None, Box::new(CSSErrorReporterTest), ParserContextExtraData::default()); let mut namespaces = Namespaces::default(); @@ -332,7 +333,9 @@ fn test_report_error_stylesheet() { let errors = error_reporter.errors.clone(); - Stylesheet::from_str(css, url, Origin::UserAgent, Default::default(), error_reporter, + Stylesheet::from_str(css, url, Origin::UserAgent, Default::default(), + None, + error_reporter, ParserContextExtraData::default()); let mut errors = errors.lock().unwrap(); diff --git a/tests/unit/style/viewport.rs b/tests/unit/style/viewport.rs index 9a3bc773a9c..2ca9733afc2 100644 --- a/tests/unit/style/viewport.rs +++ b/tests/unit/style/viewport.rs @@ -25,6 +25,7 @@ macro_rules! stylesheet { ServoUrl::parse("http://localhost").unwrap(), Origin::$origin, Default::default(), + None, $error_reporter, ParserContextExtraData::default() )) diff --git a/tests/unit/stylo/lib.rs b/tests/unit/stylo/lib.rs index d28f0663d0e..35fcdd01602 100644 --- a/tests/unit/stylo/lib.rs +++ b/tests/unit/stylo/lib.rs @@ -16,5 +16,8 @@ extern crate style_traits; mod sanity_checks; +#[path = "../../../ports/geckolib/stylesheet_loader.rs"] +mod stylesheet_loader; + mod servo_function_signatures; From ca93a2dcec06bbae68b17a2faf11ca53d0691fe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Fri, 16 Dec 2016 12:11:42 +0100 Subject: [PATCH 03/10] style: Add a way to update an empty stylesheet for @import. We'll update the empty stylesheet we've created when instantiating the ImportRule when the stylesheet finishes loading. --- components/style/media_queries.rs | 4 + components/style/stylesheets.rs | 121 ++++++++++++++++++++++-------- 2 files changed, 92 insertions(+), 33 deletions(-) diff --git a/components/style/media_queries.rs b/components/style/media_queries.rs index 9bdfcfc748d..a450995c565 100644 --- a/components/style/media_queries.rs +++ b/components/style/media_queries.rs @@ -297,4 +297,8 @@ impl MediaList { } }) } + + pub fn is_empty(&self) -> bool { + self.media_queries.is_empty() + } } diff --git a/components/style/stylesheets.rs b/components/style/stylesheets.rs index 1933d98e583..1689953ba8a 100644 --- a/components/style/stylesheets.rs +++ b/components/style/stylesheets.rs @@ -56,6 +56,12 @@ pub struct Namespaces { #[derive(Debug)] pub struct CssRules(pub Vec); +impl CssRules { + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } +} + pub enum RulesMutateError { Syntax, IndexSize, @@ -106,8 +112,9 @@ impl CssRules { // Step 3, 4 // XXXManishearth should we also store the namespace map? - let (new_rule, new_state) = try!(CssRule::parse(&rule, parent_stylesheet, - ParserContextExtraData::default(), state)); + let (new_rule, new_state) = + try!(CssRule::parse(&rule, parent_stylesheet, + ParserContextExtraData::default(), state)); // Step 5 // Computes the maximum allowed parser state at a given index. @@ -464,54 +471,102 @@ impl Stylesheet { environment_encoding: Option, origin: Origin, media: MediaList, + stylesheet_loader: Option<&StylesheetLoader>, error_reporter: Box, extra_data: ParserContextExtraData) -> Stylesheet { let (string, _) = decode_stylesheet_bytes( bytes, protocol_encoding_label, environment_encoding); - Stylesheet::from_str(&string, base_url, origin, media, error_reporter, extra_data) + Stylesheet::from_str(&string, + base_url, + origin, + media, + stylesheet_loader, + error_reporter, + extra_data) } - pub fn from_str(css: &str, base_url: ServoUrl, origin: Origin, media: MediaList, - error_reporter: Box, - extra_data: ParserContextExtraData) -> Stylesheet { - let mut namespaces = Namespaces::default(); - let mut rules = vec![]; - let dirty_on_viewport_size_change; + pub fn update_from_bytes(existing: &Stylesheet, + bytes: &[u8], + protocol_encoding_label: Option<&str>, + environment_encoding: Option, + stylesheet_loader: Option<&StylesheetLoader>, + error_reporter: Box, + extra_data: ParserContextExtraData) { + assert!(existing.rules.read().is_empty()); + let (string, _) = decode_stylesheet_bytes( + bytes, protocol_encoding_label, environment_encoding); + Self::update_from_str(existing, + &string, + stylesheet_loader, + error_reporter, + extra_data) + } + + pub fn update_from_str(existing: &Stylesheet, + css: &str, + stylesheet_loader: Option<&StylesheetLoader>, + error_reporter: Box, + extra_data: ParserContextExtraData) { + let mut rules = existing.rules.write(); + let mut namespaces = existing.namespaces.write(); + + assert!(rules.is_empty()); + + let mut input = Parser::new(css); + let rule_parser = TopLevelRuleParser { + stylesheet_origin: existing.origin, + namespaces: &mut namespaces, + loader: stylesheet_loader, + context: ParserContext::new_with_extra_data(existing.origin, + &existing.base_url, + error_reporter, + extra_data), + state: Cell::new(State::Start), + }; + + input.look_for_viewport_percentages(); + { - let rule_parser = TopLevelRuleParser { - stylesheet_origin: origin, - namespaces: &mut namespaces, - context: ParserContext::new_with_extra_data(origin, &base_url, error_reporter, extra_data), - state: Cell::new(State::Start), - }; - let mut input = Parser::new(css); - 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); - } + let mut iter = RuleListParser::new_for_stylesheet(&mut input, rule_parser); + while let Some(result) = iter.next() { + match result { + Ok(rule) => rules.0.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); } } } - dirty_on_viewport_size_change = input.seen_viewport_percentages(); } - Stylesheet { + existing.dirty_on_viewport_size_change + .store(input.seen_viewport_percentages(), Ordering::Release); + } + + pub fn from_str(css: &str, base_url: ServoUrl, origin: Origin, + media: MediaList, + stylesheet_loader: Option<&StylesheetLoader>, + error_reporter: Box, + extra_data: ParserContextExtraData) -> Stylesheet { + let s = Stylesheet { origin: origin, base_url: base_url, - namespaces: RwLock::new(namespaces), - rules: CssRules::new(rules), + namespaces: RwLock::new(Namespaces::default()), + rules: CssRules::new(vec![]), media: Arc::new(RwLock::new(media)), - dirty_on_viewport_size_change: AtomicBool::new(dirty_on_viewport_size_change), + dirty_on_viewport_size_change: AtomicBool::new(false), disabled: AtomicBool::new(false), - } + }; + + Self::update_from_str(&s, + css, + stylesheet_loader, + error_reporter, + extra_data); + + s } pub fn dirty_on_viewport_size_change(&self) -> bool { From b86aa41568463ed8454037dbd4247dd939c3f114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Fri, 16 Dec 2016 12:13:32 +0100 Subject: [PATCH 04/10] script: Add CSSImportRule WebIDL interface. --- components/script/dom/bindings/trace.rs | 8 ++- components/script/dom/cssimportrule.rs | 53 +++++++++++++++++++ components/script/dom/cssrule.rs | 4 ++ components/script/dom/mod.rs | 1 + .../script/dom/webidls/CSSImportRule.webidl | 11 ++++ .../wpt/mozilla/tests/mozilla/interfaces.html | 1 + 6 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 components/script/dom/cssimportrule.rs create mode 100644 components/script/dom/webidls/CSSImportRule.webidl diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index ad8dc6b935e..3365c106bd9 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -98,7 +98,7 @@ use style::keyframes::Keyframe; use style::media_queries::MediaList; use style::properties::PropertyDeclarationBlock; use style::selector_parser::{PseudoElement, Snapshot}; -use style::stylesheets::{CssRules, KeyframesRule, MediaRule, NamespaceRule, StyleRule}; +use style::stylesheets::{CssRules, KeyframesRule, MediaRule, NamespaceRule, StyleRule, ImportRule}; use style::values::specified::Length; use style::viewport::ViewportRule; use time::Duration; @@ -525,6 +525,12 @@ unsafe impl JSTraceable for RwLock { } } +unsafe impl JSTraceable for RwLock { + unsafe fn trace(&self, _trc: *mut JSTracer) { + // Do nothing. + } +} + unsafe impl JSTraceable for RwLock { unsafe fn trace(&self, _trc: *mut JSTracer) { // Do nothing. diff --git a/components/script/dom/cssimportrule.rs b/components/script/dom/cssimportrule.rs new file mode 100644 index 00000000000..582a6f597f9 --- /dev/null +++ b/components/script/dom/cssimportrule.rs @@ -0,0 +1,53 @@ +/* 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 dom::bindings::codegen::Bindings::CSSImportRuleBinding; +use dom::bindings::js::Root; +use dom::bindings::reflector::reflect_dom_object; +use dom::bindings::str::DOMString; +use dom::cssrule::{CSSRule, SpecificCSSRule}; +use dom::cssstylesheet::CSSStyleSheet; +use dom::window::Window; +use parking_lot::RwLock; +use std::sync::Arc; +use style::stylesheets::ImportRule; +use style_traits::ToCss; + +#[dom_struct] +pub struct CSSImportRule { + cssrule: CSSRule, + #[ignore_heap_size_of = "Arc"] + import_rule: Arc>, +} + +impl CSSImportRule { + fn new_inherited(parent_stylesheet: &CSSStyleSheet, + import_rule: Arc>) + -> Self { + CSSImportRule { + cssrule: CSSRule::new_inherited(parent_stylesheet), + import_rule: import_rule, + } + } + + #[allow(unrooted_must_root)] + pub fn new(window: &Window, + parent_stylesheet: &CSSStyleSheet, + import_rule: Arc>) -> Root { + reflect_dom_object(box Self::new_inherited(parent_stylesheet, import_rule), + window, + CSSImportRuleBinding::Wrap) + } +} + +impl SpecificCSSRule for CSSImportRule { + fn ty(&self) -> u16 { + use dom::bindings::codegen::Bindings::CSSRuleBinding::CSSRuleConstants; + CSSRuleConstants::IMPORT_RULE + } + + fn get_css(&self) -> DOMString { + self.import_rule.read().to_css_string().into() + } +} diff --git a/components/script/dom/cssrule.rs b/components/script/dom/cssrule.rs index 603354c8621..27b439f9dc4 100644 --- a/components/script/dom/cssrule.rs +++ b/components/script/dom/cssrule.rs @@ -9,6 +9,7 @@ use dom::bindings::js::{JS, Root}; use dom::bindings::reflector::{Reflector, reflect_dom_object}; use dom::bindings::str::DOMString; use dom::cssfontfacerule::CSSFontFaceRule; +use dom::cssimportrule::CSSImportRule; use dom::csskeyframerule::CSSKeyframeRule; use dom::csskeyframesrule::CSSKeyframesRule; use dom::cssmediarule::CSSMediaRule; @@ -64,6 +65,8 @@ impl CSSRule { rule as &SpecificCSSRule } else if let Some(rule) = self.downcast::() { rule as &SpecificCSSRule + } else if let Some(rule) = self.downcast::() { + rule as &SpecificCSSRule } else { unreachable!() } @@ -75,6 +78,7 @@ impl CSSRule { rule: StyleCssRule) -> Root { // be sure to update the match in as_specific when this is updated match rule { + StyleCssRule::Import(s) => Root::upcast(CSSImportRule::new(window, parent_stylesheet, s)), StyleCssRule::Style(s) => Root::upcast(CSSStyleRule::new(window, parent_stylesheet, s)), StyleCssRule::FontFace(s) => Root::upcast(CSSFontFaceRule::new(window, parent_stylesheet, s)), StyleCssRule::Keyframes(s) => Root::upcast(CSSKeyframesRule::new(window, parent_stylesheet, s)), diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 03847518408..b432b5a12e5 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -244,6 +244,7 @@ pub mod crypto; pub mod css; pub mod cssfontfacerule; pub mod cssgroupingrule; +pub mod cssimportrule; pub mod csskeyframerule; pub mod csskeyframesrule; pub mod cssmediarule; diff --git a/components/script/dom/webidls/CSSImportRule.webidl b/components/script/dom/webidls/CSSImportRule.webidl new file mode 100644 index 00000000000..b8131a7bb87 --- /dev/null +++ b/components/script/dom/webidls/CSSImportRule.webidl @@ -0,0 +1,11 @@ +/* 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/. */ + +// https://drafts.csswg.org/cssom/#cssimportrule +[Exposed=Window] +interface CSSImportRule : CSSRule { + // readonly attribute DOMString href; + // [SameObject, PutForwards=mediaText] readonly attribute MediaList media; + // [SameObject] readonly attribute CSSStyleSheet styleSheet; +}; diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.html b/tests/wpt/mozilla/tests/mozilla/interfaces.html index a658a8eb346..85e6335aafb 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.html +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.html @@ -22,6 +22,7 @@ test_interfaces([ "CSS", "CSSFontFaceRule", "CSSGroupingRule", + "CSSImportRule", "CSSKeyframeRule", "CSSKeyframesRule", "CSSMediaRule", From 21bf91c38676900fa2f1ef5a67ac05507466abfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Fri, 16 Dec 2016 12:16:41 +0100 Subject: [PATCH 05/10] script: Add infrastructure to track subresource loads in and + + +