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::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};

View file

@ -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<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::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<Node>, before: Option<JS<Node>>);
fn remove_child(&mut self, child: &mut JS<Node>);
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<Node> {
}
}
self.parent_node().map(|parent| parent.child_inserted());
document.get().content_changed();
}
@ -499,6 +503,15 @@ impl NodeHelpers for JS<Node> {
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()
}

View file

@ -18,16 +18,18 @@ pub enum StylesheetProvenance {
InlineProvenance(Url, ~str),
}
pub fn spawn_css_parser(provenance: StylesheetProvenance,
resource_task: ResourceTask)
-> Receiver<Stylesheet> {
let (result_chan, result_port) = channel();
// 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;
spawn_named("cssparser", proc() {
let sheet = match provenance {
match provenance {
UrlProvenance(url) => {
debug!("cssparse: loading style sheet at {:s}", url.to_str());
let (input_chan, input_port) = channel();
@ -42,10 +44,19 @@ pub fn spawn_css_parser(provenance: StylesheetProvenance,
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)
}
};
result_chan.send(sheet);
}
}
pub fn spawn_css_parser(provenance: StylesheetProvenance,
resource_task: ResourceTask)
-> Receiver<Stylesheet> {
let (result_chan, result_port) = channel();
spawn_named("cssparser", proc() {
result_chan.send(parse_css(provenance, resource_task));
});
return result_port;

View file

@ -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 <style> so we can submit all the text to the parser.
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));
}
complete_style: |_| {
// style parsing is handled in element::notify_child_list_changed.
},
};
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
# == simple_iframe.html simple_iframe_ref.html -- disabled due to iframe crashiness
== object_element_a.html object_element_b.html
== append_style_a.html append_style_b.html
== height_compute_reset.html height_compute.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