aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom')
-rw-r--r--components/script/dom/bindings/trace.rs16
-rw-r--r--components/script/dom/document.rs206
-rw-r--r--components/script/dom/htmllinkelement.rs18
-rw-r--r--components/script/dom/htmlmetaelement.rs14
-rw-r--r--components/script/dom/htmlstyleelement.rs23
-rw-r--r--components/script/dom/mediaquerylist.rs11
-rw-r--r--components/script/dom/stylesheetlist.rs12
-rw-r--r--components/script/dom/window.rs10
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(),
};