diff options
Diffstat (limited to 'components/script/dom')
-rw-r--r-- | components/script/dom/bindings/trace.rs | 16 | ||||
-rw-r--r-- | components/script/dom/document.rs | 206 | ||||
-rw-r--r-- | components/script/dom/htmllinkelement.rs | 18 | ||||
-rw-r--r-- | components/script/dom/htmlmetaelement.rs | 14 | ||||
-rw-r--r-- | components/script/dom/htmlstyleelement.rs | 23 | ||||
-rw-r--r-- | components/script/dom/mediaquerylist.rs | 11 | ||||
-rw-r--r-- | components/script/dom/stylesheetlist.rs | 12 | ||||
-rw-r--r-- | components/script/dom/window.rs | 10 |
8 files changed, 210 insertions, 100 deletions
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 4168fb4effa..df681e3add9 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -102,7 +102,8 @@ 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, FontFaceRule, KeyframesRule, MediaRule}; +use style::stylesheet_set::StylesheetSet; +use style::stylesheets::{CssRules, FontFaceRule, KeyframesRule, MediaRule, Stylesheet}; use style::stylesheets::{NamespaceRule, StyleRule, ImportRule, SupportsRule, ViewportRule}; use style::stylesheets::keyframes_rule::Keyframe; use style::values::specified::Length; @@ -374,6 +375,7 @@ unsafe_no_jsmanaged_fields!(AttrIdentifier); unsafe_no_jsmanaged_fields!(AttrValue); unsafe_no_jsmanaged_fields!(Snapshot); unsafe_no_jsmanaged_fields!(PendingRestyle); +unsafe_no_jsmanaged_fields!(Stylesheet); unsafe_no_jsmanaged_fields!(HttpsState); unsafe_no_jsmanaged_fields!(Request); unsafe_no_jsmanaged_fields!(RequestInit); @@ -641,6 +643,18 @@ unsafe impl JSTraceable for StyleLocked<MediaList> { } } +unsafe impl<S> JSTraceable for StylesheetSet<S> +where + S: JSTraceable + ::style::stylesheets::StylesheetInDocument + PartialEq + 'static, +{ + unsafe fn trace(&self, tracer: *mut JSTracer) { + for s in self.iter() { + s.trace(tracer) + } + } +} + + /// Holds a set of JSTraceables that need to be rooted struct RootedTraceableSet { set: Vec<*const JSTraceable>, diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 39f5a8e6d38..3105f0f4341 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -36,6 +36,7 @@ use dom::bindings::xmlname::{namespace_from_domstring, validate_and_extract, xml use dom::bindings::xmlname::XMLName::InvalidXMLName; use dom::closeevent::CloseEvent; use dom::comment::Comment; +use dom::cssstylesheet::CSSStyleSheet; use dom::customelementregistry::CustomElementDefinition; use dom::customevent::CustomEvent; use dom::documentfragment::DocumentFragment; @@ -63,6 +64,7 @@ use dom::htmlheadelement::HTMLHeadElement; use dom::htmlhtmlelement::HTMLHtmlElement; use dom::htmliframeelement::HTMLIFrameElement; use dom::htmlimageelement::HTMLImageElement; +use dom::htmlmetaelement::HTMLMetaElement; use dom::htmlscriptelement::{HTMLScriptElement, ScriptResult}; use dom::htmltitleelement::HTMLTitleElement; use dom::keyboardevent::KeyboardEvent; @@ -135,10 +137,12 @@ use std::time::{Duration, Instant}; use style::attr::AttrValue; use style::context::{QuirksMode, ReflowGoal}; use style::invalidation::element::restyle_hints::{RestyleHint, RESTYLE_SELF, RESTYLE_STYLE_ATTRIBUTE}; +use style::media_queries::{Device, MediaList, MediaType}; use style::selector_parser::{RestyleDamage, Snapshot}; -use style::shared_lock::SharedRwLock as StyleSharedRwLock; +use style::shared_lock::{SharedRwLock as StyleSharedRwLock, SharedRwLockReadGuard}; use style::str::{HTML_SPACE_CHARACTERS, split_html_space_chars, str_join}; -use style::stylesheets::Stylesheet; +use style::stylesheet_set::StylesheetSet; +use style::stylesheets::{Stylesheet, StylesheetContents, OriginSet}; use task_source::TaskSource; use time; use timers::OneshotTimerCallback; @@ -166,14 +170,6 @@ pub enum IsHTMLDocument { NonHTMLDocument, } -#[derive(JSTraceable, HeapSizeOf)] -#[must_root] -pub struct StylesheetInDocument { - pub node: JS<Node>, - #[ignore_heap_size_of = "Arc"] - pub stylesheet: Arc<Stylesheet>, -} - #[derive(Debug, HeapSizeOf)] pub struct PendingRestyle { /// If this element had a state or attribute change since the last restyle, track @@ -197,6 +193,34 @@ impl PendingRestyle { } } +#[derive(Clone, JSTraceable, HeapSizeOf)] +#[must_root] +struct StyleSheetInDocument { + #[ignore_heap_size_of = "Arc"] + sheet: Arc<Stylesheet>, + owner: JS<Element>, +} + +impl PartialEq for StyleSheetInDocument { + fn eq(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.sheet, &other.sheet) + } +} + +impl ::style::stylesheets::StylesheetInDocument for StyleSheetInDocument { + fn contents(&self, guard: &SharedRwLockReadGuard) -> &StylesheetContents { + self.sheet.contents(guard) + } + + fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> { + self.sheet.media(guard) + } + + fn enabled(&self) -> bool { + self.sheet.enabled() + } +} + /// https://dom.spec.whatwg.org/#document #[dom_struct] pub struct Document { @@ -229,9 +253,7 @@ pub struct Document { /// Can be acquired once for accessing many objects. style_shared_lock: StyleSharedRwLock, /// List of stylesheets associated with nodes in this document. |None| if the list needs to be refreshed. - stylesheets: DOMRefCell<Option<Vec<StylesheetInDocument>>>, - /// Whether the list of stylesheets has changed since the last reflow was triggered. - stylesheets_changed_since_reflow: Cell<bool>, + stylesheets: DOMRefCell<StylesheetSet<StyleSheetInDocument>>, stylesheet_list: MutNullableJS<StyleSheetList>, ready_state: Cell<DocumentReadyState>, /// Whether the DOMContentLoaded event has already been dispatched. @@ -495,15 +517,12 @@ impl Document { // FIXME: This should check the dirty bit on the document, // not the document element. Needs some layout changes to make // that workable. - self.stylesheets_changed_since_reflow.get() || - match self.GetDocumentElement() { - Some(root) => { - root.upcast::<Node>().has_dirty_descendants() || - !self.pending_restyles.borrow().is_empty() || - self.needs_paint() - } - None => false, - } + self.stylesheets.borrow().has_changed() || + self.GetDocumentElement().map_or(false, |root| { + root.upcast::<Node>().has_dirty_descendants() || + !self.pending_restyles.borrow().is_empty() || + self.needs_paint() + }) } /// Returns the first `base` element in the DOM that has an `href` attribute. @@ -1501,20 +1520,16 @@ impl Document { } pub fn invalidate_stylesheets(&self) { - self.stylesheets_changed_since_reflow.set(true); - *self.stylesheets.borrow_mut() = None; + self.stylesheets.borrow_mut().force_dirty(OriginSet::all()); + // Mark the document element dirty so a reflow will be performed. + // + // FIXME(emilio): Use the StylesheetSet invalidation stuff. if let Some(element) = self.GetDocumentElement() { element.upcast::<Node>().dirty(NodeDamage::NodeStyleDamaged); } } - pub fn get_and_reset_stylesheets_changed_since_reflow(&self) -> bool { - let changed = self.stylesheets_changed_since_reflow.get(); - self.stylesheets_changed_since_reflow.set(false); - changed - } - pub fn trigger_mozbrowser_event(&self, event: MozBrowserEvent) { if PREFS.is_mozbrowser_enabled() { if let Some((parent_pipeline_id, _)) = self.window.parent_info() { @@ -2203,8 +2218,7 @@ impl Document { PER_PROCESS_AUTHOR_SHARED_LOCK.clone() //StyleSharedRwLock::new() }, - stylesheets: DOMRefCell::new(None), - stylesheets_changed_since_reflow: Cell::new(false), + stylesheets: DOMRefCell::new(StylesheetSet::new()), stylesheet_list: MutNullableJS::new(None), ready_state: Cell::new(ready_state), domcontentloaded_dispatched: Cell::new(domcontentloaded_dispatched), @@ -2315,40 +2329,113 @@ impl Document { self.GetDocumentElement().and_then(Root::downcast) } - // Ensure that the stylesheets vector is populated - fn ensure_stylesheets(&self) { + /// Return a reference to the per-document shared lock used in stylesheets. + pub fn style_shared_lock(&self) -> &StyleSharedRwLock { + &self.style_shared_lock + } + + /// Flushes the stylesheet list, and returns whether any stylesheet changed. + pub fn flush_stylesheets_for_reflow(&self) -> bool { + // NOTE(emilio): The invalidation machinery is used on the replicated + // list on the layout thread. let mut stylesheets = self.stylesheets.borrow_mut(); - if stylesheets.is_none() { - *stylesheets = Some(self.upcast::<Node>() - .traverse_preorder() - .filter_map(|node| { - node.get_stylesheet() - .map(|stylesheet| StylesheetInDocument { - node: JS::from_ref(&*node), - stylesheet: stylesheet, - }) - }) - .collect()); + let have_changed = stylesheets.has_changed(); + stylesheets.flush_without_invalidation(); + have_changed + } + + /// Returns a `Device` suitable for media query evaluation. + /// + /// FIXME(emilio): This really needs to be somehow more in sync with layout. + /// Feels like a hack. + /// + /// Also, shouldn't return an option, I'm quite sure. + pub fn device(&self) -> Option<Device> { + let window_size = match self.window().window_size() { + Some(ws) => ws, + None => return None, }; + + let viewport_size = window_size.initial_viewport; + let device_pixel_ratio = window_size.device_pixel_ratio; + Some(Device::new(MediaType::screen(), viewport_size, device_pixel_ratio)) } - /// Return a reference to the per-document shared lock used in stylesheets. - pub fn style_shared_lock(&self) -> &StyleSharedRwLock { - &self.style_shared_lock + /// Remove a stylesheet owned by `owner` from the list of document sheets. + #[allow(unrooted_must_root)] // Owner needs to be rooted already necessarily. + pub fn remove_stylesheet(&self, owner: &Element, s: &Arc<Stylesheet>) { + self.window() + .layout_chan() + .send(Msg::RemoveStylesheet(s.clone())) + .unwrap(); + + let guard = s.shared_lock.read(); + let device = self.device(); + + // FIXME(emilio): Would be nice to remove the clone, etc. + self.stylesheets.borrow_mut().remove_stylesheet( + device.as_ref(), + StyleSheetInDocument { + sheet: s.clone(), + owner: JS::from_ref(owner), + }, + &guard, + ); + } + + /// Add a stylesheet owned by `owner` to the list of document sheets, in the + /// correct tree position. + #[allow(unrooted_must_root)] // Owner needs to be rooted already necessarily. + pub fn add_stylesheet(&self, owner: &Element, sheet: Arc<Stylesheet>) { + // FIXME(emilio): It'd be nice to unify more code between the elements + // that own stylesheets, but StylesheetOwner is more about loading + // them... + debug_assert!(owner.as_stylesheet_owner().is_some() || + owner.is::<HTMLMetaElement>(), "Wat"); + + let mut stylesheets = self.stylesheets.borrow_mut(); + let insertion_point = stylesheets.iter().find(|sheet_in_doc| { + owner.upcast::<Node>().is_before(sheet_in_doc.owner.upcast()) + }).cloned(); + + self.window() + .layout_chan() + .send(Msg::AddStylesheet( + sheet.clone(), + insertion_point.as_ref().map(|s| s.sheet.clone()) + )) + .unwrap(); + + let sheet = StyleSheetInDocument { + sheet, + owner: JS::from_ref(owner), + }; + + let lock = self.style_shared_lock(); + let guard = lock.read(); + + let device = self.device(); + match insertion_point { + Some(ip) => { + stylesheets.insert_stylesheet_before(device.as_ref(), sheet, ip, &guard); + } + None => { + stylesheets.append_stylesheet(device.as_ref(), sheet, &guard); + } + } } - /// Returns the list of stylesheets associated with nodes in the document. - pub fn stylesheets(&self) -> Vec<Arc<Stylesheet>> { - self.ensure_stylesheets(); - self.stylesheets.borrow().as_ref().unwrap().iter() - .map(|s| s.stylesheet.clone()) - .collect() + /// Returns the number of document stylesheets. + pub fn stylesheet_count(&self) -> usize { + self.stylesheets.borrow().len() } - pub fn with_style_sheets_in_document<F, T>(&self, mut f: F) -> T - where F: FnMut(&[StylesheetInDocument]) -> T { - self.ensure_stylesheets(); - f(&self.stylesheets.borrow().as_ref().unwrap()) + pub fn stylesheet_at(&self, index: usize) -> Option<Root<CSSStyleSheet>> { + let stylesheets = self.stylesheets.borrow(); + + stylesheets.get(index).and_then(|s| { + s.owner.upcast::<Node>().get_cssom_stylesheet() + }) } /// https://html.spec.whatwg.org/multipage/#appropriate-template-contents-owner-document @@ -3684,8 +3771,7 @@ impl DocumentMethods for Document { self.scripts.set(None); self.anchors.set(None); self.applets.set(None); - *self.stylesheets.borrow_mut() = None; - self.stylesheets_changed_since_reflow.set(true); + *self.stylesheets.borrow_mut() = StylesheetSet::new(); self.animation_frame_ident.set(0); self.animation_frame_list.borrow_mut().clear(); self.pending_restyles.borrow_mut().clear(); diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs index e30dbbc783d..d7e5c071f50 100644 --- a/components/script/dom/htmllinkelement.rs +++ b/components/script/dom/htmllinkelement.rs @@ -24,7 +24,6 @@ use dom::virtualmethods::VirtualMethods; use dom_struct::dom_struct; use html5ever::{LocalName, Prefix}; use net_traits::ReferrerPolicy; -use script_layout_interface::message::Msg; use script_traits::{MozBrowserEvent, ScriptMsg}; use servo_arc::Arc; use std::ascii::AsciiExt; @@ -39,8 +38,6 @@ use style::stylesheets::{CssRuleType, Stylesheet}; use style_traits::PARSING_MODE_DEFAULT; use stylesheet_loader::{StylesheetLoader, StylesheetContextSource, StylesheetOwner}; -unsafe_no_jsmanaged_fields!(Stylesheet); - #[derive(JSTraceable, PartialEq, Clone, Copy, HeapSizeOf)] pub struct RequestGenerationId(u32); @@ -98,10 +95,16 @@ impl HTMLLinkElement { self.request_generation_id.get() } + // FIXME(emilio): These methods are duplicated with + // HTMLStyleElement::set_stylesheet. pub fn set_stylesheet(&self, s: Arc<Stylesheet>) { + let doc = document_from_node(self); + if let Some(ref s) = *self.stylesheet.borrow() { + doc.remove_stylesheet(self.upcast(), s) + } *self.stylesheet.borrow_mut() = Some(s.clone()); - window_from_node(self).layout_chan().send(Msg::AddStylesheet(s)).unwrap(); - document_from_node(self).invalidate_stylesheets(); + self.cssom_stylesheet.set(None); + doc.add_stylesheet(self.upcast(), s); } pub fn get_stylesheet(&self) -> Option<Arc<Stylesheet>> { @@ -239,8 +242,9 @@ impl VirtualMethods for HTMLLinkElement { s.unbind_from_tree(context); } - let document = document_from_node(self); - document.invalidate_stylesheets(); + if let Some(ref s) = *self.stylesheet.borrow() { + document_from_node(self).remove_stylesheet(self.upcast(), s); + } } } diff --git a/components/script/dom/htmlmetaelement.rs b/components/script/dom/htmlmetaelement.rs index 481da1c092f..d2cb332e7e4 100644 --- a/components/script/dom/htmlmetaelement.rs +++ b/components/script/dom/htmlmetaelement.rs @@ -99,10 +99,10 @@ impl HTMLMetaElement { let content = content.value(); if !content.is_empty() { if let Some(translated_rule) = ViewportRule::from_meta(&**content) { - let document = self.upcast::<Node>().owner_doc(); + let document = document_from_node(self); let shared_lock = document.style_shared_lock(); let rule = CssRule::Viewport(Arc::new(shared_lock.wrap(translated_rule))); - *self.stylesheet.borrow_mut() = Some(Arc::new(Stylesheet { + let sheet = Arc::new(Stylesheet { contents: StylesheetContents { rules: CssRules::new(vec![rule], shared_lock), origin: Origin::Author, @@ -118,9 +118,9 @@ impl HTMLMetaElement { media: Arc::new(shared_lock.wrap(MediaList::empty())), shared_lock: shared_lock.clone(), disabled: AtomicBool::new(false), - })); - let doc = document_from_node(self); - doc.invalidate_stylesheets(); + }); + *self.stylesheet.borrow_mut() = Some(sheet.clone()); + document.add_stylesheet(self.upcast(), sheet); } } } @@ -199,6 +199,10 @@ impl VirtualMethods for HTMLMetaElement { if context.tree_in_doc { self.process_referrer_attribute(); + + if let Some(ref s) = *self.stylesheet.borrow() { + document_from_node(self).remove_stylesheet(self.upcast(), s); + } } } } diff --git a/components/script/dom/htmlstyleelement.rs b/components/script/dom/htmlstyleelement.rs index 9c6f34ba7bd..222f25a9b1d 100644 --- a/components/script/dom/htmlstyleelement.rs +++ b/components/script/dom/htmlstyleelement.rs @@ -20,7 +20,6 @@ use dom::virtualmethods::VirtualMethods; use dom_struct::dom_struct; use html5ever::{LocalName, Prefix}; use net_traits::ReferrerPolicy; -use script_layout_interface::message::Msg; use servo_arc::Arc; use std::cell::Cell; use style::media_queries::parse_media_query_list; @@ -109,10 +108,18 @@ impl HTMLStyleElement { self.upcast::<EventTarget>().fire_event(atom!("load")); } - win.layout_chan().send(Msg::AddStylesheet(sheet.clone())).unwrap(); - *self.stylesheet.borrow_mut() = Some(sheet); + self.set_stylesheet(sheet); + } + + // FIXME(emilio): This is duplicated with HTMLLinkElement::set_stylesheet. + pub fn set_stylesheet(&self, s: Arc<Stylesheet>) { + let doc = document_from_node(self); + if let Some(ref s) = *self.stylesheet.borrow() { + doc.remove_stylesheet(self.upcast(), s) + } + *self.stylesheet.borrow_mut() = Some(s.clone()); self.cssom_stylesheet.set(None); - doc.invalidate_stylesheets(); + doc.add_stylesheet(self.upcast(), s); } pub fn get_stylesheet(&self) -> Option<Arc<Stylesheet>> { @@ -180,8 +187,12 @@ impl VirtualMethods for HTMLStyleElement { s.unbind_from_tree(context); } - let doc = document_from_node(self); - doc.invalidate_stylesheets(); + if context.tree_in_doc { + if let Some(ref s) = *self.stylesheet.borrow() { + let doc = document_from_node(self); + doc.remove_stylesheet(self.upcast(), s) + } + } } } diff --git a/components/script/dom/mediaquerylist.rs b/components/script/dom/mediaquerylist.rs index 60cb875163e..199241b14dd 100644 --- a/components/script/dom/mediaquerylist.rs +++ b/components/script/dom/mediaquerylist.rs @@ -22,7 +22,7 @@ use dom_struct::dom_struct; use js::jsapi::JSTracer; use std::cell::Cell; use std::rc::Rc; -use style::media_queries::{Device, MediaList, MediaType}; +use style::media_queries::MediaList; use style_traits::ToCss; pub enum MediaQueryListMatchState { @@ -74,14 +74,9 @@ impl MediaQueryList { } pub fn evaluate(&self) -> bool { - if let Some(window_size) = self.document.window().window_size() { - let viewport_size = window_size.initial_viewport; - let device_pixel_ratio = window_size.device_pixel_ratio; - let device = Device::new(MediaType::screen(), viewport_size, device_pixel_ratio); + self.document.device().map_or(false, |device| { self.media_query_list.evaluate(&device, self.document.quirks_mode()) - } else { - false - } + }) } } diff --git a/components/script/dom/stylesheetlist.rs b/components/script/dom/stylesheetlist.rs index 25d95ae3986..faeb04c1dda 100644 --- a/components/script/dom/stylesheetlist.rs +++ b/components/script/dom/stylesheetlist.rs @@ -36,18 +36,14 @@ impl StyleSheetList { impl StyleSheetListMethods for StyleSheetList { // https://drafts.csswg.org/cssom/#dom-stylesheetlist-length fn Length(&self) -> u32 { - self.document.with_style_sheets_in_document(|s| s.len() as u32) + self.document.stylesheet_count() as u32 } // https://drafts.csswg.org/cssom/#dom-stylesheetlist-item fn Item(&self, index: u32) -> Option<Root<StyleSheet>> { - // XXXManishearth this doesn't handle the origin clean flag - // and is a cors vulnerability - self.document.with_style_sheets_in_document(|sheets| { - sheets.get(index as usize) - .and_then(|sheet| sheet.node.get_cssom_stylesheet()) - .map(Root::upcast) - }) + // XXXManishearth this doesn't handle the origin clean flag and is a + // cors vulnerability + self.document.stylesheet_at(index as usize).map(Root::upcast) } // check-tidy: no specs after this line diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 48677a64d38..d45a6b9bc17 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -1240,7 +1240,8 @@ impl Window { } let document = self.Document(); - let stylesheets_changed = document.get_and_reset_stylesheets_changed_since_reflow(); + + let stylesheets_changed = document.flush_stylesheets_for_reflow(); // Send new document and relevant styles to layout. let reflow = ScriptReflow { @@ -1249,11 +1250,10 @@ impl Window { page_clip_rect: self.page_clip_rect.get(), }, document: self.Document().upcast::<Node>().to_trusted_node_address(), - document_stylesheets: document.stylesheets(), - stylesheets_changed: stylesheets_changed, - window_size: window_size, + stylesheets_changed, + window_size, + query_type, script_join_chan: join_chan, - query_type: query_type, dom_count: self.Document().dom_count(), }; |