auto merge of #1984 : mbrubeck/servo/1959-parser-style, r=jdm

This is a partial fix for #1959.  This commit only addresses inline `<style>` elements.  Next I will need to move `<link rel="stylesheet">` handling into shared code, but I wanted to get some early feedback on this piece first.
This commit is contained in:
bors-servo 2014-04-14 14:22:09 -04:00
commit 18b5453e09
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,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<Stylesheet> {
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;

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