From 8c794c673999e7b2aef472d72663e27e0e448165 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Wed, 26 Mar 2014 10:15:29 -0700 Subject: [PATCH] Move inline stylesheet parsing out of HTML parser. Instead, use shared code to parse stylesheet content when it is inserted, whether during parsing or dynamically by script. Based on work by sanools in #1350. --- src/components/script/dom/element.rs | 6 +- src/components/script/dom/htmlstyleelement.rs | 23 +++++++- src/components/script/dom/node.rs | 15 ++++- src/components/script/html/cssparse.rs | 55 +++++++++++-------- .../script/html/hubbub_html_parser.rs | 22 ++------ src/test/ref/append_style_a.html | 1 + src/test/ref/append_style_b.html | 6 ++ src/test/ref/basic.list | 1 + 8 files changed, 83 insertions(+), 46 deletions(-) create mode 100644 src/test/ref/append_style_a.html create mode 100644 src/test/ref/append_style_b.html diff --git a/src/components/script/dom/element.rs b/src/components/script/dom/element.rs index b06e3253989..3abe8de8edb 100644 --- a/src/components/script/dom/element.rs +++ b/src/components/script/dom/element.rs @@ -12,17 +12,17 @@ use dom::bindings::js::JS; use dom::bindings::utils::{Reflectable, Reflector}; use dom::bindings::error::{ErrorResult, Fallible, NamespaceError, InvalidCharacter}; use dom::bindings::utils::{QName, Name, InvalidXMLName, xml_name_type}; -use dom::htmlcollection::HTMLCollection; use dom::clientrect::ClientRect; use dom::clientrectlist::ClientRectList; use dom::document::Document; use dom::eventtarget::{EventTarget, NodeTargetTypeId}; -use dom::node::{ElementNodeTypeId, Node, NodeHelpers, NodeIterator, document_from_node}; +use dom::htmlcollection::HTMLCollection; use dom::htmlserializer::serialize; +use dom::node::{ElementNodeTypeId, Node, NodeHelpers, NodeIterator, document_from_node}; use dom::virtualmethods::{VirtualMethods, vtable_for}; use layout_interface::{ContentBoxQuery, ContentBoxResponse, ContentBoxesQuery}; use layout_interface::{ContentBoxesResponse, ContentChangedDocumentDamage}; -use layout_interface::{MatchSelectorsDocumentDamage}; +use layout_interface::MatchSelectorsDocumentDamage; use style; use servo_util::namespace; use servo_util::namespace::{Namespace, Null}; diff --git a/src/components/script/dom/htmlstyleelement.rs b/src/components/script/dom/htmlstyleelement.rs index 8c09d6336c0..6596eaec932 100644 --- a/src/components/script/dom/htmlstyleelement.rs +++ b/src/components/script/dom/htmlstyleelement.rs @@ -3,14 +3,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use dom::bindings::codegen::HTMLStyleElementBinding; -use dom::bindings::codegen::InheritTypes::HTMLStyleElementDerived; +use dom::bindings::codegen::InheritTypes::{HTMLStyleElementDerived, NodeCast}; use dom::bindings::js::JS; use dom::bindings::error::ErrorResult; use dom::document::Document; use dom::element::HTMLStyleElementTypeId; use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::htmlelement::HTMLElement; -use dom::node::{Node, ElementNodeTypeId}; +use dom::node::{Node, ElementNodeTypeId, window_from_node}; +use html::cssparse::parse_inline_css; +use layout_interface::{AddStylesheetMsg, LayoutChan}; use servo_util::str::DOMString; #[deriving(Encodable)] @@ -72,3 +74,20 @@ impl HTMLStyleElement { Ok(()) } } + +pub trait StyleElementHelpers { + fn parse_own_css(&self); +} + +impl StyleElementHelpers for JS { + fn parse_own_css(&self) { + let node: JS = NodeCast::from(self); + let win = window_from_node(&node); + let url = win.get().page().get_url(); + + let data = node.get().GetTextContent(&node).expect("Element.textContent must be a string"); + let sheet = parse_inline_css(url, data); + let LayoutChan(ref layout_chan) = win.get().page().layout_chan; + layout_chan.send(AddStylesheetMsg(sheet)); + } +} diff --git a/src/components/script/dom/node.rs b/src/components/script/dom/node.rs index 733d1ad7f86..a8c7dbfe961 100644 --- a/src/components/script/dom/node.rs +++ b/src/components/script/dom/node.rs @@ -6,7 +6,7 @@ use dom::attr::Attr; use dom::bindings::codegen::InheritTypes::{CommentCast, DocumentCast, DocumentTypeCast}; -use dom::bindings::codegen::InheritTypes::{ElementCast, TextCast, NodeCast}; +use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLStyleElementCast, TextCast, NodeCast}; use dom::bindings::codegen::InheritTypes::{CharacterDataCast, NodeBase, NodeDerived}; use dom::bindings::codegen::InheritTypes::{ProcessingInstructionCast, EventTargetCast}; use dom::bindings::codegen::NodeBinding::NodeConstants; @@ -21,6 +21,7 @@ use dom::documentfragment::DocumentFragment; use dom::documenttype::DocumentType; use dom::element::{Element, ElementTypeId, HTMLAnchorElementTypeId}; use dom::eventtarget::{EventTarget, NodeTargetTypeId}; +use dom::htmlstyleelement::StyleElementHelpers; use dom::nodelist::{NodeList}; use dom::processinginstruction::ProcessingInstruction; use dom::text::Text; @@ -269,6 +270,8 @@ pub trait NodeHelpers { fn add_child(&mut self, new_child: &mut JS, before: Option>); fn remove_child(&mut self, child: &mut JS); + fn child_inserted(&self); + fn get_hover_state(&self) -> bool; fn set_hover_state(&mut self, state: bool); @@ -408,6 +411,7 @@ impl NodeHelpers for JS { } } + self.parent_node().map(|parent| parent.child_inserted()); document.get().content_changed(); } @@ -499,6 +503,15 @@ impl NodeHelpers for JS { child_node.set_parent_node(None); } + fn child_inserted(&self) { + // Parse text content added to an inline stylesheet. + match HTMLStyleElementCast::to(self) { + Some(elem) => elem.parse_own_css(), + None => () + } + } + + fn get_hover_state(&self) -> bool { self.get().flags.get_in_hover_state() } diff --git a/src/components/script/html/cssparse.rs b/src/components/script/html/cssparse.rs index c5cad0849de..b538676edac 100644 --- a/src/components/script/html/cssparse.rs +++ b/src/components/script/html/cssparse.rs @@ -18,34 +18,45 @@ pub enum StylesheetProvenance { InlineProvenance(Url, ~str), } +// Parses the style data and returns the stylesheet +pub fn parse_inline_css(url: Url, data: ~str) -> Stylesheet { + let resource_task = ResourceTask(); // Resource task is not used for inline parsing + parse_css(InlineProvenance(url, data), resource_task) +} + +fn parse_css(provenance: StylesheetProvenance, + resource_task: ResourceTask) -> Stylesheet { + // TODO: Get the actual value. http://dev.w3.org/csswg/css-syntax/#environment-encoding + let environment_encoding = UTF_8 as EncodingRef; + + match provenance { + UrlProvenance(url) => { + debug!("cssparse: loading style sheet at {:s}", url.to_str()); + let (input_chan, input_port) = channel(); + resource_task.send(Load(url, input_chan)); + let LoadResponse { metadata: metadata, progress_port: progress_port } + = input_port.recv(); + let final_url = &metadata.final_url; + let protocol_encoding_label = metadata.charset.as_ref().map(|s| s.as_slice()); + let iter = ProgressMsgPortIterator { progress_port: progress_port }; + Stylesheet::from_bytes_iter( + iter, final_url.clone(), + protocol_encoding_label, Some(environment_encoding)) + } + InlineProvenance(base_url, data) => { + debug!("cssparse: loading inline stylesheet {:s}", data); + Stylesheet::from_str(data, base_url, environment_encoding) + } + } +} + pub fn spawn_css_parser(provenance: StylesheetProvenance, resource_task: ResourceTask) -> Receiver { let (result_chan, result_port) = channel(); - // TODO: Get the actual value. http://dev.w3.org/csswg/css-syntax/#environment-encoding - let environment_encoding = UTF_8 as EncodingRef; - spawn_named("cssparser", proc() { - let sheet = match provenance { - UrlProvenance(url) => { - debug!("cssparse: loading style sheet at {:s}", url.to_str()); - let (input_chan, input_port) = channel(); - resource_task.send(Load(url, input_chan)); - let LoadResponse { metadata: metadata, progress_port: progress_port } - = input_port.recv(); - let final_url = &metadata.final_url; - let protocol_encoding_label = metadata.charset.as_ref().map(|s| s.as_slice()); - let iter = ProgressMsgPortIterator { progress_port: progress_port }; - Stylesheet::from_bytes_iter( - iter, final_url.clone(), - protocol_encoding_label, Some(environment_encoding)) - } - InlineProvenance(base_url, data) => { - Stylesheet::from_str(data, base_url, environment_encoding) - } - }; - result_chan.send(sheet); + result_chan.send(parse_css(provenance, resource_task)); }); return result_port; diff --git a/src/components/script/html/hubbub_html_parser.rs b/src/components/script/html/hubbub_html_parser.rs index c9d57ba0e16..21d285632f8 100644 --- a/src/components/script/html/hubbub_html_parser.rs +++ b/src/components/script/html/hubbub_html_parser.rs @@ -14,7 +14,7 @@ use dom::htmliframeelement::IFrameSize; use dom::htmlformelement::HTMLFormElement; use dom::node::{ElementNodeTypeId, INode, NodeHelpers}; use dom::types::*; -use html::cssparse::{InlineProvenance, StylesheetProvenance, UrlProvenance, spawn_css_parser}; +use html::cssparse::{StylesheetProvenance, UrlProvenance, spawn_css_parser}; use script_task::Page; use hubbub::hubbub; @@ -298,7 +298,7 @@ pub fn parse_html(page: &Page, parser.enable_scripting(true); parser.enable_styling(true); - let (css_chan2, css_chan3, js_chan2) = (css_chan.clone(), css_chan.clone(), js_chan.clone()); + let (css_chan2, js_chan2) = (css_chan.clone(), js_chan.clone()); let next_subpage_id = RefCell::new(next_subpage_id); @@ -483,22 +483,8 @@ pub fn parse_html(page: &Page, } debug!("complete script"); }, - complete_style: |style| { - // We've reached the end of a