diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index 58f81a71a593..e41787c19131 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -115,6 +115,7 @@ use style::logical_geometry::LogicalPoint; use style::media_queries::{Device, MediaType}; use style::parser::ParserContextExtraData; use style::servo::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, REPOSITION, STORE_OVERFLOW}; +use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard}; use style::stylesheets::{Origin, Stylesheet, UserAgentStylesheets}; use style::stylist::Stylist; use style::thread_state; @@ -344,13 +345,14 @@ impl<'a, 'b: 'a> RwData<'a, 'b> { } fn add_font_face_rules(stylesheet: &Stylesheet, + guard: &SharedRwLockReadGuard, device: &Device, font_cache_thread: &FontCacheThread, font_cache_sender: &IpcSender<()>, outstanding_web_fonts_counter: &Arc) { if opts::get().load_webfonts_synchronously { let (sender, receiver) = ipc::channel().unwrap(); - stylesheet.effective_font_face_rules(&device, |font_face| { + stylesheet.effective_font_face_rules(&device, guard, |font_face| { let effective_sources = font_face.effective_sources(); font_cache_thread.add_web_font(font_face.family.clone(), effective_sources, @@ -358,7 +360,7 @@ fn add_font_face_rules(stylesheet: &Stylesheet, receiver.recv().unwrap(); }) } else { - stylesheet.effective_font_face_rules(&device, |font_face| { + stylesheet.effective_font_face_rules(&device, guard, |font_face| { let effective_sources = font_face.effective_sources(); outstanding_web_fonts_counter.fetch_add(1, Ordering::SeqCst); font_cache_thread.add_web_font(font_face.family.clone(), @@ -406,8 +408,11 @@ impl LayoutThread { let stylist = Arc::new(Stylist::new(device)); let outstanding_web_fonts_counter = Arc::new(AtomicUsize::new(0)); - for stylesheet in &*UA_STYLESHEETS.user_or_user_agent_stylesheets { + let ua_stylesheets = &*UA_STYLESHEETS; + let guard = ua_stylesheets.shared_lock.read(); + for stylesheet in &ua_stylesheets.user_or_user_agent_stylesheets { add_font_face_rules(stylesheet, + &guard, &stylist.device, &font_cache_thread, &ipc_font_cache_sender, @@ -733,8 +738,10 @@ impl LayoutThread { // GWTODO: Need to handle unloading web fonts. let rw_data = possibly_locked_rw_data.lock(); - if stylesheet.is_effective_for_device(&rw_data.stylist.device) { + let guard = stylesheet.shared_lock.read(); + if stylesheet.is_effective_for_device(&rw_data.stylist.device, &guard) { add_font_face_rules(&*stylesheet, + &guard, &rw_data.stylist.device, &self.font_cache_thread, &self.font_cache_sender, @@ -937,6 +944,7 @@ impl LayoutThread { possibly_locked_rw_data: &mut RwData<'a, 'b>) { let document = unsafe { ServoLayoutNode::new(&data.document) }; let document = document.as_document().unwrap(); + let style_guard = document.style_shared_lock().read(); self.quirks_mode = Some(document.quirks_mode()); // FIXME(pcwalton): Combine `ReflowGoal` and `ReflowQueryType`. Then remove this assert. @@ -1013,7 +1021,8 @@ impl LayoutThread { // Calculate the actual viewport as per DEVICE-ADAPT § 6 let device = Device::new(MediaType::Screen, initial_viewport); - Arc::get_mut(&mut rw_data.stylist).unwrap().set_device(device, &data.document_stylesheets); + Arc::get_mut(&mut rw_data.stylist).unwrap() + .set_device(device, &style_guard, &data.document_stylesheets); self.viewport_size = rw_data.stylist.viewport_constraints().map_or(current_screen_size, |constraints| { @@ -1057,9 +1066,11 @@ impl LayoutThread { } // If the entire flow tree is invalid, then it will be reflowed anyhow. - let needs_dirtying = Arc::get_mut(&mut rw_data.stylist).unwrap().update(&data.document_stylesheets, - Some(&*UA_STYLESHEETS), - data.stylesheets_changed); + let needs_dirtying = Arc::get_mut(&mut rw_data.stylist).unwrap().update( + &data.document_stylesheets, + &style_guard, + Some(&*UA_STYLESHEETS), + data.stylesheets_changed); let needs_reflow = viewport_size_changed && !needs_dirtying; if needs_dirtying { if let Some(mut d) = element.mutate_data() { @@ -1553,7 +1564,8 @@ fn get_root_flow_background_color(flow: &mut Flow) -> webrender_traits::ColorF { } fn get_ua_stylesheets() -> Result { - fn parse_ua_stylesheet(filename: &'static str) -> Result { + fn parse_ua_stylesheet(shared_lock: &SharedRwLock, filename: &'static str) + -> Result { let res = try!(read_resource_file(filename).map_err(|_| filename)); Ok(Stylesheet::from_bytes( &res, @@ -1562,26 +1574,29 @@ fn get_ua_stylesheets() -> Result { None, Origin::UserAgent, Default::default(), + shared_lock.clone(), None, &StdoutErrorReporter, ParserContextExtraData::default())) } + let shared_lock = SharedRwLock::new(); let mut user_or_user_agent_stylesheets = vec!(); // FIXME: presentational-hints.css should be at author origin with zero specificity. // (Does it make a difference?) for &filename in &["user-agent.css", "servo.css", "presentational-hints.css"] { - user_or_user_agent_stylesheets.push(try!(parse_ua_stylesheet(filename))); + user_or_user_agent_stylesheets.push(try!(parse_ua_stylesheet(&shared_lock, filename))); } 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(), - None, &StdoutErrorReporter, ParserContextExtraData::default())); + shared_lock.clone(), None, &StdoutErrorReporter, ParserContextExtraData::default())); } - let quirks_mode_stylesheet = try!(parse_ua_stylesheet("quirks-mode.css")); + let quirks_mode_stylesheet = try!(parse_ua_stylesheet(&shared_lock, "quirks-mode.css")); Ok(UserAgentStylesheets { + shared_lock: shared_lock, user_or_user_agent_stylesheets: user_or_user_agent_stylesheets, quirks_mode_stylesheet: quirks_mode_stylesheet, }) diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 4d38b39d333f..b0fabb5fd459 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -99,6 +99,7 @@ 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::stylesheets::{CssRules, KeyframesRule, MediaRule, NamespaceRule, StyleRule, ImportRule}; use style::stylesheets::SupportsRule; use style::values::specified::Length; @@ -360,6 +361,7 @@ unsafe_no_jsmanaged_fields!(HttpsState); unsafe_no_jsmanaged_fields!(Request); unsafe_no_jsmanaged_fields!(RequestInit); unsafe_no_jsmanaged_fields!(SharedRt); +unsafe_no_jsmanaged_fields!(StyleSharedRwLock); unsafe_no_jsmanaged_fields!(TouchpadPressurePhase); unsafe_no_jsmanaged_fields!(USVString); unsafe_no_jsmanaged_fields!(ReferrerPolicy); @@ -572,7 +574,7 @@ unsafe impl JSTraceable for RwLock { } } -unsafe impl JSTraceable for RwLock { +unsafe impl JSTraceable for StyleLocked { unsafe fn trace(&self, _trc: *mut JSTracer) { // Do nothing. } diff --git a/components/script/dom/cssconditionrule.rs b/components/script/dom/cssconditionrule.rs index 02c52bcf28f4..44d10c21dd36 100644 --- a/components/script/dom/cssconditionrule.rs +++ b/components/script/dom/cssconditionrule.rs @@ -12,6 +12,7 @@ use dom::csssupportsrule::CSSSupportsRule; use dom_struct::dom_struct; use parking_lot::RwLock; use std::sync::Arc; +use style::shared_lock::SharedRwLock; use style::stylesheets::CssRules as StyleCssRules; #[dom_struct] @@ -27,6 +28,13 @@ impl CSSConditionRule { } } + pub fn parent_stylesheet(&self) -> &CSSStyleSheet { + self.cssgroupingrule.parent_stylesheet() + } + + pub fn shared_lock(&self) -> &SharedRwLock { + self.cssgroupingrule.shared_lock() + } } impl CSSConditionRuleMethods for CSSConditionRule { diff --git a/components/script/dom/cssfontfacerule.rs b/components/script/dom/cssfontfacerule.rs index b9ef949c1b7d..05b665a99758 100644 --- a/components/script/dom/cssfontfacerule.rs +++ b/components/script/dom/cssfontfacerule.rs @@ -13,7 +13,7 @@ use dom_struct::dom_struct; use parking_lot::RwLock; use std::sync::Arc; use style::font_face::FontFaceRule; -use style_traits::ToCss; +use style::shared_lock::ToCssWithGuard; #[dom_struct] pub struct CSSFontFaceRule { @@ -47,6 +47,7 @@ impl SpecificCSSRule for CSSFontFaceRule { } fn get_css(&self) -> DOMString { - self.fontfacerule.read().to_css_string().into() + let guard = self.cssrule.shared_lock().read(); + self.fontfacerule.read().to_css_string(&guard).into() } } diff --git a/components/script/dom/cssgroupingrule.rs b/components/script/dom/cssgroupingrule.rs index ec608b2bc771..7204b8447f9a 100644 --- a/components/script/dom/cssgroupingrule.rs +++ b/components/script/dom/cssgroupingrule.rs @@ -14,6 +14,7 @@ use dom::cssstylesheet::CSSStyleSheet; use dom_struct::dom_struct; use parking_lot::RwLock; use std::sync::Arc; +use style::shared_lock::SharedRwLock; use style::stylesheets::CssRules as StyleCssRules; #[dom_struct] @@ -40,6 +41,14 @@ impl CSSGroupingRule { parent_stylesheet, RulesSource::Rules(self.rules.clone()))) } + + pub fn parent_stylesheet(&self) -> &CSSStyleSheet { + self.cssrule.parent_stylesheet() + } + + pub fn shared_lock(&self) -> &SharedRwLock { + self.cssrule.shared_lock() + } } impl CSSGroupingRuleMethods for CSSGroupingRule { diff --git a/components/script/dom/cssimportrule.rs b/components/script/dom/cssimportrule.rs index 6ef354ec43ec..bb01fdfcc84b 100644 --- a/components/script/dom/cssimportrule.rs +++ b/components/script/dom/cssimportrule.rs @@ -12,8 +12,8 @@ use dom::window::Window; use dom_struct::dom_struct; use parking_lot::RwLock; use std::sync::Arc; +use style::shared_lock::ToCssWithGuard; use style::stylesheets::ImportRule; -use style_traits::ToCss; #[dom_struct] pub struct CSSImportRule { @@ -49,6 +49,7 @@ impl SpecificCSSRule for CSSImportRule { } fn get_css(&self) -> DOMString { - self.import_rule.read().to_css_string().into() + let guard = self.cssrule.shared_lock().read(); + self.import_rule.read().to_css_string(&guard).into() } } diff --git a/components/script/dom/csskeyframesrule.rs b/components/script/dom/csskeyframesrule.rs index db0240bf9a29..330b1be36279 100644 --- a/components/script/dom/csskeyframesrule.rs +++ b/components/script/dom/csskeyframesrule.rs @@ -21,8 +21,8 @@ use servo_atoms::Atom; use std::sync::Arc; use style::keyframes::{Keyframe, KeyframeSelector}; use style::parser::ParserContextExtraData; +use style::shared_lock::ToCssWithGuard; use style::stylesheets::KeyframesRule; -use style_traits::ToCss; #[dom_struct] pub struct CSSKeyframesRule { @@ -134,7 +134,8 @@ impl SpecificCSSRule for CSSKeyframesRule { } fn get_css(&self) -> DOMString { - self.keyframesrule.read().to_css_string().into() + let guard = self.cssrule.shared_lock().read(); + self.keyframesrule.read().to_css_string(&guard).into() } fn deparent_children(&self) { diff --git a/components/script/dom/cssmediarule.rs b/components/script/dom/cssmediarule.rs index 31923cb29dd8..1d1f0846a510 100644 --- a/components/script/dom/cssmediarule.rs +++ b/components/script/dom/cssmediarule.rs @@ -17,12 +17,13 @@ use dom_struct::dom_struct; use parking_lot::RwLock; use std::sync::Arc; use style::media_queries::parse_media_query_list; +use style::shared_lock::ToCssWithGuard; use style::stylesheets::MediaRule; use style_traits::ToCss; #[dom_struct] pub struct CSSMediaRule { - cssrule: CSSConditionRule, + cssconditionrule: CSSConditionRule, #[ignore_heap_size_of = "Arc"] mediarule: Arc>, medialist: MutNullableJS, @@ -33,7 +34,7 @@ impl CSSMediaRule { -> CSSMediaRule { let list = mediarule.read().rules.clone(); CSSMediaRule { - cssrule: CSSConditionRule::new_inherited(parent_stylesheet, list), + cssconditionrule: CSSConditionRule::new_inherited(parent_stylesheet, list), mediarule: mediarule, medialist: MutNullableJS::new(None), } @@ -49,22 +50,25 @@ impl CSSMediaRule { fn medialist(&self) -> Root { self.medialist.or_init(|| MediaList::new(self.global().as_window(), + self.cssconditionrule.parent_stylesheet(), self.mediarule.read().media_queries.clone())) } /// https://drafts.csswg.org/css-conditional-3/#the-cssmediarule-interface pub fn get_condition_text(&self) -> DOMString { + let guard = self.cssconditionrule.shared_lock().read(); let rule = self.mediarule.read(); - let list = rule.media_queries.read(); + let list = rule.media_queries.read_with(&guard); list.to_css_string().into() } /// https://drafts.csswg.org/css-conditional-3/#the-cssmediarule-interface pub fn set_condition_text(&self, text: DOMString) { + let mut guard = self.cssconditionrule.shared_lock().write(); let mut input = Parser::new(&text); let new_medialist = parse_media_query_list(&mut input); let rule = self.mediarule.read(); - let mut list = rule.media_queries.write(); + let mut list = rule.media_queries.write_with(&mut guard); *list = new_medialist; } } @@ -76,7 +80,8 @@ impl SpecificCSSRule for CSSMediaRule { } fn get_css(&self) -> DOMString { - self.mediarule.read().to_css_string().into() + let guard = self.cssconditionrule.shared_lock().read(); + self.mediarule.read().to_css_string(&guard).into() } } diff --git a/components/script/dom/cssnamespacerule.rs b/components/script/dom/cssnamespacerule.rs index 51f60fafd08a..46702c275bad 100644 --- a/components/script/dom/cssnamespacerule.rs +++ b/components/script/dom/cssnamespacerule.rs @@ -13,8 +13,8 @@ use dom::window::Window; use dom_struct::dom_struct; use parking_lot::RwLock; use std::sync::Arc; +use style::shared_lock::ToCssWithGuard; use style::stylesheets::NamespaceRule; -use style_traits::ToCss; #[dom_struct] pub struct CSSNamespaceRule { @@ -62,6 +62,7 @@ impl SpecificCSSRule for CSSNamespaceRule { } fn get_css(&self) -> DOMString { - self.namespacerule.read().to_css_string().into() + let guard = self.cssrule.shared_lock().read(); + self.namespacerule.read().to_css_string(&guard).into() } } diff --git a/components/script/dom/cssrule.rs b/components/script/dom/cssrule.rs index 277d7323a211..54bb8c175a51 100644 --- a/components/script/dom/cssrule.rs +++ b/components/script/dom/cssrule.rs @@ -20,6 +20,7 @@ use dom::cssviewportrule::CSSViewportRule; use dom::window::Window; use dom_struct::dom_struct; use std::cell::Cell; +use style::shared_lock::SharedRwLock; use style::stylesheets::CssRule as StyleCssRule; @@ -103,6 +104,10 @@ impl CSSRule { pub fn parent_stylesheet(&self) -> &CSSStyleSheet { &self.parent_stylesheet } + + pub fn shared_lock(&self) -> &SharedRwLock { + &self.parent_stylesheet.style_stylesheet().shared_lock + } } impl CSSRuleMethods for CSSRule { diff --git a/components/script/dom/cssstylerule.rs b/components/script/dom/cssstylerule.rs index 3c4c8c815d1a..2423861e4464 100644 --- a/components/script/dom/cssstylerule.rs +++ b/components/script/dom/cssstylerule.rs @@ -14,8 +14,8 @@ use dom::window::Window; use dom_struct::dom_struct; use parking_lot::RwLock; use std::sync::Arc; +use style::shared_lock::ToCssWithGuard; use style::stylesheets::StyleRule; -use style_traits::ToCss; #[dom_struct] pub struct CSSStyleRule { @@ -51,7 +51,8 @@ impl SpecificCSSRule for CSSStyleRule { } fn get_css(&self) -> DOMString { - self.stylerule.read().to_css_string().into() + let guard = self.cssrule.shared_lock().read(); + self.stylerule.read().to_css_string(&guard).into() } } diff --git a/components/script/dom/csssupportsrule.rs b/components/script/dom/csssupportsrule.rs index 0cc113bef1c3..d5e844e52aa7 100644 --- a/components/script/dom/csssupportsrule.rs +++ b/components/script/dom/csssupportsrule.rs @@ -16,13 +16,14 @@ use dom_struct::dom_struct; use parking_lot::RwLock; use std::sync::Arc; use style::parser::ParserContext; +use style::shared_lock::ToCssWithGuard; use style::stylesheets::SupportsRule; use style::supports::SupportsCondition; use style_traits::ToCss; #[dom_struct] pub struct CSSSupportsRule { - cssrule: CSSConditionRule, + cssconditionrule: CSSConditionRule, #[ignore_heap_size_of = "Arc"] supportsrule: Arc>, } @@ -32,7 +33,7 @@ impl CSSSupportsRule { -> CSSSupportsRule { let list = supportsrule.read().rules.clone(); CSSSupportsRule { - cssrule: CSSConditionRule::new_inherited(parent_stylesheet, list), + cssconditionrule: CSSConditionRule::new_inherited(parent_stylesheet, list), supportsrule: supportsrule, } } @@ -75,6 +76,7 @@ impl SpecificCSSRule for CSSSupportsRule { } fn get_css(&self) -> DOMString { - self.supportsrule.read().to_css_string().into() + let guard = self.cssconditionrule.shared_lock().read(); + self.supportsrule.read().to_css_string(&guard).into() } } diff --git a/components/script/dom/cssviewportrule.rs b/components/script/dom/cssviewportrule.rs index c784a26f4428..855c8bdda576 100644 --- a/components/script/dom/cssviewportrule.rs +++ b/components/script/dom/cssviewportrule.rs @@ -12,8 +12,8 @@ use dom::window::Window; use dom_struct::dom_struct; use parking_lot::RwLock; use std::sync::Arc; +use style::shared_lock::ToCssWithGuard; use style::viewport::ViewportRule; -use style_traits::ToCss; #[dom_struct] pub struct CSSViewportRule { @@ -46,6 +46,7 @@ impl SpecificCSSRule for CSSViewportRule { } fn get_css(&self) -> DOMString { - self.viewportrule.read().to_css_string().into() + let guard = self.cssrule.shared_lock().read(); + self.viewportrule.read().to_css_string(&guard).into() } } diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index b85366a27d2b..9a00fbca8999 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -134,6 +134,7 @@ use style::attr::AttrValue; use style::context::{QuirksMode, ReflowGoal}; use style::restyle_hints::{RestyleHint, RESTYLE_STYLE_ATTRIBUTE}; use style::selector_parser::{RestyleDamage, Snapshot}; +use style::shared_lock::SharedRwLock as StyleSharedRwLock; use style::str::{HTML_SPACE_CHARACTERS, split_html_space_chars, str_join}; use style::stylesheets::Stylesheet; use task_source::TaskSource; @@ -220,6 +221,7 @@ pub struct Document { scripts: MutNullableJS, anchors: MutNullableJS, applets: MutNullableJS, + style_shared_lock: StyleSharedRwLock, /// List of stylesheets associated with nodes in this document. |None| if the list needs to be refreshed. stylesheets: DOMRefCell>>, /// Whether the list of stylesheets has changed since the last reflow was triggered. @@ -1964,6 +1966,7 @@ pub trait LayoutDocumentHelpers { unsafe fn needs_paint_from_layout(&self); unsafe fn will_paint(&self); unsafe fn quirks_mode(&self) -> QuirksMode; + unsafe fn style_shared_lock(&self) -> &StyleSharedRwLock; } #[allow(unsafe_code)] @@ -2000,6 +2003,11 @@ impl LayoutDocumentHelpers for LayoutJS { unsafe fn quirks_mode(&self) -> QuirksMode { (*self.unsafe_get()).quirks_mode() } + + #[inline] + unsafe fn style_shared_lock(&self) -> &StyleSharedRwLock { + (*self.unsafe_get()).style_shared_lock() + } } // https://html.spec.whatwg.org/multipage/#is-a-registrable-domain-suffix-of-or-is-equal-to @@ -2121,6 +2129,7 @@ impl Document { scripts: Default::default(), anchors: Default::default(), applets: Default::default(), + style_shared_lock: StyleSharedRwLock::new(), stylesheets: DOMRefCell::new(None), stylesheets_changed_since_reflow: Cell::new(false), stylesheet_list: MutNullableJS::new(None), @@ -2250,6 +2259,11 @@ impl Document { }; } + /// Return a reference to the per-document shared lock used in stylesheets. + pub fn style_shared_lock(&self) -> &StyleSharedRwLock { + &self.style_shared_lock + } + /// Returns the list of stylesheets associated with nodes in the document. pub fn stylesheets(&self) -> Vec> { self.ensure_stylesheets(); diff --git a/components/script/dom/htmlmetaelement.rs b/components/script/dom/htmlmetaelement.rs index a751486de7d1..2f78e74d3b40 100644 --- a/components/script/dom/htmlmetaelement.rs +++ b/components/script/dom/htmlmetaelement.rs @@ -99,12 +99,15 @@ impl HTMLMetaElement { let content = content.value(); if !content.is_empty() { if let Some(translated_rule) = ViewportRule::from_meta(&**content) { + let document = self.upcast::().owner_doc(); + let shared_lock = document.style_shared_lock(); *self.stylesheet.borrow_mut() = Some(Arc::new(Stylesheet { rules: CssRules::new(vec![CssRule::Viewport(Arc::new(RwLock::new(translated_rule)))]), origin: Origin::Author, + shared_lock: shared_lock.clone(), base_url: window_from_node(self).get_url(), namespaces: Default::default(), - media: Default::default(), + media: Arc::new(shared_lock.wrap(Default::default())), // Viewport constraints are always recomputed on resize; they don't need to // force all styles to be recomputed. dirty_on_viewport_size_change: AtomicBool::new(false), diff --git a/components/script/dom/htmlstyleelement.rs b/components/script/dom/htmlstyleelement.rs index f9c6412f854b..13721feba58c 100644 --- a/components/script/dom/htmlstyleelement.rs +++ b/components/script/dom/htmlstyleelement.rs @@ -84,9 +84,10 @@ impl HTMLStyleElement { let data = node.GetTextContent().expect("Element.textContent must be a string"); let mq = parse_media_query_list(&mut CssParser::new(&mq_str)); + let shared_lock = node.owner_doc().style_shared_lock().clone(); let loader = StylesheetLoader::for_element(self.upcast()); let sheet = Stylesheet::from_str(&data, url, Origin::Author, mq, - Some(&loader), + shared_lock, Some(&loader), win.css_error_reporter(), ParserContextExtraData::default()); diff --git a/components/script/dom/medialist.rs b/components/script/dom/medialist.rs index 806946f6356c..b403200983e7 100644 --- a/components/script/dom/medialist.rs +++ b/components/script/dom/medialist.rs @@ -6,52 +6,62 @@ use core::default::Default; use cssparser::Parser; use dom::bindings::codegen::Bindings::MediaListBinding; use dom::bindings::codegen::Bindings::MediaListBinding::MediaListMethods; -use dom::bindings::js::Root; +use dom::bindings::js::{JS, Root}; use dom::bindings::reflector::{Reflector, reflect_dom_object}; use dom::bindings::str::DOMString; +use dom::cssstylesheet::CSSStyleSheet; use dom::window::Window; use dom_struct::dom_struct; -use parking_lot::RwLock; use std::sync::Arc; use style::media_queries::{MediaQuery, parse_media_query_list}; use style::media_queries::MediaList as StyleMediaList; +use style::shared_lock::{SharedRwLock, Locked}; use style_traits::ToCss; #[dom_struct] pub struct MediaList { reflector_: Reflector, + parent_stylesheet: JS, #[ignore_heap_size_of = "Arc"] - media_queries: Arc>, + media_queries: Arc>, } impl MediaList { #[allow(unrooted_must_root)] - pub fn new_inherited(media_queries: Arc>) -> MediaList { + pub fn new_inherited(parent_stylesheet: &CSSStyleSheet, + media_queries: Arc>) -> MediaList { MediaList { + parent_stylesheet: JS::from_ref(parent_stylesheet), reflector_: Reflector::new(), media_queries: media_queries, } } #[allow(unrooted_must_root)] - pub fn new(window: &Window, media_queries: Arc>) + pub fn new(window: &Window, parent_stylesheet: &CSSStyleSheet, + media_queries: Arc>) -> Root { - reflect_dom_object(box MediaList::new_inherited(media_queries), + reflect_dom_object(box MediaList::new_inherited(parent_stylesheet, media_queries), window, MediaListBinding::Wrap) } + fn shared_lock(&self) -> &SharedRwLock { + &self.parent_stylesheet.style_stylesheet().shared_lock + } } impl MediaListMethods for MediaList { // https://drafts.csswg.org/cssom/#dom-medialist-mediatext fn MediaText(&self) -> DOMString { - DOMString::from(self.media_queries.read().to_css_string()) + let guard = self.shared_lock().read(); + DOMString::from(self.media_queries.read_with(&guard).to_css_string()) } // https://drafts.csswg.org/cssom/#dom-medialist-mediatext fn SetMediaText(&self, value: DOMString) { - let mut media_queries = self.media_queries.write(); + let mut guard = self.shared_lock().write(); + let mut media_queries = self.media_queries.write_with(&mut guard); // Step 2 if value.is_empty() { // Step 1 @@ -65,13 +75,15 @@ impl MediaListMethods for MediaList { // https://drafts.csswg.org/cssom/#dom-medialist-length fn Length(&self) -> u32 { - self.media_queries.read().media_queries.len() as u32 + let guard = self.shared_lock().read(); + self.media_queries.read_with(&guard).media_queries.len() as u32 } // https://drafts.csswg.org/cssom/#dom-medialist-item fn Item(&self, index: u32) -> Option { - self.media_queries.read().media_queries.get(index as usize) - .and_then(|query| { + let guard = self.shared_lock().read(); + self.media_queries.read_with(&guard).media_queries + .get(index as usize).and_then(|query| { let mut s = String::new(); query.to_css(&mut s).unwrap(); Some(DOMString::from_string(s)) @@ -94,13 +106,14 @@ impl MediaListMethods for MediaList { } // Step 3 let m_serialized = m.clone().unwrap().to_css_string(); - let any = self.media_queries.read().media_queries.iter() - .any(|q| m_serialized == q.to_css_string()); + let mut guard = self.shared_lock().write(); + let mq = self.media_queries.write_with(&mut guard); + let any = mq.media_queries.iter().any(|q| m_serialized == q.to_css_string()); if any { return; } // Step 4 - self.media_queries.write().media_queries.push(m.unwrap()); + mq.media_queries.push(m.unwrap()); } // https://drafts.csswg.org/cssom/#dom-medialist-deletemedium @@ -114,7 +127,8 @@ impl MediaListMethods for MediaList { } // Step 3 let m_serialized = m.unwrap().to_css_string(); - let mut media_list = self.media_queries.write(); + let mut guard = self.shared_lock().write(); + let mut media_list = self.media_queries.write_with(&mut guard); let new_vec = media_list.media_queries.drain(..) .filter(|q| m_serialized != q.to_css_string()) .collect(); diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs index 98ce86745f29..5a9a834667b9 100644 --- a/components/script/layout_wrapper.rs +++ b/components/script/layout_wrapper.rs @@ -69,6 +69,7 @@ use style::dom::UnsafeNode; use style::element_state::*; use style::properties::{ComputedValues, PropertyDeclarationBlock}; use style::selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl}; +use style::shared_lock::SharedRwLock as StyleSharedRwLock; use style::sink::Push; use style::str::is_whitespace; use style::stylist::ApplicableDeclarationBlock; @@ -330,6 +331,10 @@ impl<'ld> ServoLayoutDocument<'ld> { unsafe { self.document.quirks_mode() } } + pub fn style_shared_lock(&self) -> &StyleSharedRwLock { + unsafe { self.document.style_shared_lock() } + } + pub fn from_layout_js(doc: LayoutJS) -> ServoLayoutDocument<'ld> { ServoLayoutDocument { document: doc, diff --git a/components/script/stylesheet_loader.rs b/components/script/stylesheet_loader.rs index cdc93c9808bd..26dffa5fe56d 100644 --- a/components/script/stylesheet_loader.rs +++ b/components/script/stylesheet_loader.rs @@ -152,12 +152,14 @@ impl FetchResponseListener for StylesheetContext { let is_stylesheet_load_applicable = self.request_generation_id.map_or(true, |gen| gen == link.get_request_generation_id()); if is_stylesheet_load_applicable { + let shared_lock = document.style_shared_lock().clone(); let sheet = Arc::new(Stylesheet::from_bytes(&data, final_url, protocol_encoding_label, Some(environment_encoding), Origin::Author, media.take().unwrap(), + shared_lock, Some(&loader), win.css_error_reporter(), ParserContextExtraData::default())); diff --git a/components/style/encoding_support.rs b/components/style/encoding_support.rs index 62323ec23140..141291c896e8 100644 --- a/components/style/encoding_support.rs +++ b/components/style/encoding_support.rs @@ -12,6 +12,7 @@ use media_queries::MediaList; use parser::ParserContextExtraData; use self::encoding::{EncodingRef, DecoderTrap}; use servo_url::ServoUrl; +use shared_lock::SharedRwLock; use std::str; use stylesheets::{Stylesheet, StylesheetLoader, Origin}; @@ -54,6 +55,7 @@ impl Stylesheet { environment_encoding: Option, origin: Origin, media: MediaList, + shared_lock: SharedRwLock, stylesheet_loader: Option<&StylesheetLoader>, error_reporter: &ParseErrorReporter, extra_data: ParserContextExtraData) @@ -64,6 +66,7 @@ impl Stylesheet { base_url, origin, media, + shared_lock, stylesheet_loader, error_reporter, extra_data) diff --git a/components/style/font_face.rs b/components/style/font_face.rs index 0d4bc4f8a09a..8bad3a93aee5 100644 --- a/components/style/font_face.rs +++ b/components/style/font_face.rs @@ -14,6 +14,7 @@ use computed_values::font_family::FamilyName; use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser}; #[cfg(feature = "gecko")] use cssparser::UnicodeRange; use parser::{ParserContext, log_css_error, Parse}; +use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard}; use std::fmt; use std::iter; use style_traits::{ToCss, OneOrMoreCommaSeparated}; @@ -230,11 +231,10 @@ macro_rules! font_face_descriptors { } } - impl ToCss for FontFaceRule { + impl ToCssWithGuard for FontFaceRule { // Serialization of FontFaceRule is not specced. - fn to_css(&self, dest: &mut W) -> fmt::Result - where W: fmt::Write, - { + fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result + where W: fmt::Write { dest.write_str("@font-face {\n")?; $( dest.write_str(concat!(" ", $m_name, ": "))?; diff --git a/components/style/gecko/arc_types.rs b/components/style/gecko/arc_types.rs index 43a87f6b35f8..3d74d33a36c2 100644 --- a/components/style/gecko/arc_types.rs +++ b/components/style/gecko/arc_types.rs @@ -17,6 +17,7 @@ use media_queries::MediaList; use parking_lot::RwLock; use properties::{ComputedValues, PropertyDeclarationBlock}; use properties::animated_properties::AnimationValue; +use shared_lock::Locked; use stylesheets::{CssRules, Stylesheet, StyleRule, ImportRule, MediaRule, NamespaceRule}; macro_rules! impl_arc_ffi { @@ -59,7 +60,7 @@ impl_arc_ffi!(RwLock => RawServoImportRule impl_arc_ffi!(AnimationValue => RawServoAnimationValue [Servo_AnimationValue_AddRef, Servo_AnimationValue_Release]); -impl_arc_ffi!(RwLock => RawServoMediaList +impl_arc_ffi!(Locked => RawServoMediaList [Servo_MediaList_AddRef, Servo_MediaList_Release]); impl_arc_ffi!(RwLock => RawServoMediaRule diff --git a/components/style/gecko/data.rs b/components/style/gecko/data.rs index 627b38450511..e0f7df023e2f 100644 --- a/components/style/gecko/data.rs +++ b/components/style/gecko/data.rs @@ -13,6 +13,7 @@ use gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI}; use media_queries::Device; use parking_lot::RwLock; use properties::ComputedValues; +use shared_lock::SharedRwLockReadGuard; use std::collections::HashMap; use std::sync::Arc; use std::sync::mpsc::{Receiver, Sender, channel}; @@ -83,20 +84,20 @@ impl PerDocumentStyleDataImpl { /// Reset the device state because it may have changed. /// /// Implies also a stylesheet flush. - pub fn reset_device(&mut self) { + pub fn reset_device(&mut self, guard: &SharedRwLockReadGuard) { { let mut stylist = Arc::get_mut(&mut self.stylist).unwrap(); Arc::get_mut(&mut stylist.device).unwrap().reset(); } self.stylesheets_changed = true; - self.flush_stylesheets(); + self.flush_stylesheets(guard); } /// Recreate the style data if the stylesheets have changed. - pub fn flush_stylesheets(&mut self) { + pub fn flush_stylesheets(&mut self, guard: &SharedRwLockReadGuard) { if self.stylesheets_changed { let mut stylist = Arc::get_mut(&mut self.stylist).unwrap(); - stylist.update(&self.stylesheets, None, true); + stylist.update(&self.stylesheets, guard, None, true); self.stylesheets_changed = false; } } diff --git a/components/style/lib.rs b/components/style/lib.rs index b5bc9f744403..7f79563a8b8a 100644 --- a/components/style/lib.rs +++ b/components/style/lib.rs @@ -106,6 +106,7 @@ pub mod restyle_hints; pub mod rule_tree; pub mod scoped_tls; pub mod selector_parser; +pub mod shared_lock; pub mod stylist; #[cfg(feature = "servo")] #[allow(unsafe_code)] pub mod servo; pub mod sequential; diff --git a/components/style/shared_lock.rs b/components/style/shared_lock.rs new file mode 100644 index 000000000000..9371b5866131 --- /dev/null +++ b/components/style/shared_lock.rs @@ -0,0 +1,183 @@ +/* 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/. */ + +//! Different objects protected by the same lock + +use parking_lot::RwLock; +use std::cell::UnsafeCell; +use std::fmt; +use std::ops::Deref; +use std::sync::Arc; + +/// A shared read/write lock that can protect multiple objects. +#[derive(Clone)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct SharedRwLock { + #[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")] + arc: Arc>, +} + +impl fmt::Debug for SharedRwLock { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("SharedRwLock") + } +} + +impl SharedRwLock { + /// Create a new shared lock + pub fn new() -> Self { + SharedRwLock { + arc: Arc::new(RwLock::new(())) + } + } + + /// Wrap the given data to make its access protected by this lock. + pub fn wrap(&self, data: T) -> Locked { + Locked { + shared_lock: self.clone(), + data: UnsafeCell::new(data), + } + } + + /// Obtain the lock for reading + pub fn read(&self) -> SharedRwLockReadGuard { + self.arc.raw_read(); + SharedRwLockReadGuard { + shared_lock: self + } + } + + /// Obtain the lock for writing + pub fn write(&self) -> SharedRwLockWriteGuard { + self.arc.raw_write(); + SharedRwLockWriteGuard { + shared_lock: self + } + } + + fn same_as(&self, other: &Self) -> bool { + // FIXME: Use Arc::ptr_eq once it’s stable. https://github.com/rust-lang/rust/issues/36497 + let a: *const RwLock<()> = Arc::deref(&self.arc); + let b: *const RwLock<()> = Arc::deref(&other.arc); + a == b + } +} + +/// Data protect by a shared lock. +pub struct Locked { + shared_lock: SharedRwLock, + data: UnsafeCell, +} + +// Unsafe: the data inside `UnsafeCell` is only accessed in `read_with` and `write_with`, +// where guards ensure synchronization. +unsafe impl Send for Locked {} +unsafe impl Sync for Locked {} + +impl fmt::Debug for Locked { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let guard = self.shared_lock.read(); + self.read_with(&guard).fmt(f) + } +} + +impl Locked { + /// Access the data for reading. + pub fn read_with<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a T { + assert!(self.shared_lock.same_as(&guard.shared_lock), + "StyleRef::read_with called with a guard from an unrelated SharedRwLock"); + let ptr = self.data.get(); + + // Unsafe: + // + // * The guard guarantees that the lock is taken for reading, + // and we’ve checked that it’s the correct lock. + // * The returned reference borrows *both* the data and the guard, + // so that it can outlive neither. + unsafe { + &*ptr + } + } + + /// Access the data for writing. + pub fn write_with<'a>(&'a self, guard: &'a mut SharedRwLockWriteGuard) -> &'a mut T { + assert!(self.shared_lock.same_as(&guard.shared_lock), + "StyleRef::write_with called with a guard from an unrelated SharedRwLock"); + let ptr = self.data.get(); + + // Unsafe: + // + // * The guard guarantees that the lock is taken for writing, + // and we’ve checked that it’s the correct lock. + // * The returned reference borrows *both* the data and the guard, + // so that it can outlive neither. + // * We require a mutable borrow of the guard, + // so that one write guard can only be used once at a time. + unsafe { + &mut *ptr + } + } +} + +/// Proof that a shared lock was obtained for reading. +pub struct SharedRwLockReadGuard<'a> { + shared_lock: &'a SharedRwLock, +} + +/// Proof that a shared lock was obtained for writing. +pub struct SharedRwLockWriteGuard<'a> { + shared_lock: &'a SharedRwLock, +} + +impl<'a> Drop for SharedRwLockReadGuard<'a> { + fn drop(&mut self) { + // Unsafe: self.lock is private to this module, only ever set after `raw_read()`, + // and never copied or cloned (see `compile_time_assert` below). + unsafe { + self.shared_lock.arc.raw_unlock_read() + } + } +} + +impl<'a> Drop for SharedRwLockWriteGuard<'a> { + fn drop(&mut self) { + // Unsafe: self.lock is private to this module, only ever set after `raw_write()`, + // and never copied or cloned (see `compile_time_assert` below). + unsafe { + self.shared_lock.arc.raw_unlock_write() + } + } +} + +#[allow(dead_code)] +mod compile_time_assert { + use super::{SharedRwLockReadGuard, SharedRwLockWriteGuard}; + + trait Marker1 {} + impl Marker1 for T {} + impl<'a> Marker1 for SharedRwLockReadGuard<'a> {} // Assert SharedRwLockReadGuard: !Clone + impl<'a> Marker1 for SharedRwLockWriteGuard<'a> {} // Assert SharedRwLockWriteGuard: !Clone + + trait Marker2 {} + impl Marker2 for T {} + impl<'a> Marker2 for SharedRwLockReadGuard<'a> {} // Assert SharedRwLockReadGuard: !Copy + impl<'a> Marker2 for SharedRwLockWriteGuard<'a> {} // Assert SharedRwLockWriteGuard: !Copy +} + +/// Like ToCss, but with a lock guard given by the caller. +pub trait ToCssWithGuard { + /// Serialize `self` in CSS syntax, writing to `dest`, using the given lock guard. + fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result + where W: fmt::Write; + + /// Serialize `self` in CSS syntax using the given lock guard and return a string. + /// + /// (This is a convenience wrapper for `to_css` and probably should not be overridden.) + #[inline] + fn to_css_string(&self, guard: &SharedRwLockReadGuard) -> String { + let mut s = String::new(); + self.to_css(guard, &mut s).unwrap(); + s + } +} diff --git a/components/style/stylesheets.rs b/components/style/stylesheets.rs index 5b5c47e152f7..3f3d78c1e16d 100644 --- a/components/style/stylesheets.rs +++ b/components/style/stylesheets.rs @@ -21,6 +21,7 @@ use selector_parser::{SelectorImpl, SelectorParser}; use selectors::parser::SelectorList; use servo_config::prefs::PREFS; use servo_url::ServoUrl; +use shared_lock::{SharedRwLock, Locked, SharedRwLockReadGuard, ToCssWithGuard}; use std::cell::Cell; use std::fmt; use std::sync::Arc; @@ -175,11 +176,13 @@ pub struct Stylesheet { /// cascading order) pub rules: Arc>, /// List of media associated with the Stylesheet. - pub media: Arc>, + pub media: Arc>, /// The origin of this stylesheet. pub origin: Origin, /// The base url this stylesheet should use. pub base_url: ServoUrl, + /// The lock used for objects inside this stylesheet + pub shared_lock: SharedRwLock, /// The namespaces that apply to this stylesheet. pub namespaces: RwLock, /// Whether this stylesheet would be dirty when the viewport size changes. @@ -191,6 +194,8 @@ pub struct Stylesheet { /// 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, /// The quirks mode stylesheet. @@ -291,12 +296,12 @@ impl CssRule { /// used for others. /// /// This will not recurse down unsupported @supports rules - pub fn with_nested_rules_and_mq(&self, mut f: F) -> R + pub fn with_nested_rules_and_mq(&self, guard: &SharedRwLockReadGuard, 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 media = rule.stylesheet.media.read_with(guard); let rules = rule.stylesheet.rules.read(); // FIXME(emilio): Include the nested rules if the stylesheet is // loaded. @@ -311,7 +316,7 @@ impl CssRule { } CssRule::Media(ref lock) => { let media_rule = lock.read(); - let mq = media_rule.media_queries.read(); + let mq = media_rule.media_queries.read_with(guard); let rules = &media_rule.rules.read().0; f(rules, Some(&mq)) } @@ -349,6 +354,7 @@ impl CssRule { let mut rule_parser = TopLevelRuleParser { stylesheet_origin: parent_stylesheet.origin, context: context, + shared_lock: &parent_stylesheet.shared_lock, loader: None, state: Cell::new(state), namespaces: &mut namespaces, @@ -366,18 +372,19 @@ impl CssRule { } } -impl ToCss for CssRule { +impl ToCssWithGuard for CssRule { // https://drafts.csswg.org/cssom/#serialize-a-css-rule - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + fn to_css(&self, guard: &SharedRwLockReadGuard, 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), - CssRule::Keyframes(ref lock) => lock.read().to_css(dest), - CssRule::Media(ref lock) => lock.read().to_css(dest), - CssRule::Supports(ref lock) => lock.read().to_css(dest), + CssRule::Namespace(ref lock) => lock.read().to_css(guard, dest), + CssRule::Import(ref lock) => lock.read().to_css(guard, dest), + CssRule::Style(ref lock) => lock.read().to_css(guard, dest), + CssRule::FontFace(ref lock) => lock.read().to_css(guard, dest), + CssRule::Viewport(ref lock) => lock.read().to_css(guard, dest), + CssRule::Keyframes(ref lock) => lock.read().to_css(guard, dest), + CssRule::Media(ref lock) => lock.read().to_css(guard, dest), + CssRule::Supports(ref lock) => lock.read().to_css(guard, dest), } } } @@ -390,9 +397,10 @@ pub struct NamespaceRule { pub url: Namespace, } -impl ToCss for NamespaceRule { +impl ToCssWithGuard for NamespaceRule { // https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSNamespaceRule - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result + where W: fmt::Write { try!(dest.write_str("@namespace ")); if let Some(ref prefix) = self.prefix { try!(dest.write_str(&*prefix.to_string())); @@ -420,11 +428,12 @@ pub struct ImportRule { pub stylesheet: Arc, } -impl ToCss for ImportRule { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { +impl ToCssWithGuard for ImportRule { + fn to_css(&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(); + let media = self.stylesheet.media.read_with(guard); if !media.is_empty() { try!(dest.write_str(" ")); try!(media.to_css(dest)); @@ -444,9 +453,10 @@ pub struct KeyframesRule { pub keyframes: Vec>>, } -impl ToCss for KeyframesRule { +impl ToCssWithGuard for KeyframesRule { // Serialization of KeyframesRule is not specced. - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result + where W: fmt::Write { try!(dest.write_str("@keyframes ")); try!(dest.write_str(&*self.name.to_string())); try!(dest.write_str(" { ")); @@ -467,20 +477,21 @@ impl ToCss for KeyframesRule { #[allow(missing_docs)] #[derive(Debug)] pub struct MediaRule { - pub media_queries: Arc>, + pub media_queries: Arc>, pub rules: Arc>, } -impl ToCss for MediaRule { +impl ToCssWithGuard for MediaRule { // Serialization of MediaRule is not specced. // https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSMediaRule - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result + where W: fmt::Write { try!(dest.write_str("@media ")); - try!(self.media_queries.read().to_css(dest)); + try!(self.media_queries.read_with(guard).to_css(dest)); try!(dest.write_str(" {")); for rule in self.rules.read().0.iter() { try!(dest.write_str(" ")); - try!(rule.to_css(dest)); + try!(rule.to_css(guard, dest)); } dest.write_str(" }") } @@ -498,14 +509,15 @@ pub struct SupportsRule { pub enabled: bool, } -impl ToCss for SupportsRule { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { +impl ToCssWithGuard for SupportsRule { + fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result + where W: fmt::Write { try!(dest.write_str("@supports ")); try!(self.condition.to_css(dest)); try!(dest.write_str(" {")); for rule in self.rules.read().0.iter() { try!(dest.write_str(" ")); - try!(rule.to_css(dest)); + try!(rule.to_css(guard, dest)); } dest.write_str(" }") } @@ -518,9 +530,10 @@ pub struct StyleRule { pub block: Arc>, } -impl ToCss for StyleRule { +impl ToCssWithGuard for StyleRule { // https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSStyleRule - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result + where W: fmt::Write { // Step 1 try!(self.selectors.to_css(dest)); // Step 2 @@ -554,6 +567,7 @@ impl Stylesheet { let rule_parser = TopLevelRuleParser { stylesheet_origin: existing.origin, namespaces: &mut namespaces, + shared_lock: &existing.shared_lock, loader: stylesheet_loader, context: ParserContext::new_with_extra_data(existing.origin, &existing.base_url, @@ -591,6 +605,7 @@ impl Stylesheet { base_url: ServoUrl, origin: Origin, media: MediaList, + shared_lock: SharedRwLock, stylesheet_loader: Option<&StylesheetLoader>, error_reporter: &ParseErrorReporter, extra_data: ParserContextExtraData) -> Stylesheet { @@ -599,7 +614,8 @@ impl Stylesheet { base_url: base_url, namespaces: RwLock::new(Namespaces::default()), rules: CssRules::new(vec![]), - media: Arc::new(RwLock::new(media)), + media: Arc::new(shared_lock.wrap(media)), + shared_lock: shared_lock.clone(), dirty_on_viewport_size_change: AtomicBool::new(false), disabled: AtomicBool::new(false), }; @@ -637,8 +653,8 @@ impl Stylesheet { /// on the associated MediaList. /// /// Always true if no associated MediaList exists. - pub fn is_effective_for_device(&self, device: &Device) -> bool { - self.media.read().evaluate(device) + pub fn is_effective_for_device(&self, device: &Device, guard: &SharedRwLockReadGuard) -> bool { + self.media.read_with(guard).evaluate(device) } /// Return an iterator over the effective rules within the style-sheet, as @@ -648,8 +664,9 @@ impl Stylesheet { /// nested rules will be skipped. Use `rules` if all rules need to be /// examined. #[inline] - pub fn effective_rules(&self, device: &Device, mut f: F) where F: FnMut(&CssRule) { - effective_rules(&self.rules.read().0, device, &mut f); + pub fn effective_rules(&self, device: &Device, guard: &SharedRwLockReadGuard, mut f: F) + where F: FnMut(&CssRule) { + effective_rules(&self.rules.read().0, device, guard, &mut f); } /// Returns whether the stylesheet has been explicitly disabled through the @@ -670,16 +687,17 @@ impl Stylesheet { } } -fn effective_rules(rules: &[CssRule], device: &Device, f: &mut F) where F: FnMut(&CssRule) { +fn effective_rules(rules: &[CssRule], device: &Device, guard: &SharedRwLockReadGuard, f: &mut F) +where F: FnMut(&CssRule) { for rule in rules { f(rule); - rule.with_nested_rules_and_mq(|rules, mq| { + rule.with_nested_rules_and_mq(guard, |rules, mq| { if let Some(media_queries) = mq { if !media_queries.evaluate(device) { return } } - effective_rules(rules, device, f) + effective_rules(rules, device, guard, f) }) } } @@ -689,8 +707,9 @@ macro_rules! rule_filter { impl Stylesheet { $( #[allow(missing_docs)] - pub fn $method(&self, device: &Device, mut f: F) where F: FnMut(&$rule_type) { - self.effective_rules(device, |rule| { + pub fn $method(&self, device: &Device, guard: &SharedRwLockReadGuard, mut f: F) + where F: FnMut(&$rule_type) { + self.effective_rules(device, guard, |rule| { if let CssRule::$variant(ref lock) = *rule { let rule = lock.read(); f(&rule) @@ -724,6 +743,7 @@ pub trait StylesheetLoader { struct TopLevelRuleParser<'a> { stylesheet_origin: Origin, namespaces: &'a mut Namespaces, + shared_lock: &'a SharedRwLock, loader: Option<&'a StylesheetLoader>, context: ParserContext<'a>, state: Cell, @@ -733,6 +753,7 @@ 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, namespaces: self.namespaces, } @@ -754,7 +775,7 @@ enum AtRulePrelude { /// A @font-face rule prelude. FontFace, /// A @media rule prelude, with its media queries. - Media(Arc>), + Media(Arc>), /// An @supports rule, with its conditional Supports(SupportsCondition), /// A @viewport rule prelude. @@ -780,7 +801,7 @@ impl<'a> AtRuleParser for TopLevelRuleParser<'a> { &self.context)); let media = - Arc::new(RwLock::new(parse_media_query_list(input))); + Arc::new(self.shared_lock.wrap(parse_media_query_list(input))); let is_valid_url = url.url().is_some(); @@ -790,6 +811,7 @@ impl<'a> AtRuleParser for TopLevelRuleParser<'a> { stylesheet: Arc::new(Stylesheet { rules: Arc::new(RwLock::new(CssRules(vec![]))), media: media, + shared_lock: self.shared_lock.clone(), origin: self.context.stylesheet_origin, base_url: self.context.base_url.clone(), namespaces: RwLock::new(Namespaces::default()), @@ -879,6 +901,7 @@ impl<'a> QualifiedRuleParser for TopLevelRuleParser<'a> { #[derive(Clone)] // shallow, relatively cheap .clone struct NestedRuleParser<'a, 'b: 'a> { stylesheet_origin: Origin, + shared_lock: &'a SharedRwLock, context: &'a ParserContext<'b>, namespaces: &'b Namespaces, } @@ -910,7 +933,8 @@ impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> { match_ignore_ascii_case! { name, "media" => { let media_queries = parse_media_query_list(input); - Ok(AtRuleType::WithBlock(AtRulePrelude::Media(Arc::new(RwLock::new(media_queries))))) + let arc = Arc::new(self.shared_lock.wrap(media_queries)); + Ok(AtRuleType::WithBlock(AtRulePrelude::Media(arc))) }, "supports" => { let cond = SupportsCondition::parse(input)?; diff --git a/components/style/stylist.rs b/components/style/stylist.rs index f7dd149e7d33..0ff9d20d883e 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -28,6 +28,7 @@ use selectors::matching::{AFFECTED_BY_STYLE_ATTRIBUTE, AFFECTED_BY_PRESENTATIONA use selectors::matching::{ElementSelectorFlags, StyleRelations, matches_complex_selector}; use selectors::parser::{Selector, SimpleSelector, LocalName as LocalNameSelector, ComplexSelector}; use selectors::parser::SelectorMethods; +use shared_lock::SharedRwLockReadGuard; use sink::Push; use smallvec::VecLike; use std::borrow::Borrow; @@ -158,6 +159,7 @@ impl Stylist { /// device is dirty, which means we need to re-evaluate media queries. pub fn update(&mut self, doc_stylesheets: &[Arc], + doc_guard: &SharedRwLockReadGuard, ua_stylesheets: Option<&UserAgentStylesheets>, stylesheets_changed: bool) -> bool { if !(self.is_device_dirty || stylesheets_changed) { @@ -165,7 +167,9 @@ impl Stylist { } let cascaded_rule = ViewportRule { - declarations: viewport::Cascade::from_stylesheets(doc_stylesheets, &self.device).finish(), + declarations: viewport::Cascade::from_stylesheets( + doc_stylesheets, doc_guard, &self.device + ).finish(), }; self.viewport_constraints = @@ -192,17 +196,18 @@ impl Stylist { self.non_common_style_affecting_attributes_selectors.clear(); if let Some(ua_stylesheets) = ua_stylesheets { + let ua_guard = ua_stylesheets.shared_lock.read(); for stylesheet in &ua_stylesheets.user_or_user_agent_stylesheets { - self.add_stylesheet(&stylesheet); + self.add_stylesheet(&stylesheet, &ua_guard); } if self.quirks_mode { - self.add_stylesheet(&ua_stylesheets.quirks_mode_stylesheet); + self.add_stylesheet(&ua_stylesheets.quirks_mode_stylesheet, &ua_guard); } } for ref stylesheet in doc_stylesheets.iter() { - self.add_stylesheet(stylesheet); + self.add_stylesheet(stylesheet, doc_guard); } debug!("Stylist stats:"); @@ -226,15 +231,15 @@ impl Stylist { true } - fn add_stylesheet(&mut self, stylesheet: &Stylesheet) { - if stylesheet.disabled() || !stylesheet.is_effective_for_device(&self.device) { + fn add_stylesheet(&mut self, stylesheet: &Stylesheet, guard: &SharedRwLockReadGuard) { + if stylesheet.disabled() || !stylesheet.is_effective_for_device(&self.device, guard) { return; } // Cheap `Arc` clone so that the closure below can borrow `&mut Stylist`. let device = self.device.clone(); - stylesheet.effective_rules(&device, |rule| { + stylesheet.effective_rules(&device, guard, |rule| { match *rule { CssRule::Style(ref style_rule) => { let guard = style_rule.read(); @@ -270,7 +275,7 @@ impl Stylist { } CssRule::Import(ref import) => { let import = import.read(); - self.add_stylesheet(&import.stylesheet) + self.add_stylesheet(&import.stylesheet, guard) } CssRule::Keyframes(ref keyframes_rule) => { let keyframes_rule = keyframes_rule.read(); @@ -461,9 +466,10 @@ impl Stylist { /// FIXME(emilio): The semantics of the device for Servo and Gecko are /// different enough we may want to unify them. #[cfg(feature = "servo")] - pub fn set_device(&mut self, mut device: Device, stylesheets: &[Arc]) { + pub fn set_device(&mut self, mut device: Device, guard: &SharedRwLockReadGuard, + stylesheets: &[Arc]) { let cascaded_rule = ViewportRule { - declarations: viewport::Cascade::from_stylesheets(stylesheets, &device).finish(), + declarations: viewport::Cascade::from_stylesheets(stylesheets, guard, &device).finish(), }; self.viewport_constraints = @@ -473,15 +479,16 @@ impl Stylist { device.account_for_viewport_rule(constraints); } - fn mq_eval_changed(rules: &[CssRule], before: &Device, after: &Device) -> bool { + fn mq_eval_changed(guard: &SharedRwLockReadGuard, rules: &[CssRule], + before: &Device, after: &Device) -> bool { for rule in rules { - let changed = rule.with_nested_rules_and_mq(|rules, mq| { + let changed = rule.with_nested_rules_and_mq(guard, |rules, mq| { if let Some(mq) = mq { if mq.evaluate(before) != mq.evaluate(after) { return true } } - mq_eval_changed(rules, before, after) + mq_eval_changed(guard, rules, before, after) }); if changed { return true @@ -490,12 +497,12 @@ impl Stylist { false } self.is_device_dirty |= stylesheets.iter().any(|stylesheet| { - let mq = stylesheet.media.read(); + let mq = stylesheet.media.read_with(guard); if mq.evaluate(&self.device) != mq.evaluate(&device) { return true } - mq_eval_changed(&stylesheet.rules.read().0, &self.device, &device) + mq_eval_changed(guard, &stylesheet.rules.read().0, &self.device, &device) }); self.device = Arc::new(device); diff --git a/components/style/viewport.rs b/components/style/viewport.rs index 9c364dc72496..690c06fd3775 100644 --- a/components/style/viewport.rs +++ b/components/style/viewport.rs @@ -15,6 +15,7 @@ use cssparser::ToCss as ParserToCss; use euclid::size::TypedSize2D; use media_queries::Device; use parser::{ParserContext, log_css_error}; +use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard}; use std::ascii::AsciiExt; use std::borrow::Cow; use std::fmt; @@ -504,9 +505,10 @@ impl ViewportRule { } } -impl ToCss for ViewportRule { +impl ToCssWithGuard for ViewportRule { // Serialization of ViewportRule is not specced. - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result + where W: fmt::Write { try!(dest.write_str("@viewport { ")); let mut iter = self.declarations.iter(); try!(iter.next().unwrap().to_css(dest)); @@ -555,13 +557,14 @@ impl Cascade { } } - pub fn from_stylesheets<'a, I>(stylesheets: I, device: &Device) -> Self + pub fn from_stylesheets<'a, I>(stylesheets: I, guard: &SharedRwLockReadGuard, + device: &Device) -> Self where I: IntoIterator, I::Item: AsRef, { let mut cascade = Self::new(); for stylesheet in stylesheets { - stylesheet.as_ref().effective_viewport_rules(device, |rule| { + stylesheet.as_ref().effective_viewport_rules(device, guard, |rule| { for declaration in &rule.declarations { cascade.add(Cow::Borrowed(declaration)) } diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index 07be5c8fce38..b61849f30f28 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -76,6 +76,7 @@ use style::properties::parse_one_declaration; use style::restyle_hints::{self, RestyleHint}; use style::selector_parser::PseudoElementCascadeType; use style::sequential; +use style::shared_lock::{SharedRwLock, ToCssWithGuard, Locked}; use style::string_cache::Atom; use style::stylesheets::{CssRule, CssRules, ImportRule, MediaRule, NamespaceRule}; use style::stylesheets::{Origin, Stylesheet, StyleRule}; @@ -85,7 +86,7 @@ use style::thread_state; use style::timer::Timer; use style::traversal::{resolve_style, DomTraversal, TraversalDriver}; use style_traits::ToCss; -use stylesheet_loader::StylesheetLoader; +use super::stylesheet_loader::StylesheetLoader; /* * For Gecko->Servo function calls, we need to redeclare the same signature that was declared in @@ -95,12 +96,15 @@ use stylesheet_loader::StylesheetLoader; * depend on but good enough for our purposes. */ -struct GlobalStyleData { +pub struct GlobalStyleData { // How many threads parallel styling can use. pub num_threads: usize, // The parallel styling thread pool. pub style_thread_pool: Option, + + // Shared RWLock for CSSOM objects + pub shared_lock: SharedRwLock, } impl GlobalStyleData { @@ -124,12 +128,13 @@ impl GlobalStyleData { GlobalStyleData { num_threads: num_threads, style_thread_pool: pool, + shared_lock: SharedRwLock::new(), } } } lazy_static! { - static ref GLOBAL_STYLE_DATA: GlobalStyleData = { + pub static ref GLOBAL_STYLE_DATA: GlobalStyleData = { GlobalStyleData::new() }; } @@ -199,7 +204,7 @@ fn traverse_subtree(element: GeckoElement, raw_data: RawServoStyleSetBorrowed, debug!("{:?}", ShowSubtreeData(element.as_node())); let shared_style_context = create_shared_context(&per_doc_data); - let ref global_style_data = *GLOBAL_STYLE_DATA; + let global_style_data = &*GLOBAL_STYLE_DATA; let traversal_driver = if global_style_data.style_thread_pool.is_none() { TraversalDriver::Sequential @@ -330,6 +335,7 @@ pub extern "C" fn Servo_Element_ClearData(element: RawGeckoElementBorrowed) { #[no_mangle] pub extern "C" fn Servo_StyleSheet_Empty(mode: SheetParsingMode) -> RawServoStyleSheetStrong { + let global_style_data = &*GLOBAL_STYLE_DATA; let url = ServoUrl::parse("about:blank").unwrap(); let extra_data = ParserContextExtraData::default(); let origin = match mode { @@ -337,8 +343,9 @@ pub extern "C" fn Servo_StyleSheet_Empty(mode: SheetParsingMode) -> RawServoStyl SheetParsingMode::eUserSheetFeatures => Origin::User, SheetParsingMode::eAgentSheetFeatures => Origin::UserAgent, }; + let shared_lock = global_style_data.shared_lock.clone(); Arc::new(Stylesheet::from_str( - "", url, origin, Default::default(), None, + "", url, origin, Default::default(), shared_lock, None, &StdoutErrorReporter, extra_data) ).into_strong() } @@ -353,6 +360,7 @@ pub extern "C" fn Servo_StyleSheet_FromUTF8Bytes(loader: *mut Loader, referrer: *mut ThreadSafeURIHolder, principal: *mut ThreadSafePrincipalHolder) -> RawServoStyleSheetStrong { + let global_style_data = &*GLOBAL_STYLE_DATA; let input = unsafe { data.as_ref().unwrap().as_str_unchecked() }; let origin = match mode { @@ -380,8 +388,9 @@ pub extern "C" fn Servo_StyleSheet_FromUTF8Bytes(loader: *mut Loader, Some(ref s) => Some(s), }; + let shared_lock = global_style_data.shared_lock.clone(); Arc::new(Stylesheet::from_str( - input, url, origin, Default::default(), loader, + input, url, origin, Default::default(), shared_lock, loader, &StdoutErrorReporter, extra_data) ).into_strong() } @@ -425,13 +434,15 @@ pub extern "C" fn Servo_StyleSheet_ClearAndUpdate(stylesheet: RawServoStyleSheet pub extern "C" fn Servo_StyleSet_AppendStyleSheet(raw_data: RawServoStyleSetBorrowed, raw_sheet: RawServoStyleSheetBorrowed, flush: bool) { + let global_style_data = &*GLOBAL_STYLE_DATA; + let guard = global_style_data.shared_lock.read(); let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut(); let sheet = HasArcFFI::as_arc(&raw_sheet); data.stylesheets.retain(|x| !arc_ptr_eq(x, sheet)); data.stylesheets.push(sheet.clone()); data.stylesheets_changed = true; if flush { - data.flush_stylesheets(); + data.flush_stylesheets(&guard); } } @@ -439,13 +450,15 @@ pub extern "C" fn Servo_StyleSet_AppendStyleSheet(raw_data: RawServoStyleSetBorr pub extern "C" fn Servo_StyleSet_PrependStyleSheet(raw_data: RawServoStyleSetBorrowed, raw_sheet: RawServoStyleSheetBorrowed, flush: bool) { + let global_style_data = &*GLOBAL_STYLE_DATA; + let guard = global_style_data.shared_lock.read(); let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut(); let sheet = HasArcFFI::as_arc(&raw_sheet); data.stylesheets.retain(|x| !arc_ptr_eq(x, sheet)); data.stylesheets.insert(0, sheet.clone()); data.stylesheets_changed = true; if flush { - data.flush_stylesheets(); + data.flush_stylesheets(&guard); } } @@ -454,6 +467,8 @@ pub extern "C" fn Servo_StyleSet_InsertStyleSheetBefore(raw_data: RawServoStyleS raw_sheet: RawServoStyleSheetBorrowed, raw_reference: RawServoStyleSheetBorrowed, flush: bool) { + let global_style_data = &*GLOBAL_STYLE_DATA; + let guard = global_style_data.shared_lock.read(); let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut(); let sheet = HasArcFFI::as_arc(&raw_sheet); let reference = HasArcFFI::as_arc(&raw_reference); @@ -462,7 +477,7 @@ pub extern "C" fn Servo_StyleSet_InsertStyleSheetBefore(raw_data: RawServoStyleS data.stylesheets.insert(index, sheet.clone()); data.stylesheets_changed = true; if flush { - data.flush_stylesheets(); + data.flush_stylesheets(&guard); } } @@ -470,19 +485,23 @@ pub extern "C" fn Servo_StyleSet_InsertStyleSheetBefore(raw_data: RawServoStyleS pub extern "C" fn Servo_StyleSet_RemoveStyleSheet(raw_data: RawServoStyleSetBorrowed, raw_sheet: RawServoStyleSheetBorrowed, flush: bool) { + let global_style_data = &*GLOBAL_STYLE_DATA; + let guard = global_style_data.shared_lock.read(); let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut(); let sheet = HasArcFFI::as_arc(&raw_sheet); data.stylesheets.retain(|x| !arc_ptr_eq(x, sheet)); data.stylesheets_changed = true; if flush { - data.flush_stylesheets(); + data.flush_stylesheets(&guard); } } #[no_mangle] pub extern "C" fn Servo_StyleSet_FlushStyleSheets(raw_data: RawServoStyleSetBorrowed) { + let global_style_data = &*GLOBAL_STYLE_DATA; + let guard = global_style_data.shared_lock.read(); let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut(); - data.flush_stylesheets(); + data.flush_stylesheets(&guard); } #[no_mangle] @@ -564,8 +583,10 @@ macro_rules! impl_basic_rule_funcs { #[no_mangle] pub extern "C" fn $to_css(rule: &$raw_type, result: *mut nsAString) { + let global_style_data = &*GLOBAL_STYLE_DATA; + let guard = global_style_data.shared_lock.read(); let rule = RwLock::<$rule_type>::as_arc(&rule); - rule.read().to_css(unsafe { result.as_mut().unwrap() }).unwrap(); + rule.read().to_css(&guard, unsafe { result.as_mut().unwrap() }).unwrap(); } } } @@ -727,8 +748,10 @@ pub extern "C" fn Servo_StyleSet_Init(pres_context: RawGeckoPresContextOwned) #[no_mangle] pub extern "C" fn Servo_StyleSet_RebuildData(raw_data: RawServoStyleSetBorrowed) { + let global_style_data = &*GLOBAL_STYLE_DATA; + let guard = global_style_data.shared_lock.read(); let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut(); - data.reset_device(); + data.reset_device(&guard); } #[no_mangle] @@ -943,29 +966,37 @@ pub extern "C" fn Servo_DeclarationBlock_RemovePropertyById(declarations: RawSer #[no_mangle] pub extern "C" fn Servo_MediaList_GetText(list: RawServoMediaListBorrowed, result: *mut nsAString) { - let list = RwLock::::as_arc(&list); - list.read().to_css(unsafe { result.as_mut().unwrap() }).unwrap(); + let global_style_data = &*GLOBAL_STYLE_DATA; + let guard = global_style_data.shared_lock.read(); + let list = Locked::::as_arc(&list); + list.read_with(&guard).to_css(unsafe { result.as_mut().unwrap() }).unwrap(); } #[no_mangle] pub extern "C" fn Servo_MediaList_SetText(list: RawServoMediaListBorrowed, text: *const nsACString) { - let list = RwLock::::as_arc(&list); + let global_style_data = &*GLOBAL_STYLE_DATA; + let mut guard = global_style_data.shared_lock.write(); + let list = Locked::::as_arc(&list); let text = unsafe { text.as_ref().unwrap().as_str_unchecked() }; let mut parser = Parser::new(&text); - *list.write() = parse_media_query_list(&mut parser); + *list.write_with(&mut guard) = parse_media_query_list(&mut parser); } #[no_mangle] pub extern "C" fn Servo_MediaList_GetLength(list: RawServoMediaListBorrowed) -> u32 { - let list = RwLock::::as_arc(&list); - list.read().media_queries.len() as u32 + let global_style_data = &*GLOBAL_STYLE_DATA; + let guard = global_style_data.shared_lock.read(); + let list = Locked::::as_arc(&list); + list.read_with(&guard).media_queries.len() as u32 } #[no_mangle] pub extern "C" fn Servo_MediaList_GetMediumAt(list: RawServoMediaListBorrowed, index: u32, result: *mut nsAString) -> bool { - let list = RwLock::::as_arc(&list); - if let Some(media_query) = list.read().media_queries.get(index as usize) { + let global_style_data = &*GLOBAL_STYLE_DATA; + let guard = global_style_data.shared_lock.read(); + let list = Locked::::as_arc(&list); + if let Some(media_query) = list.read_with(&guard).media_queries.get(index as usize) { media_query.to_css(unsafe { result.as_mut().unwrap() }).unwrap(); true } else { @@ -976,17 +1007,21 @@ pub extern "C" fn Servo_MediaList_GetMediumAt(list: RawServoMediaListBorrowed, i #[no_mangle] pub extern "C" fn Servo_MediaList_AppendMedium(list: RawServoMediaListBorrowed, new_medium: *const nsACString) { - let list = RwLock::::as_arc(&list); + let global_style_data = &*GLOBAL_STYLE_DATA; + let mut guard = global_style_data.shared_lock.write(); + let list = Locked::::as_arc(&list); let new_medium = unsafe { new_medium.as_ref().unwrap().as_str_unchecked() }; - list.write().append_medium(new_medium); + list.write_with(&mut guard).append_medium(new_medium); } #[no_mangle] pub extern "C" fn Servo_MediaList_DeleteMedium(list: RawServoMediaListBorrowed, old_medium: *const nsACString) -> bool { - let list = RwLock::::as_arc(&list); + let global_style_data = &*GLOBAL_STYLE_DATA; + let mut guard = global_style_data.shared_lock.write(); + let list = Locked::::as_arc(&list); let old_medium = unsafe { old_medium.as_ref().unwrap().as_str_unchecked() }; - list.write().delete_medium(old_medium) + list.write_with(&mut guard).delete_medium(old_medium) } macro_rules! get_longhand_from_id { diff --git a/ports/geckolib/stylesheet_loader.rs b/ports/geckolib/stylesheet_loader.rs index 3e6fb421c869..bcd55cce2fd0 100644 --- a/ports/geckolib/stylesheet_loader.rs +++ b/ports/geckolib/stylesheet_loader.rs @@ -9,6 +9,7 @@ use style::gecko_bindings::structs::{Loader, ServoStyleSheet}; use style::gecko_bindings::sugar::ownership::HasArcFFI; use style::stylesheets::{ImportRule, StylesheetLoader as StyleStylesheetLoader}; use style_traits::ToCss; +use super::glue::GLOBAL_STYLE_DATA; pub struct StylesheetLoader(*mut Loader, *mut ServoStyleSheet); @@ -20,6 +21,8 @@ impl StylesheetLoader { impl StyleStylesheetLoader for StylesheetLoader { fn request_stylesheet(&self, import_rule: &Arc>) { + let global_style_data = &*GLOBAL_STYLE_DATA; + let guard = global_style_data.shared_lock.read(); let import = import_rule.read(); let (spec_bytes, spec_len) = import.url.as_slice_components() .expect("Import only loads valid URLs"); @@ -32,7 +35,7 @@ impl StyleStylesheetLoader for StylesheetLoader { // evaluate them on the main thread. // // Meanwhile, this works. - let media = import.stylesheet.media.read().to_css_string(); + let media = import.stylesheet.media.read_with(&guard).to_css_string(); unsafe { Gecko_LoadStyleSheet(self.0, diff --git a/tests/unit/style/media_queries.rs b/tests/unit/style/media_queries.rs index 95182676e3c2..652d68ce953e 100644 --- a/tests/unit/style/media_queries.rs +++ b/tests/unit/style/media_queries.rs @@ -11,6 +11,7 @@ use style::error_reporting::ParseErrorReporter; use style::media_queries::*; use style::parser::ParserContextExtraData; use style::servo::media_queries::*; +use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard}; use style::stylesheets::{Stylesheet, Origin, CssRule}; use style::values::specified; use style_traits::ToCss; @@ -29,26 +30,27 @@ fn test_media_rule(css: &str, callback: F) let url = ServoUrl::parse("http://localhost").unwrap(); let css_str = css.to_owned(); let stylesheet = Stylesheet::from_str( - css, url, Origin::Author, Default::default(), + css, url, Origin::Author, Default::default(), SharedRwLock::new(), None, &CSSErrorReporterTest, ParserContextExtraData::default()); let mut rule_count = 0; - media_queries(&stylesheet.rules.read().0, &mut |mq| { + let guard = stylesheet.shared_lock.read(); + media_queries(&guard, &stylesheet.rules.read().0, &mut |mq| { rule_count += 1; callback(mq, css); }); assert!(rule_count > 0, css_str); } -fn media_queries(rules: &[CssRule], f: &mut F) +fn media_queries(guard: &SharedRwLockReadGuard, rules: &[CssRule], f: &mut F) where F: FnMut(&MediaList), { for rule in rules { - rule.with_nested_rules_and_mq(|rules, mq| { + rule.with_nested_rules_and_mq(guard, |rules, mq| { if let Some(mq) = mq { f(mq) } - media_queries(rules, f) + media_queries(guard, rules, f) }) } } @@ -56,11 +58,11 @@ fn media_queries(rules: &[CssRule], f: &mut F) 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(), + css, url, Origin::Author, Default::default(), SharedRwLock::new(), None, &CSSErrorReporterTest, ParserContextExtraData::default()); let mut rule_count = 0; - ss.effective_style_rules(device, |_| rule_count += 1); + ss.effective_style_rules(device, &ss.shared_lock.read(), |_| rule_count += 1); assert!(rule_count == expected_rule_count, css.to_owned()); } diff --git a/tests/unit/style/rule_tree/bench.rs b/tests/unit/style/rule_tree/bench.rs index 37bef590a648..623a534159a4 100644 --- a/tests/unit/style/rule_tree/bench.rs +++ b/tests/unit/style/rule_tree/bench.rs @@ -12,6 +12,7 @@ use style::media_queries::MediaList; use style::parser::ParserContextExtraData; use style::properties::{longhands, Importance, PropertyDeclaration, PropertyDeclarationBlock}; use style::rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource}; +use style::shared_lock::SharedRwLock; use style::stylesheets::{Origin, Stylesheet, CssRule}; use test::{self, Bencher}; @@ -44,6 +45,7 @@ fn parse_rules(css: &str) -> Vec<(StyleSource, CascadeLevel)> { MediaList { media_queries: vec![], }, + SharedRwLock::new(), None, &ErrorringErrorReporter, ParserContextExtraData {}); diff --git a/tests/unit/style/stylesheets.rs b/tests/unit/style/stylesheets.rs index a1d649c099ca..590e93185f40 100644 --- a/tests/unit/style/stylesheets.rs +++ b/tests/unit/style/stylesheets.rs @@ -20,6 +20,7 @@ use style::properties::Importance; use style::properties::{CSSWideKeyword, DeclaredValueOwned, PropertyDeclaration, PropertyDeclarationBlock}; use style::properties::longhands; use style::properties::longhands::animation_play_state; +use style::shared_lock::SharedRwLock; use style::stylesheets::{Origin, Namespaces}; use style::stylesheets::{Stylesheet, NamespaceRule, CssRule, CssRules, StyleRule, KeyframesRule}; use style::values::specified::{LengthOrPercentageOrAuto, Percentage}; @@ -62,14 +63,15 @@ fn test_parse_stylesheet() { }"; let url = ServoUrl::parse("about::test").unwrap(); let stylesheet = Stylesheet::from_str(css, url.clone(), Origin::UserAgent, Default::default(), - None, + SharedRwLock::new(), None, &CSSErrorReporterTest, ParserContextExtraData::default()); let mut namespaces = Namespaces::default(); namespaces.default = Some(ns!(html)); let expected = Stylesheet { origin: Origin::UserAgent, - media: Default::default(), + media: Arc::new(stylesheet.shared_lock.wrap(Default::default())), + shared_lock: stylesheet.shared_lock.clone(), namespaces: RwLock::new(namespaces), base_url: url, dirty_on_viewport_size_change: AtomicBool::new(false), @@ -324,7 +326,7 @@ fn test_report_error_stylesheet() { let errors = error_reporter.errors.clone(); Stylesheet::from_str(css, url.clone(), Origin::UserAgent, Default::default(), - None, + SharedRwLock::new(), None, &error_reporter, ParserContextExtraData::default()); diff --git a/tests/unit/style/viewport.rs b/tests/unit/style/viewport.rs index 166094ebdc15..7423286227dd 100644 --- a/tests/unit/style/viewport.rs +++ b/tests/unit/style/viewport.rs @@ -9,6 +9,7 @@ use servo_config::prefs::{PREFS, PrefValue}; use servo_url::ServoUrl; use style::media_queries::{Device, MediaType}; use style::parser::{ParserContext, ParserContextExtraData}; +use style::shared_lock::SharedRwLock; use style::stylesheets::{Stylesheet, Origin}; use style::values::specified::LengthOrPercentageOrAuto::{self, Auto}; use style::values::specified::NoCalcLength::{self, ViewportPercentage}; @@ -19,11 +20,15 @@ use style_traits::viewport::*; macro_rules! stylesheet { ($css:expr, $origin:ident, $error_reporter:expr) => { + stylesheet!($css, $origin, $error_reporter, SharedRwLock::new()) + }; + ($css:expr, $origin:ident, $error_reporter:expr, $shared_lock:expr) => { Box::new(Stylesheet::from_str( $css, ServoUrl::parse("http://localhost").unwrap(), Origin::$origin, Default::default(), + $shared_lock, None, &$error_reporter, ParserContextExtraData::default() @@ -39,7 +44,7 @@ fn test_viewport_rule(css: &str, PREFS.set("layout.viewport.enabled", PrefValue::Boolean(true)); let stylesheet = stylesheet!(css, Author, CSSErrorReporterTest); let mut rule_count = 0; - stylesheet.effective_viewport_rules(&device, |rule| { + stylesheet.effective_viewport_rules(&device, &stylesheet.shared_lock.read(), |rule| { rule_count += 1; callback(&rule.declarations, css); }); @@ -251,24 +256,31 @@ fn multiple_stylesheets_cascading() { PREFS.set("layout.viewport.enabled", PrefValue::Boolean(true)); let device = Device::new(MediaType::Screen, TypedSize2D::new(800., 600.)); let error_reporter = CSSErrorReporterTest; + let shared_lock = SharedRwLock::new(); let stylesheets = vec![ - stylesheet!("@viewport { min-width: 100px; min-height: 100px; zoom: 1; }", UserAgent, error_reporter), - stylesheet!("@viewport { min-width: 200px; min-height: 200px; }", User, error_reporter), - stylesheet!("@viewport { min-width: 300px; }", Author, error_reporter)]; - - let declarations = Cascade::from_stylesheets(&stylesheets, &device).finish(); + stylesheet!("@viewport { min-width: 100px; min-height: 100px; zoom: 1; }", + UserAgent, error_reporter, shared_lock.clone()), + stylesheet!("@viewport { min-width: 200px; min-height: 200px; }", + User, error_reporter, shared_lock.clone()), + stylesheet!("@viewport { min-width: 300px; }", + Author, error_reporter, shared_lock.clone()) + ]; + + let declarations = Cascade::from_stylesheets(&stylesheets, &shared_lock.read(), &device).finish(); assert_decl_len!(declarations == 3); assert_decl_eq!(&declarations[0], UserAgent, Zoom: Zoom::Number(1.)); assert_decl_eq!(&declarations[1], User, MinHeight: viewport_length!(200., px)); assert_decl_eq!(&declarations[2], Author, MinWidth: viewport_length!(300., px)); let stylesheets = vec![ - stylesheet!("@viewport { min-width: 100px !important; }", UserAgent, error_reporter), + stylesheet!("@viewport { min-width: 100px !important; }", + UserAgent, error_reporter, shared_lock.clone()), stylesheet!("@viewport { min-width: 200px !important; min-height: 200px !important; }", - User, error_reporter), + User, error_reporter, shared_lock.clone()), stylesheet!("@viewport { min-width: 300px !important; min-height: 300px !important; zoom: 3 !important; }", - Author, error_reporter)]; - let declarations = Cascade::from_stylesheets(&stylesheets, &device).finish(); + Author, error_reporter, shared_lock.clone()) + ]; + let declarations = Cascade::from_stylesheets(&stylesheets, &shared_lock.read(), &device).finish(); assert_decl_len!(declarations == 3); assert_decl_eq!(&declarations[0], UserAgent, MinWidth: viewport_length!(100., px), !important); assert_decl_eq!(&declarations[1], User, MinHeight: viewport_length!(200., px), !important); diff --git a/tests/unit/stylo/lib.rs b/tests/unit/stylo/lib.rs index c89687918ef4..c0d04ffcbec5 100644 --- a/tests/unit/stylo/lib.rs +++ b/tests/unit/stylo/lib.rs @@ -21,8 +21,5 @@ extern crate style_traits; mod sanity_checks; mod size_of; -#[path = "../../../ports/geckolib/stylesheet_loader.rs"] -mod stylesheet_loader; - mod servo_function_signatures; diff --git a/tests/unit/stylo/servo_function_signatures.rs b/tests/unit/stylo/servo_function_signatures.rs index 6bdeb6d5af72..30d6d02439b7 100644 --- a/tests/unit/stylo/servo_function_signatures.rs +++ b/tests/unit/stylo/servo_function_signatures.rs @@ -10,6 +10,9 @@ use style::gecko_properties::*; include!(concat!(env!("OUT_DIR"), "/check_bindings.rs")); +#[path = "../../../ports/geckolib/stylesheet_loader.rs"] +mod stylesheet_loader; + #[allow(non_snake_case, unused_unsafe, private_no_mangle_fns)] mod glue { // this module pretends to be glue.rs, with the safe functions swapped for unsafe ones. This is