diff options
author | Till Schneidereit <till@tillschneidereit.net> | 2015-10-06 15:55:28 +0200 |
---|---|---|
committer | Till Schneidereit <till@tillschneidereit.net> | 2015-11-07 18:11:29 +0100 |
commit | 543703e3d8d7dde5efd280fb01e42061287c7002 (patch) | |
tree | 3b36ac6d5b08f842035f7f9b2eed16b94aeb0d28 | |
parent | 068e6a8e9f926fa6e8649b934ee9ffe2ef7b78de (diff) | |
download | servo-543703e3d8d7dde5efd280fb01e42061287c7002.tar.gz servo-543703e3d8d7dde5efd280fb01e42061287c7002.zip |
Move Stylesheet loading and ownership from the layout task into HTML elements
Stylesheets for `HTMLLinkElement`s are now loaded by the resource task, triggered by the element in question. Stylesheets are owned by the elements they're associated with, which can be `HTMLStyleElement`, `HTMLLinkElement`, and `HTMLMetaElement` (for `<meta name="viewport">).
Additionally, the quirks mode stylesheet (just as the user and user agent stylesheets a couple of commits ago), is implemented as a lazy static, loaded once per process and shared between all documents.
This all has various nice consequences:
- Stylesheet loading becomes a non-blocking operation.
- Stylesheets are removed when the element they're associated with is removed from the document.
- It'll be possible to implement the CSSOM APIs that require direct access to the stylesheets (i.e., ~ all of them).
- Various subtle correctness issues are fixed.
One piece of interesting follow-up work would be to move parsing of external stylesheets to the resource task, too. Right now, it happens in the link element once loading is complete, so blocks the script task. Moving it to the resource task would probably be fairly straight-forward as it doesn't require access to any external state.
-rw-r--r-- | components/layout/layout_task.rs | 97 | ||||
-rw-r--r-- | components/script/dom/create.rs | 2 | ||||
-rw-r--r-- | components/script/dom/document.rs | 58 | ||||
-rw-r--r-- | components/script/dom/htmllinkelement.rs | 163 | ||||
-rw-r--r-- | components/script/dom/htmlmetaelement.rs | 29 | ||||
-rw-r--r-- | components/script/dom/htmlstyleelement.rs | 24 | ||||
-rw-r--r-- | components/script/dom/window.rs | 7 | ||||
-rw-r--r-- | components/script/layout_interface.rs | 18 | ||||
-rw-r--r-- | components/script/script_task.rs | 13 | ||||
-rw-r--r-- | components/script_traits/lib.rs | 10 | ||||
-rw-r--r-- | components/style/selector_matching.rs | 222 | ||||
-rw-r--r-- | components/style/stylesheets.rs | 19 | ||||
-rw-r--r-- | components/style_traits/viewport.rs | 2 | ||||
-rw-r--r-- | tests/unit/style/stylesheets.rs | 1 | ||||
-rw-r--r-- | tests/wpt/metadata-css/css21_dev/html4/cascade-import-dynamic-control.htm.ini | 3 |
15 files changed, 387 insertions, 281 deletions
diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs index 46f889de0a3..9e0a77c8bfc 100644 --- a/components/layout/layout_task.rs +++ b/components/layout/layout_task.rs @@ -16,8 +16,6 @@ use context::{SharedLayoutContext, heap_size_of_local_context}; use cssparser::ToCss; use data::LayoutDataWrapper; use display_list_builder::ToGfxColor; -use encoding::EncodingRef; -use encoding::all::UTF_8; use euclid::Matrix4; use euclid::point::Point2D; use euclid::rect::Rect; @@ -42,7 +40,6 @@ use msg::compositor_msg::{Epoch, LayerId, ScrollPolicy}; use msg::constellation_msg::Msg as ConstellationMsg; use msg::constellation_msg::{ConstellationChan, Failure, PipelineExitType, PipelineId}; use net_traits::image_cache_task::{ImageCacheChan, ImageCacheResult, ImageCacheTask}; -use net_traits::{PendingAsyncLoad, load_bytes_iter}; use opaque_node::OpaqueNodeMethods; use parallel::{self, WorkQueueData}; use profile_traits::mem::{self, Report, ReportKind, ReportsChan}; @@ -56,7 +53,6 @@ use script::layout_interface::Animation; use script::layout_interface::{LayoutChan, LayoutRPC, OffsetParentResponse}; use script::layout_interface::{Msg, NewLayoutTaskInfo, Reflow, ReflowGoal, ReflowQueryType}; use script::layout_interface::{ScriptLayoutChan, ScriptReflow, TrustedNodeAddress}; -use script_traits::StylesheetLoadResponder; use script_traits::{ConstellationControlMsg, LayoutControlMsg, OpaqueScriptLayoutChannel}; use selectors::parser::PseudoElement; use sequential; @@ -72,13 +68,12 @@ use std::sync::mpsc::{channel, Sender, Receiver}; use std::sync::{Arc, Mutex, MutexGuard}; use string_cache::Atom; use style::computed_values::{self, filter, mix_blend_mode}; -use style::media_queries::{Device, MediaQueryList, MediaType}; +use style::media_queries::{Device, MediaType}; use style::properties::longhands::{display, position}; use style::properties::style_structs; use style::selector_matching::{Stylist, USER_OR_USER_AGENT_STYLESHEETS}; -use style::stylesheets::{CSSRule, CSSRuleIteratorExt, Origin, Stylesheet}; +use style::stylesheets::{CSSRuleIteratorExt, Stylesheet}; use style::values::AuExtensionMethods; -use style::viewport::ViewportRule; use url::Url; use util::geometry::{MAX_RECT, ZERO_POINT}; use util::ipc::OptionalIpcSender; @@ -576,20 +571,10 @@ impl LayoutTask { LayoutTaskData>>) -> bool { match request { - Msg::AddStylesheet(sheet, mq) => { - self.handle_add_stylesheet(sheet, mq, possibly_locked_rw_data) - } - Msg::LoadStylesheet(url, mq, pending, link_element) => { - self.handle_load_stylesheet(url, - mq, - pending, - link_element, - possibly_locked_rw_data) + Msg::AddStylesheet(style_info) => { + self.handle_add_stylesheet(style_info, possibly_locked_rw_data) } Msg::SetQuirksMode => self.handle_set_quirks_mode(possibly_locked_rw_data), - Msg::AddMetaViewport(translated_rule) => { - self.handle_add_meta_viewport(translated_rule, possibly_locked_rw_data) - } Msg::GetRPC(response_chan) => { response_chan.send(box LayoutRPCImpl(self.rw_data.clone()) as Box<LayoutRPC + Send>).unwrap(); @@ -745,75 +730,31 @@ impl LayoutTask { response_port.recv().unwrap() } - fn handle_load_stylesheet<'a>(&'a self, - url: Url, - mq: MediaQueryList, - pending: PendingAsyncLoad, - responder: Box<StylesheetLoadResponder + Send>, - possibly_locked_rw_data: - &mut Option<MutexGuard<'a, LayoutTaskData>>) { - // TODO: Get the actual value. http://dev.w3.org/csswg/css-syntax/#environment-encoding - let environment_encoding = UTF_8 as EncodingRef; - - // TODO we don't really even need to load this if mq does not match - let (metadata, iter) = load_bytes_iter(pending); - let protocol_encoding_label = metadata.charset.as_ref().map(|s| &**s); - let final_url = metadata.final_url; - - let sheet = Stylesheet::from_bytes_iter(iter, - final_url, - protocol_encoding_label, - Some(environment_encoding), - Origin::Author); - - //TODO: mark critical subresources as blocking load as well (#5974) - self.script_chan.send(ConstellationControlMsg::StylesheetLoadComplete(self.id, - url, - responder)).unwrap(); - - self.handle_add_stylesheet(sheet, mq, possibly_locked_rw_data); - } - fn handle_add_stylesheet<'a>(&'a self, - sheet: Stylesheet, - mq: MediaQueryList, + stylesheet: Arc<Stylesheet>, possibly_locked_rw_data: &mut Option<MutexGuard<'a, LayoutTaskData>>) { // Find all font-face rules and notify the font cache of them. - // GWTODO: Need to handle unloading web fonts (when we handle unloading stylesheets!) + // GWTODO: Need to handle unloading web fonts. - let mut rw_data = self.lock_rw_data(possibly_locked_rw_data); - if mq.evaluate(&rw_data.stylist.device) { - add_font_face_rules(&sheet, + let rw_data = self.lock_rw_data(possibly_locked_rw_data); + if stylesheet.is_effective_for_device(&rw_data.stylist.device) { + add_font_face_rules(&*stylesheet, &rw_data.stylist.device, &self.font_cache_task, &self.font_cache_sender, &rw_data.outstanding_web_fonts); - rw_data.stylist.add_stylesheet(sheet); } LayoutTask::return_rw_data(possibly_locked_rw_data, rw_data); } - fn handle_add_meta_viewport<'a>(&'a self, - translated_rule: ViewportRule, - possibly_locked_rw_data: - &mut Option<MutexGuard<'a, LayoutTaskData>>) - { - let mut rw_data = self.lock_rw_data(possibly_locked_rw_data); - rw_data.stylist.add_stylesheet(Stylesheet { - rules: vec![CSSRule::Viewport(translated_rule)], - origin: Origin::Author - }); - LayoutTask::return_rw_data(possibly_locked_rw_data, rw_data); - } - - /// Sets quirks mode for the document, causing the quirks mode stylesheet to be loaded. + /// Sets quirks mode for the document, causing the quirks mode stylesheet to be used. fn handle_set_quirks_mode<'a>(&'a self, possibly_locked_rw_data: &mut Option<MutexGuard<'a, LayoutTaskData>>) { let mut rw_data = self.lock_rw_data(possibly_locked_rw_data); - rw_data.stylist.add_quirks_mode_stylesheet(); + rw_data.stylist.set_quirks_mode(true); LayoutTask::return_rw_data(possibly_locked_rw_data, rw_data); } @@ -1077,7 +1018,7 @@ impl LayoutTask { flow_ref::deref_mut(layout_root)); let root_size = { let root_flow = flow::base(&**layout_root); - if rw_data.stylist.get_viewport_constraints().is_some() { + if rw_data.stylist.viewport_constraints().is_some() { root_flow.position.size.to_physical(root_flow.writing_mode) } else { root_flow.overflow.size @@ -1153,6 +1094,9 @@ impl LayoutTask { } let mut rw_data = self.lock_rw_data(possibly_locked_rw_data); + let stylesheets: Vec<&Stylesheet> = data.document_stylesheets.iter().map(|entry| &**entry) + .collect(); + let stylesheets_changed = data.stylesheets_changed; let initial_viewport = data.window_size.initial_viewport; let old_viewport_size = rw_data.viewport_size; @@ -1162,9 +1106,9 @@ impl LayoutTask { // Calculate the actual viewport as per DEVICE-ADAPT § 6 let device = Device::new(MediaType::Screen, initial_viewport); - rw_data.stylist.set_device(device); + rw_data.stylist.set_device(device, &stylesheets); - let constraints = rw_data.stylist.get_viewport_constraints(); + let constraints = rw_data.stylist.viewport_constraints().clone(); rw_data.viewport_size = match constraints { Some(ref constraints) => { debug!("Viewport constraints: {:?}", constraints); @@ -1180,9 +1124,6 @@ impl LayoutTask { let viewport_size_changed = rw_data.viewport_size != old_viewport_size; if viewport_size_changed { if let Some(constraints) = constraints { - let device = Device::new(MediaType::Screen, constraints.size); - rw_data.stylist.set_device(device); - // let the constellation know about the viewport constraints let ConstellationChan(ref constellation_chan) = rw_data.constellation_chan; constellation_chan.send(ConstellationMsg::ViewportConstrained( @@ -1191,7 +1132,7 @@ impl LayoutTask { } // If the entire flow tree is invalid, then it will be reflowed anyhow. - let needs_dirtying = rw_data.stylist.update(); + let needs_dirtying = rw_data.stylist.update(&stylesheets, stylesheets_changed); let needs_reflow = viewport_size_changed && !needs_dirtying; unsafe { if needs_dirtying { @@ -1221,7 +1162,7 @@ impl LayoutTask { &self.url, data.reflow_info.goal); - if node.is_dirty() || node.has_dirty_descendants() || rw_data.stylist.is_dirty() { + if node.is_dirty() || node.has_dirty_descendants() { // Recalculate CSS styles and rebuild flows and fragments. profile(time::ProfilerCategory::LayoutStyleRecalc, self.profiler_metadata(), diff --git a/components/script/dom/create.rs b/components/script/dom/create.rs index 4e2b07399b1..2f1d35d3b59 100644 --- a/components/script/dom/create.rs +++ b/components/script/dom/create.rs @@ -174,7 +174,7 @@ pub fn create_element(name: QualName, prefix: Option<Atom>, atom!("label") => make!(HTMLLabelElement), atom!("legend") => make!(HTMLLegendElement), atom!("li") => make!(HTMLLIElement), - atom!("link") => make!(HTMLLinkElement), + atom!("link") => make!(HTMLLinkElement, creator), // https://html.spec.whatwg.org/multipage/#other-elements,-attributes-and-apis:listing atom!("listing") => make!(HTMLPreElement), atom!("main") => make!(HTMLElement), diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index b39adbc53fc..4d5fb5e2ccd 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -48,7 +48,10 @@ use dom::htmlheadelement::HTMLHeadElement; use dom::htmlhtmlelement::HTMLHtmlElement; use dom::htmliframeelement::{self, HTMLIFrameElement}; use dom::htmlimageelement::HTMLImageElement; +use dom::htmllinkelement::HTMLLinkElement; +use dom::htmlmetaelement::HTMLMetaElement; use dom::htmlscriptelement::HTMLScriptElement; +use dom::htmlstyleelement::HTMLStyleElement; use dom::htmltitleelement::HTMLTitleElement; use dom::keyboardevent::KeyboardEvent; use dom::location::Location; @@ -96,8 +99,10 @@ use std::default::Default; use std::iter::FromIterator; use std::ptr; use std::rc::Rc; +use std::sync::Arc; use std::sync::mpsc::channel; use string_cache::{Atom, QualName}; +use style::stylesheets::Stylesheet; use time; use url::Url; use util::str::{DOMString, split_html_space_chars, str_join}; @@ -135,6 +140,10 @@ pub struct Document { scripts: MutNullableHeap<JS<HTMLCollection>>, anchors: MutNullableHeap<JS<HTMLCollection>>, applets: MutNullableHeap<JS<HTMLCollection>>, + /// List of stylesheets associated with nodes in this document. |None| if the list needs to be refreshed. + stylesheets: DOMRefCell<Option<Vec<Arc<Stylesheet>>>>, + /// Whether the list of stylesheets has changed since the last reflow was triggered. + stylesheets_changed_since_reflow: Cell<bool>, ready_state: Cell<DocumentReadyState>, /// Whether the DOMContentLoaded event has already been dispatched. domcontentloaded_dispatched: Cell<bool>, @@ -983,6 +992,21 @@ impl Document { count_cell.set(count_cell.get() - 1); } + pub fn invalidate_stylesheets(&self) { + self.stylesheets_changed_since_reflow.set(true); + *self.stylesheets.borrow_mut() = None; + // Mark the document element dirty so a reflow will be performed. + self.get_html_element().map(|root| { + root.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 set_pending_parsing_blocking_script(&self, script: Option<&HTMLScriptElement>) { assert!(self.get_pending_parsing_blocking_script().is_none() || script.is_none()); self.pending_parsing_blocking_script.set(script); @@ -1100,6 +1124,13 @@ impl Document { if parser.is_suspended() { parser.resume(); } + } else if self.reflow_timeout.get().is_none() { + // If we don't have a parser, and the reflow timer has been reset, explicitly + // trigger a reflow. + if let LoadType::Stylesheet(_) = load { + self.window().reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, + ReflowReason::StylesheetLoaded); + } } let loader = self.loader.borrow(); @@ -1304,6 +1335,8 @@ impl Document { scripts: Default::default(), anchors: Default::default(), applets: Default::default(), + stylesheets: DOMRefCell::new(None), + stylesheets_changed_since_reflow: Cell::new(false), ready_state: Cell::new(ready_state), domcontentloaded_dispatched: Cell::new(domcontentloaded_dispatched), possibly_focused: Default::default(), @@ -1369,6 +1402,31 @@ impl Document { self.GetDocumentElement().and_then(Root::downcast) } + /// Returns the list of stylesheets associated with nodes in the document. + pub fn stylesheets(&self) -> Ref<Vec<Arc<Stylesheet>>> { + { + let mut stylesheets = self.stylesheets.borrow_mut(); + if stylesheets.is_none() { + let new_stylesheets: Vec<Arc<Stylesheet>> = self.upcast::<Node>() + .traverse_preorder() + .filter_map(|node| { + if let Some(node) = node.downcast::<HTMLStyleElement>() { + node.get_stylesheet() + } else if let Some(node) = node.downcast::<HTMLLinkElement>() { + node.get_stylesheet() + } else if let Some(node) = node.downcast::<HTMLMetaElement>() { + node.get_stylesheet() + } else { + None + } + }) + .collect(); + *stylesheets = Some(new_stylesheets); + }; + } + Ref::map(self.stylesheets.borrow(), |t| t.as_ref().unwrap()) + } + /// https://html.spec.whatwg.org/multipage/#appropriate-template-contents-owner-document pub fn appropriate_template_contents_owner_document(&self) -> Root<Document> { self.appropriate_template_contents_owner_document.or_init(|| { diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs index 6fd36f60074..2860c98dd5c 100644 --- a/components/script/dom/htmllinkelement.rs +++ b/components/script/dom/htmllinkelement.rs @@ -5,55 +5,76 @@ use cssparser::Parser as CssParser; use document_loader::LoadType; use dom::attr::{Attr, AttrValue}; +use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::HTMLLinkElementBinding; use dom::bindings::codegen::Bindings::HTMLLinkElementBinding::HTMLLinkElementMethods; use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; -use dom::bindings::global::GlobalRef; use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, MutNullableHeap, Root}; use dom::bindings::js::{RootedReference}; use dom::bindings::refcounted::Trusted; use dom::document::Document; use dom::domtokenlist::DOMTokenList; -use dom::element::{AttributeMutation, Element}; -use dom::event::{Event, EventBubbles, EventCancelable}; -use dom::eventtarget::EventTarget; +use dom::element::{AttributeMutation, Element, ElementCreator}; use dom::htmlelement::HTMLElement; -use dom::node::{Node, window_from_node}; +use dom::node::{Node, document_from_node, window_from_node}; use dom::virtualmethods::VirtualMethods; +use encoding::EncodingRef; +use encoding::all::UTF_8; +use ipc_channel::ipc; +use ipc_channel::router::ROUTER; use layout_interface::{LayoutChan, Msg}; use msg::constellation_msg::ConstellationChan; use msg::constellation_msg::Msg as ConstellationMsg; -use script_traits::StylesheetLoadResponder; +use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata}; +use network_listener::{NetworkListener, PreInvoke}; use std::ascii::AsciiExt; use std::borrow::ToOwned; +use std::cell::Cell; use std::default::Default; +use std::mem; +use std::sync::{Arc, Mutex}; use string_cache::Atom; -use style::media_queries::parse_media_query_list; -use url::UrlParser; +use style::media_queries::{MediaQueryList, parse_media_query_list}; +use style::stylesheets::{Origin, Stylesheet}; +use url::{Url, UrlParser}; use util::str::{DOMString, HTML_SPACE_CHARACTERS}; +no_jsmanaged_fields!(Stylesheet); + #[dom_struct] pub struct HTMLLinkElement { htmlelement: HTMLElement, rel_list: MutNullableHeap<JS<DOMTokenList>>, + stylesheet: DOMRefCell<Option<Arc<Stylesheet>>>, + + /// https://html.spec.whatwg.org/multipage/#a-style-sheet-that-is-blocking-scripts + parser_inserted: Cell<bool>, } impl HTMLLinkElement { - fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: &Document) -> HTMLLinkElement { + fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: &Document, + creator: ElementCreator) -> HTMLLinkElement { HTMLLinkElement { htmlelement: HTMLElement::new_inherited(localName, prefix, document), rel_list: Default::default(), + parser_inserted: Cell::new(creator == ElementCreator::ParserCreated), + stylesheet: DOMRefCell::new(None), } } #[allow(unrooted_must_root)] pub fn new(localName: DOMString, prefix: Option<DOMString>, - document: &Document) -> Root<HTMLLinkElement> { - let element = HTMLLinkElement::new_inherited(localName, prefix, document); + document: &Document, + creator: ElementCreator) -> Root<HTMLLinkElement> { + let element = HTMLLinkElement::new_inherited(localName, prefix, document, creator); Node::reflect_node(box element, document, HTMLLinkElementBinding::Wrap) } + + pub fn get_stylesheet(&self) -> Option<Arc<Stylesheet>> { + self.stylesheet.borrow().clone() + } } fn get_attr(element: &Element, local_name: &Atom) -> Option<String> { @@ -64,7 +85,7 @@ fn get_attr(element: &Element, local_name: &Atom) -> Option<String> { }) } -fn is_stylesheet(value: &Option<String>) -> bool { +fn string_is_stylesheet(value: &Option<String>) -> bool { match *value { Some(ref value) => { value.split(HTML_SPACE_CHARACTERS) @@ -100,14 +121,14 @@ impl VirtualMethods for HTMLLinkElement { let rel = get_attr(self.upcast(), &atom!(rel)); match attr.local_name() { &atom!(href) => { - if is_stylesheet(&rel) { + if string_is_stylesheet(&rel) { self.handle_stylesheet_url(&attr.value()); } else if is_favicon(&rel) { self.handle_favicon_url(&attr.value()); } }, &atom!(media) => { - if is_stylesheet(&rel) { + if string_is_stylesheet(&rel) { self.handle_stylesheet_url(&attr.value()); } }, @@ -134,7 +155,7 @@ impl VirtualMethods for HTMLLinkElement { let href = get_attr(element, &atom!("href")); match (rel, href) { - (ref rel, Some(ref href)) if is_stylesheet(rel) => { + (ref rel, Some(ref href)) if string_is_stylesheet(rel) => { self.handle_stylesheet_url(href); } (ref rel, Some(ref href)) if is_favicon(rel) => { @@ -164,13 +185,35 @@ impl HTMLLinkElement { let mut css_parser = CssParser::new(&mq_str); let media = parse_media_query_list(&mut css_parser); + // TODO: #8085 - Don't load external stylesheets if the node's mq doesn't match. let doc = window.Document(); - let link_element = Trusted::new(window.get_cx(), self, window.script_chan().clone()); - let load_dispatcher = StylesheetLoadDispatcher::new(link_element); + let script_chan = window.script_chan(); + let elem = Trusted::new(window.get_cx(), self, script_chan.clone()); + + let context = Arc::new(Mutex::new(StylesheetContext { + elem: elem, + media: Some(media), + data: vec!(), + metadata: None, + url: url.clone(), + })); + + let (action_sender, action_receiver) = ipc::channel().unwrap(); + let listener = NetworkListener { + context: context, + script_chan: script_chan, + }; + let response_target = AsyncResponseTarget { + sender: action_sender, + }; + ROUTER.add_route(action_receiver.to_opaque(), box move |message| { + listener.notify(message.to().unwrap()); + }); - let pending = doc.prepare_async_load(LoadType::Stylesheet(url.clone())); - let LayoutChan(ref layout_chan) = window.layout_chan(); - layout_chan.send(Msg::LoadStylesheet(url, media, pending, box load_dispatcher)).unwrap(); + if self.parser_inserted.get() { + doc.increment_script_blocking_stylesheet_count(); + } + doc.load_async(LoadType::Stylesheet(url), response_target); } Err(e) => debug!("Parsing url {} failed: {}", href, e) } @@ -190,6 +233,62 @@ impl HTMLLinkElement { } } +/// The context required for asynchronously loading an external stylesheet. +struct StylesheetContext { + /// The element that initiated the request. + elem: Trusted<HTMLLinkElement>, + media: Option<MediaQueryList>, + /// The response body received to date. + data: Vec<u8>, + /// The response metadata received to date. + metadata: Option<Metadata>, + /// The initial URL requested. + url: Url, +} + +impl PreInvoke for StylesheetContext {} + +impl AsyncResponseListener for StylesheetContext { + fn headers_available(&mut self, metadata: Metadata) { + self.metadata = Some(metadata); + } + + fn data_available(&mut self, payload: Vec<u8>) { + let mut payload = payload; + self.data.append(&mut payload); + } + + fn response_complete(&mut self, _status: Result<(), String>) { + let data = mem::replace(&mut self.data, vec!()); + let metadata = self.metadata.take().unwrap(); + // TODO: Get the actual value. http://dev.w3.org/csswg/css-syntax/#environment-encoding + let environment_encoding = UTF_8 as EncodingRef; + let protocol_encoding_label = metadata.charset.as_ref().map(|s| &**s); + let final_url = metadata.final_url; + let mut sheet = Stylesheet::from_bytes(&data, final_url, protocol_encoding_label, + Some(environment_encoding), Origin::Author); + let media = self.media.take().unwrap(); + sheet.set_media(Some(media)); + let sheet = Arc::new(sheet); + + let elem = self.elem.root(); + let elem = elem.r(); + let document = document_from_node(elem); + let document = document.r(); + + let win = window_from_node(elem); + let LayoutChan(ref layout_chan) = win.r().layout_chan(); + layout_chan.send(Msg::AddStylesheet(sheet.clone())).unwrap(); + + *elem.stylesheet.borrow_mut() = Some(sheet); + document.invalidate_stylesheets(); + if elem.parser_inserted.get() { + document.decrement_script_blocking_stylesheet_count(); + } + document.finish_load(LoadType::Stylesheet(self.url.clone())); + } +} + impl HTMLLinkElementMethods for HTMLLinkElement { // https://html.spec.whatwg.org/multipage/#dom-link-href make_url_getter!(Href); @@ -244,27 +343,3 @@ impl HTMLLinkElementMethods for HTMLLinkElement { // https://html.spec.whatwg.org/multipage/#dom-link-target make_setter!(SetTarget, "target"); } - -pub struct StylesheetLoadDispatcher { - elem: Trusted<HTMLLinkElement>, -} - -impl StylesheetLoadDispatcher { - pub fn new(elem: Trusted<HTMLLinkElement>) -> StylesheetLoadDispatcher { - StylesheetLoadDispatcher { - elem: elem, - } - } -} - -impl StylesheetLoadResponder for StylesheetLoadDispatcher { - fn respond(self: Box<StylesheetLoadDispatcher>) { - let elem = self.elem.root(); - let window = window_from_node(elem.r()); - let event = Event::new(GlobalRef::Window(window.r()), - DOMString("load".to_owned()), - EventBubbles::DoesNotBubble, - EventCancelable::NotCancelable); - event.fire(elem.upcast::<EventTarget>()); - } -} diff --git a/components/script/dom/htmlmetaelement.rs b/components/script/dom/htmlmetaelement.rs index e615a0efbf7..dd0245104cb 100644 --- a/components/script/dom/htmlmetaelement.rs +++ b/components/script/dom/htmlmetaelement.rs @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::HTMLMetaElementBinding; use dom::bindings::codegen::Bindings::HTMLMetaElementBinding::HTMLMetaElementMethods; use dom::bindings::inheritance::Castable; @@ -9,16 +10,18 @@ use dom::bindings::js::{Root, RootedReference}; use dom::document::Document; use dom::element::Element; use dom::htmlelement::HTMLElement; -use dom::node::{Node, window_from_node}; +use dom::node::{Node, document_from_node}; use dom::virtualmethods::VirtualMethods; -use layout_interface::{LayoutChan, Msg}; use std::ascii::AsciiExt; +use std::sync::Arc; +use style::stylesheets::{CSSRule, Origin, Stylesheet}; use style::viewport::ViewportRule; use util::str::{DOMString, HTML_SPACE_CHARACTERS}; #[dom_struct] pub struct HTMLMetaElement { htmlelement: HTMLElement, + stylesheet: DOMRefCell<Option<Arc<Stylesheet>>>, } impl HTMLMetaElement { @@ -26,7 +29,8 @@ impl HTMLMetaElement { prefix: Option<DOMString>, document: &Document) -> HTMLMetaElement { HTMLMetaElement { - htmlelement: HTMLElement::new_inherited(localName, prefix, document) + htmlelement: HTMLElement::new_inherited(localName, prefix, document), + stylesheet: DOMRefCell::new(None), } } @@ -38,6 +42,10 @@ impl HTMLMetaElement { Node::reflect_node(box element, document, HTMLMetaElementBinding::Wrap) } + pub fn get_stylesheet(&self) -> Option<Arc<Stylesheet>> { + self.stylesheet.borrow().clone() + } + fn process_attributes(&self) { let element = self.upcast::<Element>(); if let Some(name) = element.get_attribute(&ns!(""), &atom!("name")).r() { @@ -45,22 +53,25 @@ impl HTMLMetaElement { let name = name.trim_matches(HTML_SPACE_CHARACTERS); match name { - "viewport" => self.translate_viewport(), + "viewport" => self.apply_viewport(), _ => {} } } } - fn translate_viewport(&self) { + fn apply_viewport(&self) { let element = self.upcast::<Element>(); if let Some(content) = element.get_attribute(&ns!(""), &atom!("content")).r() { let content = content.value(); if !content.is_empty() { if let Some(translated_rule) = ViewportRule::from_meta(&**content) { - let win = window_from_node(self); - let LayoutChan(ref layout_chan) = win.layout_chan(); - - layout_chan.send(Msg::AddMetaViewport(translated_rule)).unwrap(); + *self.stylesheet.borrow_mut() = Some(Arc::new(Stylesheet { + rules: vec![CSSRule::Viewport(translated_rule)], + origin: Origin::Author, + media: None, + })); + let doc = document_from_node(self); + doc.invalidate_stylesheets(); } } } diff --git a/components/script/dom/htmlstyleelement.rs b/components/script/dom/htmlstyleelement.rs index d1ac6dcc37b..e225ec33981 100644 --- a/components/script/dom/htmlstyleelement.rs +++ b/components/script/dom/htmlstyleelement.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use cssparser::Parser as CssParser; +use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::HTMLStyleElementBinding; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use dom::bindings::inheritance::Castable; @@ -10,9 +11,10 @@ use dom::bindings::js::Root; use dom::document::Document; use dom::element::Element; use dom::htmlelement::HTMLElement; -use dom::node::{ChildrenMutation, Node, window_from_node}; +use dom::node::{ChildrenMutation, Node, document_from_node, window_from_node}; use dom::virtualmethods::VirtualMethods; use layout_interface::{LayoutChan, Msg}; +use std::sync::Arc; use style::media_queries::parse_media_query_list; use style::stylesheets::{Origin, Stylesheet}; use util::str::DOMString; @@ -20,6 +22,7 @@ use util::str::DOMString; #[dom_struct] pub struct HTMLStyleElement { htmlelement: HTMLElement, + stylesheet: DOMRefCell<Option<Arc<Stylesheet>>>, } impl HTMLStyleElement { @@ -27,7 +30,8 @@ impl HTMLStyleElement { prefix: Option<DOMString>, document: &Document) -> HTMLStyleElement { HTMLStyleElement { - htmlelement: HTMLElement::new_inherited(localName, prefix, document) + htmlelement: HTMLElement::new_inherited(localName, prefix, document), + stylesheet: DOMRefCell::new(None), } } @@ -52,13 +56,23 @@ impl HTMLStyleElement { Some(a) => String::from(&**a.value()), None => String::new(), }; + + let data = node.GetTextContent().expect("Element.textContent must be a string"); + let mut sheet = Stylesheet::from_str(&data, url, Origin::Author); let mut css_parser = CssParser::new(&mq_str); let media = parse_media_query_list(&mut css_parser); + sheet.set_media(Some(media)); + let sheet = Arc::new(sheet); - let data = node.GetTextContent().expect("Element.textContent must be a string"); - let sheet = Stylesheet::from_str(&data, url, Origin::Author); let LayoutChan(ref layout_chan) = win.layout_chan(); - layout_chan.send(Msg::AddStylesheet(sheet, media)).unwrap(); + layout_chan.send(Msg::AddStylesheet(sheet.clone())).unwrap(); + *self.stylesheet.borrow_mut() = Some(sheet); + let doc = document_from_node(self); + doc.r().invalidate_stylesheets(); + } + + pub fn get_stylesheet(&self) -> Option<Arc<Stylesheet>> { + self.stylesheet.borrow().clone() } } diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index e296cea77d5..e50674c222c 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -99,6 +99,7 @@ pub enum ReflowReason { WindowResize, DOMContentLoaded, DocumentLoaded, + StylesheetLoaded, ImageLoaded, RequestAnimationFrame, WebFontLoaded, @@ -915,6 +916,9 @@ impl Window { debug_reflow_events(&goal, &query_type, &reason); } + let document = self.Document(); + let stylesheets_changed = document.get_and_reset_stylesheets_changed_since_reflow(); + // Send new document and relevant styles to layout. let reflow = ScriptReflow { reflow_info: Reflow { @@ -922,6 +926,8 @@ impl Window { page_clip_rect: self.page_clip_rect.get(), }, document: self.Document().upcast::<Node>().to_trusted_node_address(), + document_stylesheets: document.stylesheets().clone(), + stylesheets_changed: stylesheets_changed, window_size: window_size, script_join_chan: join_chan, query_type: query_type, @@ -1325,6 +1331,7 @@ fn debug_reflow_events(goal: &ReflowGoal, query_type: &ReflowQueryType, reason: ReflowReason::WindowResize => "\tWindowResize", ReflowReason::DOMContentLoaded => "\tDOMContentLoaded", ReflowReason::DocumentLoaded => "\tDocumentLoaded", + ReflowReason::StylesheetLoaded => "\tStylesheetLoaded", ReflowReason::ImageLoaded => "\tImageLoaded", ReflowReason::RequestAnimationFrame => "\tRequestAnimationFrame", ReflowReason::WebFontLoaded => "\tWebFontLoaded", diff --git a/components/script/layout_interface.rs b/components/script/layout_interface.rs index afc7adedbd3..f6d96f1f09f 100644 --- a/components/script/layout_interface.rs +++ b/components/script/layout_interface.rs @@ -16,32 +16,24 @@ use msg::compositor_msg::Epoch; use msg::compositor_msg::LayerId; use msg::constellation_msg::{ConstellationChan, Failure, PipelineExitType, PipelineId}; use msg::constellation_msg::{WindowSizeData}; -use net_traits::PendingAsyncLoad; use net_traits::image_cache_task::ImageCacheTask; use profile_traits::mem::ReportsChan; use script_traits::{ConstellationControlMsg, LayoutControlMsg}; -use script_traits::{OpaqueScriptLayoutChannel, StylesheetLoadResponder, UntrustedNodeAddress}; +use script_traits::{OpaqueScriptLayoutChannel, UntrustedNodeAddress}; use selectors::parser::PseudoElement; use std::any::Any; +use std::sync::Arc; use std::sync::mpsc::{Receiver, Sender, channel}; use string_cache::Atom; use style::animation::PropertyAnimation; -use style::media_queries::MediaQueryList; use style::stylesheets::Stylesheet; -use style::viewport::ViewportRule; use url::Url; pub use dom::node::TrustedNodeAddress; /// Asynchronous messages that script can send to layout. pub enum Msg { /// Adds the given stylesheet to the document. - AddStylesheet(Stylesheet, MediaQueryList), - - /// Adds the given stylesheet to the document. - LoadStylesheet(Url, MediaQueryList, PendingAsyncLoad, Box<StylesheetLoadResponder + Send>), - - /// Adds a @viewport rule (translated from a <META name="viewport"> element) to the document. - AddMetaViewport(ViewportRule), + AddStylesheet(Arc<Stylesheet>), /// Puts a document into quirks mode, causing the quirks mode stylesheet to be loaded. SetQuirksMode, @@ -175,6 +167,10 @@ pub struct ScriptReflow { pub reflow_info: Reflow, /// The document node. pub document: TrustedNodeAddress, + /// The document's list of stylesheets. + pub document_stylesheets: Vec<Arc<Stylesheet>>, + /// Whether the document's stylesheets have changed since the last script reflow. + pub stylesheets_changed: bool, /// The current window size. pub window_size: WindowSizeData, /// The channel that we send a notification to. diff --git a/components/script/script_task.rs b/components/script/script_task.rs index dda6faaa85b..c343dd677f6 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -20,7 +20,7 @@ use devtools; use devtools_traits::ScriptToDevtoolsControlMsg; use devtools_traits::{DevtoolScriptControlMsg, DevtoolsPageInfo}; -use document_loader::{DocumentLoader, LoadType}; +use document_loader::DocumentLoader; use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, DocumentReadyState}; use dom::bindings::conversions::{FromJSValConvertible, StringificationBehavior}; @@ -1001,10 +1001,6 @@ impl ScriptTask { self.handle_tick_all_animations(pipeline_id), ConstellationControlMsg::WebFontLoaded(pipeline_id) => self.handle_web_font_loaded(pipeline_id), - ConstellationControlMsg::StylesheetLoadComplete(id, url, responder) => { - responder.respond(); - self.handle_resource_loaded(id, LoadType::Stylesheet(url)); - } ConstellationControlMsg::GetCurrentState(sender, pipeline_id) => { let state = self.handle_get_current_state(pipeline_id); sender.send(state).unwrap(); @@ -1153,13 +1149,6 @@ impl ScriptTask { panic!("Page rect message sent to nonexistent pipeline"); } - /// Handle a request to load a page in a new child frame of an existing page. - fn handle_resource_loaded(&self, pipeline: PipelineId, load: LoadType) { - let page = get_page(&self.root_page(), pipeline); - let doc = page.document(); - doc.finish_load(load); - } - /// Get the current state of a given pipeline. fn handle_get_current_state(&self, pipeline_id: PipelineId) -> ScriptState { // Check if the main page load is still pending diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index 827a729e236..f702884db01 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -41,7 +41,6 @@ use net_traits::storage_task::StorageTask; use profile_traits::mem; use std::any::Any; use std::sync::mpsc::{Receiver, Sender}; -use url::Url; use util::mem::HeapSizeOf; /// The address of a node. Layout sends these back. They must be validated via @@ -90,13 +89,6 @@ pub struct NewLayoutInfo { pub layout_shutdown_chan: Sender<()>, } -/// `StylesheetLoadResponder` is used to notify a responder that a style sheet -/// has loaded. -pub trait StylesheetLoadResponder { - /// Respond to a loaded style sheet. - fn respond(self: Box<Self>); -} - /// Used to determine if a script has any pending asynchronous activity. #[derive(Copy, Clone, Debug, PartialEq)] pub enum ScriptState { @@ -141,8 +133,6 @@ pub enum ConstellationControlMsg { /// Notifies the script task that a new Web font has been loaded, and thus the page should be /// reflowed. WebFontLoaded(PipelineId), - /// Notifies script that a stylesheet has finished loading. - StylesheetLoadComplete(PipelineId, Url, Box<StylesheetLoadResponder + Send>), /// Get the current state of the script task for a given pipeline. GetCurrentState(Sender<ScriptState>, PipelineId), } diff --git a/components/style/selector_matching.rs b/components/style/selector_matching.rs index 5c66a05aa1d..fe7d4621821 100644 --- a/components/style/selector_matching.rs +++ b/components/style/selector_matching.rs @@ -56,19 +56,37 @@ lazy_static! { }; } -pub struct Stylist { - // List of stylesheets (including all media rules) - stylesheets: Vec<Stylesheet>, +lazy_static! { + pub static ref QUIRKS_MODE_STYLESHEET: Stylesheet = { + match read_resource_file(&["quirks-mode.css"]) { + Ok(res) => { + Stylesheet::from_bytes( + &res, + Url::parse("chrome:///quirks-mode.css").unwrap(), + None, + None, + Origin::UserAgent) + }, + Err(..) => { + error!("Stylist failed to load 'quirks-mode.css'!"); + process::exit(1); + } + } + }; +} +pub struct Stylist { // Device that the stylist is currently evaluating against. pub device: Device, // Viewport constraints based on the current device. viewport_constraints: Option<ViewportConstraints>, - // If true, a stylesheet has been added or the device has - // changed, and the stylist needs to be updated. - is_dirty: bool, + // If true, the quirks-mode stylesheet is applied. + quirks_mode: bool, + + // If true, the device has changed, and the stylist needs to be updated. + is_device_dirty: bool, // The current selector maps, after evaluating media // rules against the current device. @@ -85,10 +103,10 @@ impl Stylist { #[inline] pub fn new(device: Device) -> Stylist { let stylist = Stylist { - stylesheets: vec!(), viewport_constraints: None, device: device, - is_dirty: true, + is_device_dirty: true, + quirks_mode: false, element_map: PerPseudoElementSelectorMap::new(), before_map: PerPseudoElementSelectorMap::new(), @@ -100,79 +118,90 @@ impl Stylist { stylist } - #[inline] - pub fn stylesheets(&self) -> &[Stylesheet] { - &self.stylesheets + pub fn update(&mut self, doc_stylesheets: &[&Stylesheet], + stylesheets_changed: bool) -> bool { + if !(self.is_device_dirty || stylesheets_changed) { + return false; + } + self.element_map = PerPseudoElementSelectorMap::new(); + self.before_map = PerPseudoElementSelectorMap::new(); + self.after_map = PerPseudoElementSelectorMap::new(); + self.rules_source_order = 0; + self.state_deps.clear(); + + for ref stylesheet in USER_OR_USER_AGENT_STYLESHEETS.iter() { + self.add_stylesheet(&stylesheet); + } + + if self.quirks_mode { + self.add_stylesheet(&QUIRKS_MODE_STYLESHEET); + } + + for ref stylesheet in doc_stylesheets.iter() { + self.add_stylesheet(stylesheet); + } + + self.is_device_dirty = false; + true } - pub fn update(&mut self) -> bool { - if self.is_dirty { - self.element_map = PerPseudoElementSelectorMap::new(); - self.before_map = PerPseudoElementSelectorMap::new(); - self.after_map = PerPseudoElementSelectorMap::new(); - self.rules_source_order = 0; - self.state_deps.clear(); - - for stylesheet in USER_OR_USER_AGENT_STYLESHEETS.iter().chain(&self.stylesheets) { - let (mut element_map, mut before_map, mut after_map) = match stylesheet.origin { - Origin::UserAgent => ( - &mut self.element_map.user_agent, - &mut self.before_map.user_agent, - &mut self.after_map.user_agent, - ), - Origin::Author => ( - &mut self.element_map.author, - &mut self.before_map.author, - &mut self.after_map.author, - ), - Origin::User => ( - &mut self.element_map.user, - &mut self.before_map.user, - &mut self.after_map.user, - ), - }; - let mut rules_source_order = self.rules_source_order; - - // Take apart the StyleRule into individual Rules and insert - // them into the SelectorMap of that priority. - macro_rules! append( - ($style_rule: ident, $priority: ident) => { - if $style_rule.declarations.$priority.len() > 0 { - for selector in &$style_rule.selectors { - let map = match selector.pseudo_element { - None => &mut element_map, - Some(PseudoElement::Before) => &mut before_map, - Some(PseudoElement::After) => &mut after_map, - }; - map.$priority.insert(Rule { - selector: selector.compound_selectors.clone(), - declarations: DeclarationBlock { - specificity: selector.specificity, - declarations: $style_rule.declarations.$priority.clone(), - source_order: rules_source_order, - }, - }); - } - } - }; - ); - - for style_rule in stylesheet.effective_rules(&self.device).style() { - append!(style_rule, normal); - append!(style_rule, important); - rules_source_order += 1; - for selector in &style_rule.selectors { - self.state_deps.note_selector(selector.compound_selectors.clone()); + fn add_stylesheet(&mut self, stylesheet: &Stylesheet) { + let device = &self.device; + if !stylesheet.is_effective_for_device(device) { + return; + } + let (mut element_map, mut before_map, mut after_map) = match stylesheet.origin { + Origin::UserAgent => ( + &mut self.element_map.user_agent, + &mut self.before_map.user_agent, + &mut self.after_map.user_agent, + ), + Origin::Author => ( + &mut self.element_map.author, + &mut self.before_map.author, + &mut self.after_map.author, + ), + Origin::User => ( + &mut self.element_map.user, + &mut self.before_map.user, + &mut self.after_map.user, + ), + }; + let mut rules_source_order = self.rules_source_order; + + // Take apart the StyleRule into individual Rules and insert + // them into the SelectorMap of that priority. + macro_rules! append( + ($style_rule: ident, $priority: ident) => { + if $style_rule.declarations.$priority.len() > 0 { + for selector in &$style_rule.selectors { + let map = match selector.pseudo_element { + None => &mut element_map, + Some(PseudoElement::Before) => &mut before_map, + Some(PseudoElement::After) => &mut after_map, + }; + map.$priority.insert(Rule { + selector: selector.compound_selectors.clone(), + declarations: DeclarationBlock { + specificity: selector.specificity, + declarations: $style_rule.declarations.$priority.clone(), + source_order: rules_source_order, + }, + }); } } - self.rules_source_order = rules_source_order; + }; + ); + + for style_rule in stylesheet.effective_rules(&self.device).style() { + append!(style_rule, normal); + append!(style_rule, important); + rules_source_order += 1; + for selector in &style_rule.selectors { + self.state_deps.note_selector(selector.compound_selectors.clone()); } - - self.is_dirty = false; - return true; } - - false + self.rules_source_order = rules_source_order; } pub fn restyle_hint_for_state_change<E>(&self, element: &E, @@ -183,8 +212,8 @@ impl Stylist { self.state_deps.compute_hint(element, current_state, state_change) } - pub fn set_device(&mut self, mut device: Device) { - let cascaded_rule = self.stylesheets.iter() + pub fn set_device(&mut self, mut device: Device, stylesheets: &[&Stylesheet]) { + let cascaded_rule = stylesheets.iter() .flat_map(|s| s.effective_rules(&self.device).viewport()) .cascade(); @@ -192,41 +221,20 @@ impl Stylist { if let Some(ref constraints) = self.viewport_constraints { device = Device::new(MediaType::Screen, constraints.size); } - let is_dirty = self.is_dirty || self.stylesheets.iter() + let is_device_dirty = self.is_device_dirty || stylesheets.iter() .flat_map(|stylesheet| stylesheet.rules().media()) .any(|media_rule| media_rule.evaluate(&self.device) != media_rule.evaluate(&device)); self.device = device; - self.is_dirty |= is_dirty; + self.is_device_dirty |= is_device_dirty; } - pub fn get_viewport_constraints(&self) -> Option<ViewportConstraints> { - match self.viewport_constraints { - Some(ref constraints) => Some(constraints.clone()), - None => None - } - } - - pub fn add_quirks_mode_stylesheet(&mut self) { - match read_resource_file(&["quirks-mode.css"]) { - Ok(res) => { - self.add_stylesheet(Stylesheet::from_bytes( - &res, - Url::parse("chrome:///quirks-mode.css").unwrap(), - None, - None, - Origin::UserAgent)); - } - Err(..) => { - error!("Stylist::add_quirks_mode_stylesheet() failed at loading 'quirks-mode.css'!"); - process::exit(1); - } - } + pub fn viewport_constraints(&self) -> &Option<ViewportConstraints> { + &self.viewport_constraints } - pub fn add_stylesheet(&mut self, stylesheet: Stylesheet) { - self.stylesheets.push(stylesheet); - self.is_dirty = true; + pub fn set_quirks_mode(&mut self, enabled: bool) { + self.quirks_mode = enabled; } /// Returns the applicable CSS declarations for the given element. This corresponds to @@ -245,7 +253,7 @@ impl Stylist { -> bool where E: Element + TElementAttributes, V: VecLike<DeclarationBlock> { - assert!(!self.is_dirty); + assert!(!self.is_device_dirty); assert!(style_attribute.is_none() || pseudo_element.is_none(), "Style attributes do not apply to pseudo-elements"); @@ -312,8 +320,8 @@ impl Stylist { shareable } - pub fn is_dirty(&self) -> bool { - self.is_dirty + pub fn is_device_dirty(&self) -> bool { + self.is_device_dirty } } diff --git a/components/style/stylesheets.rs b/components/style/stylesheets.rs index 0b1decedc31..00e9def4900 100644 --- a/components/style/stylesheets.rs +++ b/components/style/stylesheets.rs @@ -41,6 +41,8 @@ pub struct Stylesheet { /// List of rules in the order they were found (important for /// cascading order) pub rules: Vec<CSSRule>, + /// List of media associated with the Stylesheet, if any. + pub media: Option<MediaQueryList>, pub origin: Origin, } @@ -132,6 +134,23 @@ impl Stylesheet { Stylesheet { origin: origin, rules: rules, + media: None, + } + } + + /// Set the MediaQueryList associated with the style-sheet. + pub fn set_media(&mut self, media: Option<MediaQueryList>) { + self.media = media; + } + + /// Returns whether the style-sheet applies for the current device depending + /// on the associated MediaQueryList. + /// + /// Always true if no associated MediaQueryList exists. + pub fn is_effective_for_device(&self, device: &Device) -> bool { + match self.media { + Some(ref media) => media.evaluate(device), + None => true } } diff --git a/components/style_traits/viewport.rs b/components/style_traits/viewport.rs index cead903301a..ac7c1a16be6 100644 --- a/components/style_traits/viewport.rs +++ b/components/style_traits/viewport.rs @@ -20,7 +20,7 @@ define_css_keyword_enum!(Orientation: "landscape" => Landscape); -#[derive(Clone, Debug, HeapSizeOf, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub struct ViewportConstraints { pub size: TypedSize2D<ViewportPx, f32>, diff --git a/tests/unit/style/stylesheets.rs b/tests/unit/style/stylesheets.rs index 6eb9cde4402..d6a9c6d30ce 100644 --- a/tests/unit/style/stylesheets.rs +++ b/tests/unit/style/stylesheets.rs @@ -25,6 +25,7 @@ fn test_parse_stylesheet() { let stylesheet = Stylesheet::from_str(css, url, Origin::UserAgent); assert_eq!(stylesheet, Stylesheet { origin: Origin::UserAgent, + media: None, rules: vec![ CSSRule::Namespace(None, ns!(HTML)), CSSRule::Style(StyleRule { diff --git a/tests/wpt/metadata-css/css21_dev/html4/cascade-import-dynamic-control.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/cascade-import-dynamic-control.htm.ini deleted file mode 100644 index f0a3cbaf277..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/cascade-import-dynamic-control.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[cascade-import-dynamic-control.htm] - type: reftest - expected: FAIL |