diff options
Diffstat (limited to 'components/script/dom')
-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 |
6 files changed, 224 insertions, 59 deletions
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", |