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.
This commit is contained in:
Matt Brubeck 2014-03-26 10:15:29 -07:00
parent ce877e4f5b
commit 8c794c6739
8 changed files with 83 additions and 46 deletions

View file

@ -12,17 +12,17 @@ use dom::bindings::js::JS;
use dom::bindings::utils::{Reflectable, Reflector}; use dom::bindings::utils::{Reflectable, Reflector};
use dom::bindings::error::{ErrorResult, Fallible, NamespaceError, InvalidCharacter}; use dom::bindings::error::{ErrorResult, Fallible, NamespaceError, InvalidCharacter};
use dom::bindings::utils::{QName, Name, InvalidXMLName, xml_name_type}; use dom::bindings::utils::{QName, Name, InvalidXMLName, xml_name_type};
use dom::htmlcollection::HTMLCollection;
use dom::clientrect::ClientRect; use dom::clientrect::ClientRect;
use dom::clientrectlist::ClientRectList; use dom::clientrectlist::ClientRectList;
use dom::document::Document; use dom::document::Document;
use dom::eventtarget::{EventTarget, NodeTargetTypeId}; 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::htmlserializer::serialize;
use dom::node::{ElementNodeTypeId, Node, NodeHelpers, NodeIterator, document_from_node};
use dom::virtualmethods::{VirtualMethods, vtable_for}; use dom::virtualmethods::{VirtualMethods, vtable_for};
use layout_interface::{ContentBoxQuery, ContentBoxResponse, ContentBoxesQuery}; use layout_interface::{ContentBoxQuery, ContentBoxResponse, ContentBoxesQuery};
use layout_interface::{ContentBoxesResponse, ContentChangedDocumentDamage}; use layout_interface::{ContentBoxesResponse, ContentChangedDocumentDamage};
use layout_interface::{MatchSelectorsDocumentDamage}; use layout_interface::MatchSelectorsDocumentDamage;
use style; use style;
use servo_util::namespace; use servo_util::namespace;
use servo_util::namespace::{Namespace, Null}; use servo_util::namespace::{Namespace, Null};

View file

@ -3,14 +3,16 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::bindings::codegen::HTMLStyleElementBinding; 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::js::JS;
use dom::bindings::error::ErrorResult; use dom::bindings::error::ErrorResult;
use dom::document::Document; use dom::document::Document;
use dom::element::HTMLStyleElementTypeId; use dom::element::HTMLStyleElementTypeId;
use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement; 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; use servo_util::str::DOMString;
#[deriving(Encodable)] #[deriving(Encodable)]
@ -72,3 +74,20 @@ impl HTMLStyleElement {
Ok(()) Ok(())
} }
} }
pub trait StyleElementHelpers {
fn parse_own_css(&self);
}
impl StyleElementHelpers for JS<HTMLStyleElement> {
fn parse_own_css(&self) {
let node: JS<Node> = 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));
}
}

View file

@ -6,7 +6,7 @@
use dom::attr::Attr; use dom::attr::Attr;
use dom::bindings::codegen::InheritTypes::{CommentCast, DocumentCast, DocumentTypeCast}; 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::{CharacterDataCast, NodeBase, NodeDerived};
use dom::bindings::codegen::InheritTypes::{ProcessingInstructionCast, EventTargetCast}; use dom::bindings::codegen::InheritTypes::{ProcessingInstructionCast, EventTargetCast};
use dom::bindings::codegen::NodeBinding::NodeConstants; use dom::bindings::codegen::NodeBinding::NodeConstants;
@ -21,6 +21,7 @@ use dom::documentfragment::DocumentFragment;
use dom::documenttype::DocumentType; use dom::documenttype::DocumentType;
use dom::element::{Element, ElementTypeId, HTMLAnchorElementTypeId}; use dom::element::{Element, ElementTypeId, HTMLAnchorElementTypeId};
use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlstyleelement::StyleElementHelpers;
use dom::nodelist::{NodeList}; use dom::nodelist::{NodeList};
use dom::processinginstruction::ProcessingInstruction; use dom::processinginstruction::ProcessingInstruction;
use dom::text::Text; use dom::text::Text;
@ -269,6 +270,8 @@ pub trait NodeHelpers {
fn add_child(&mut self, new_child: &mut JS<Node>, before: Option<JS<Node>>); fn add_child(&mut self, new_child: &mut JS<Node>, before: Option<JS<Node>>);
fn remove_child(&mut self, child: &mut JS<Node>); fn remove_child(&mut self, child: &mut JS<Node>);
fn child_inserted(&self);
fn get_hover_state(&self) -> bool; fn get_hover_state(&self) -> bool;
fn set_hover_state(&mut self, state: bool); fn set_hover_state(&mut self, state: bool);
@ -408,6 +411,7 @@ impl NodeHelpers for JS<Node> {
} }
} }
self.parent_node().map(|parent| parent.child_inserted());
document.get().content_changed(); document.get().content_changed();
} }
@ -499,6 +503,15 @@ impl NodeHelpers for JS<Node> {
child_node.set_parent_node(None); 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 { fn get_hover_state(&self) -> bool {
self.get().flags.get_in_hover_state() self.get().flags.get_in_hover_state()
} }

View file

@ -18,34 +18,45 @@ pub enum StylesheetProvenance {
InlineProvenance(Url, ~str), 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, pub fn spawn_css_parser(provenance: StylesheetProvenance,
resource_task: ResourceTask) resource_task: ResourceTask)
-> Receiver<Stylesheet> { -> Receiver<Stylesheet> {
let (result_chan, result_port) = channel(); 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() { spawn_named("cssparser", proc() {
let sheet = match provenance { result_chan.send(parse_css(provenance, resource_task));
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);
}); });
return result_port; return result_port;

View file

@ -14,7 +14,7 @@ use dom::htmliframeelement::IFrameSize;
use dom::htmlformelement::HTMLFormElement; use dom::htmlformelement::HTMLFormElement;
use dom::node::{ElementNodeTypeId, INode, NodeHelpers}; use dom::node::{ElementNodeTypeId, INode, NodeHelpers};
use dom::types::*; 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 script_task::Page;
use hubbub::hubbub; use hubbub::hubbub;
@ -298,7 +298,7 @@ pub fn parse_html(page: &Page,
parser.enable_scripting(true); parser.enable_scripting(true);
parser.enable_styling(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); let next_subpage_id = RefCell::new(next_subpage_id);
@ -483,22 +483,8 @@ pub fn parse_html(page: &Page,
} }
debug!("complete script"); debug!("complete script");
}, },
complete_style: |style| { complete_style: |_| {
// We've reached the end of a <style> so we can submit all the text to the parser. // style parsing is handled in element::notify_child_list_changed.
unsafe {
let style: JS<Node> = NodeWrapping::from_hubbub_node(style);
let mut data = ~[];
debug!("iterating over children {:?}", style.first_child());
for child in style.children() {
debug!("child = {:?}", child);
let text: JS<Text> = TextCast::to(&child).unwrap();
data.push(text.get().characterdata.data.to_str()); // FIXME: Bad copy.
}
debug!("style data = {:?}", data);
let provenance = InlineProvenance(base_url.clone(), data.concat());
css_chan3.send(CSSTaskNewFile(provenance));
}
}, },
}; };
parser.set_tree_handler(&tree_handler); parser.set_tree_handler(&tree_handler);

View file

@ -0,0 +1 @@
<div style="background-color: blue">this is the story of a girl</div>

View file

@ -0,0 +1,6 @@
<div id="hello">this is the story of a girl</div>
<script>
var style = document.createElement('style');
style.textContent = "#hello { background-color: blue; }"
document.head.appendChild(style);
</script>

View file

@ -35,6 +35,7 @@
!= block_image.html noteq_500x300_white.html != block_image.html noteq_500x300_white.html
# == simple_iframe.html simple_iframe_ref.html -- disabled due to iframe crashiness # == simple_iframe.html simple_iframe_ref.html -- disabled due to iframe crashiness
== object_element_a.html object_element_b.html == object_element_a.html object_element_b.html
== append_style_a.html append_style_b.html
== height_compute_reset.html height_compute.html == height_compute_reset.html height_compute.html
== width_nonreplaced_block_simple_a.html width_nonreplaced_block_simple_b.html == width_nonreplaced_block_simple_a.html width_nonreplaced_block_simple_b.html
== max_width_float_simple_a.html max_width_float_simple_b.html == max_width_float_simple_a.html max_width_float_simple_b.html