diff options
Diffstat (limited to 'components/script/dom/servoparser')
-rw-r--r-- | components/script/dom/servoparser/html.rs | 226 | ||||
-rw-r--r-- | components/script/dom/servoparser/mod.rs | 203 | ||||
-rw-r--r-- | components/script/dom/servoparser/xml.rs | 87 |
3 files changed, 263 insertions, 253 deletions
diff --git a/components/script/dom/servoparser/html.rs b/components/script/dom/servoparser/html.rs index 0401342a5db..a2d636da6f3 100644 --- a/components/script/dom/servoparser/html.rs +++ b/components/script/dom/servoparser/html.rs @@ -4,23 +4,19 @@ #![allow(unrooted_must_root)] -use document_loader::DocumentLoader; -use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use dom::bindings::inheritance::{Castable, CharacterDataTypeId, NodeTypeId}; -use dom::bindings::js::{JS, RootedReference}; +use dom::bindings::js::{JS, Root}; use dom::bindings::str::DOMString; +use dom::bindings::trace::JSTraceable; use dom::characterdata::CharacterData; use dom::comment::Comment; -use dom::document::{DocumentSource, IsHTMLDocument}; use dom::document::Document; use dom::documenttype::DocumentType; use dom::element::{Element, ElementCreator}; -use dom::htmlformelement::HTMLFormElement; use dom::htmlscriptelement::HTMLScriptElement; use dom::htmltemplateelement::HTMLTemplateElement; -use dom::node::{document_from_node, window_from_node}; use dom::node::Node; use dom::processinginstruction::ProcessingInstruction; use dom::text::Text; @@ -29,28 +25,107 @@ use html5ever::serialize::{AttrRef, Serializable, Serializer}; use html5ever::serialize::TraversalScope; use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode}; use html5ever::tendril::StrTendril; -use html5ever::tokenizer::{Tokenizer as H5ETokenizer, TokenizerOpts}; +use html5ever::tokenizer::{Tokenizer as HtmlTokenizer, TokenizerOpts, TokenizerResult}; +use html5ever::tokenizer::buffer_queue::BufferQueue; use html5ever::tree_builder::{NodeOrText, QuirksMode}; -use html5ever::tree_builder::{TreeBuilder, TreeBuilderOpts, TreeSink}; +use html5ever::tree_builder::{Tracer as HtmlTracer, TreeBuilder, TreeBuilderOpts, TreeSink}; use html5ever_atoms::QualName; -use msg::constellation_msg::PipelineId; +use js::jsapi::JSTracer; use std::borrow::Cow; use std::io::{self, Write}; -use super::{HtmlTokenizer, LastChunkState, ServoParser, Sink, Tokenizer}; +use super::{FragmentContext, Sink}; use url::Url; -fn insert(parent: &Node, reference_child: Option<&Node>, child: NodeOrText<JS<Node>>) { - match child { - NodeOrText::AppendNode(n) => { - assert!(parent.InsertBefore(&n, reference_child).is_ok()); - }, - NodeOrText::AppendText(t) => { - // FIXME(ajeffrey): convert directly from tendrils to DOMStrings - let s: String = t.into(); - let text = Text::new(DOMString::from(s), &parent.owner_doc()); - assert!(parent.InsertBefore(text.upcast(), reference_child).is_ok()); +#[derive(HeapSizeOf, JSTraceable)] +#[must_root] +pub struct Tokenizer { + #[ignore_heap_size_of = "Defined in html5ever"] + inner: HtmlTokenizer<TreeBuilder<JS<Node>, Sink>>, + #[ignore_heap_size_of = "Defined in html5ever"] + input_buffer: BufferQueue, +} + +impl Tokenizer { + pub fn new( + document: &Document, + url: Url, + fragment_context: Option<FragmentContext>) + -> Self { + let sink = Sink { + base_url: url, + document: JS::from_ref(document), + }; + + let options = TreeBuilderOpts { + ignore_missing_rules: true, + .. Default::default() + }; + + let inner = if let Some(fc) = fragment_context { + let tb = TreeBuilder::new_for_fragment( + sink, + JS::from_ref(fc.context_elem), + fc.form_elem.map(|n| JS::from_ref(n)), + options); + + let tok_options = TokenizerOpts { + initial_state: Some(tb.tokenizer_state_for_context_elem()), + .. Default::default() + }; + + HtmlTokenizer::new(tb, tok_options) + } else { + HtmlTokenizer::new(TreeBuilder::new(sink, options), Default::default()) + }; + + Tokenizer { + inner: inner, + input_buffer: BufferQueue::new(), + } + } + + pub fn feed(&mut self, input: String) { + self.input_buffer.push_back(input.into()); + self.run(); + } + + #[allow(unrooted_must_root)] + pub fn run(&mut self) { + while let TokenizerResult::Script(script) = self.inner.feed(&mut self.input_buffer) { + let script = Root::from_ref(script.downcast::<HTMLScriptElement>().unwrap()); + if !script.prepare() { + break; + } } } + + pub fn end(&mut self) { + assert!(self.input_buffer.is_empty()); + self.inner.end(); + } + + pub fn set_plaintext_state(&mut self) { + self.inner.set_plaintext_state(); + } +} + +impl JSTraceable for HtmlTokenizer<TreeBuilder<JS<Node>, Sink>> { + fn trace(&self, trc: *mut JSTracer) { + struct Tracer(*mut JSTracer); + let tracer = Tracer(trc); + + impl HtmlTracer for Tracer { + type Handle = JS<Node>; + #[allow(unrooted_must_root)] + fn trace_handle(&self, node: &JS<Node>) { + node.trace(self.0); + } + } + + let tree_builder = self.sink(); + tree_builder.trace_handles(&tracer); + tree_builder.sink().trace(trc); + } } impl<'a> TreeSink for Sink { @@ -161,6 +236,20 @@ impl<'a> TreeSink for Sink { } } +fn insert(parent: &Node, reference_child: Option<&Node>, child: NodeOrText<JS<Node>>) { + match child { + NodeOrText::AppendNode(n) => { + assert!(parent.InsertBefore(&n, reference_child).is_ok()); + }, + NodeOrText::AppendText(t) => { + // FIXME(ajeffrey): convert directly from tendrils to DOMStrings + let s: String = t.into(); + let text = Text::new(DOMString::from(s), &parent.owner_doc()); + assert!(parent.InsertBefore(text.upcast(), reference_child).is_ok()); + } + } +} + impl<'a> Serializable for &'a Node { fn serialize<'wr, Wr: Write>(&self, serializer: &mut Serializer<'wr, Wr>, traversal_scope: TraversalScope) -> io::Result<()> { @@ -237,100 +326,3 @@ impl<'a> Serializable for &'a Node { } } } - -/// FragmentContext is used only to pass this group of related values -/// into functions. -#[derive(Copy, Clone)] -pub struct FragmentContext<'a> { - pub context_elem: &'a Node, - pub form_elem: Option<&'a Node>, -} - -pub enum ParseContext<'a> { - Fragment(FragmentContext<'a>), - Owner(Option<PipelineId>), -} - -pub fn parse_html(document: &Document, - input: DOMString, - url: Url, - context: ParseContext) { - let sink = Sink { - base_url: url, - document: JS::from_ref(document), - }; - - let options = TreeBuilderOpts { - ignore_missing_rules: true, - .. Default::default() - }; - - let parser = match context { - ParseContext::Owner(owner) => { - let tb = TreeBuilder::new(sink, options); - let tok = H5ETokenizer::new(tb, Default::default()); - - ServoParser::new( - document, - owner, - Tokenizer::HTML(HtmlTokenizer::new(tok)), - LastChunkState::NotReceived) - }, - ParseContext::Fragment(fc) => { - let tb = TreeBuilder::new_for_fragment( - sink, - JS::from_ref(fc.context_elem), - fc.form_elem.map(|n| JS::from_ref(n)), - options); - - let tok_options = TokenizerOpts { - initial_state: Some(tb.tokenizer_state_for_context_elem()), - .. Default::default() - }; - let tok = H5ETokenizer::new(tb, tok_options); - - ServoParser::new( - document, - None, - Tokenizer::HTML(HtmlTokenizer::new(tok)), - LastChunkState::Received) - } - }; - parser.parse_chunk(String::from(input)); -} - -// https://html.spec.whatwg.org/multipage/#parsing-html-fragments -pub fn parse_html_fragment(context_node: &Node, - input: DOMString, - output: &Node) { - let window = window_from_node(context_node); - let context_document = document_from_node(context_node); - let url = context_document.url(); - - // Step 1. - let loader = DocumentLoader::new(&*context_document.loader()); - let document = Document::new(&window, None, Some(url.clone()), - IsHTMLDocument::HTMLDocument, - None, None, - DocumentSource::FromParser, - loader, - None, None); - - // Step 2. - document.set_quirks_mode(context_document.quirks_mode()); - - // Step 11. - let form = context_node.inclusive_ancestors() - .find(|element| element.is::<HTMLFormElement>()); - let fragment_context = FragmentContext { - context_elem: context_node, - form_elem: form.r(), - }; - parse_html(&document, input, url.clone(), ParseContext::Fragment(fragment_context)); - - // Step 14. - let root_element = document.GetDocumentElement().expect("no document element"); - for child in root_element.upcast::<Node>().children() { - output.AppendChild(&child).unwrap(); - } -} diff --git a/components/script/dom/servoparser/mod.rs b/components/script/dom/servoparser/mod.rs index 474c8f036f0..7b5c5c7f4ab 100644 --- a/components/script/dom/servoparser/mod.rs +++ b/components/script/dom/servoparser/mod.rs @@ -2,33 +2,27 @@ * 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 document_loader::LoadType; +use document_loader::{DocumentLoader, LoadType}; use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::codegen::Bindings::HTMLImageElementBinding::HTMLImageElementMethods; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use dom::bindings::codegen::Bindings::ServoParserBinding; use dom::bindings::inheritance::Castable; -use dom::bindings::js::{JS, Root}; +use dom::bindings::js::{JS, Root, RootedReference}; use dom::bindings::refcounted::Trusted; use dom::bindings::reflector::{Reflector, reflect_dom_object}; use dom::bindings::str::DOMString; -use dom::bindings::trace::JSTraceable; -use dom::document::Document; +use dom::document::{Document, DocumentSource, IsHTMLDocument}; use dom::globalscope::GlobalScope; +use dom::htmlformelement::HTMLFormElement; use dom::htmlimageelement::HTMLImageElement; -use dom::htmlscriptelement::HTMLScriptElement; -use dom::node::Node; +use dom::node::{Node, document_from_node, window_from_node}; use encoding::all::UTF_8; use encoding::types::{DecoderTrap, Encoding}; -use html5ever::tokenizer::{Tokenizer as H5ETokenizer, TokenizerResult}; -use html5ever::tokenizer::buffer_queue::BufferQueue; -use html5ever::tree_builder::Tracer as HtmlTracer; -use html5ever::tree_builder::TreeBuilder as HtmlTreeBuilder; use hyper::header::ContentType; use hyper::mime::{Mime, SubLevel, TopLevel}; use hyper_serde::Serde; -use js::jsapi::JSTracer; use msg::constellation_msg::PipelineId; use net_traits::{FetchMetadata, FetchResponseListener, Metadata, NetworkError}; use network_listener::PreInvoke; @@ -39,11 +33,9 @@ use std::cell::Cell; use std::collections::VecDeque; use url::Url; use util::resource_files::read_resource_file; -use xml5ever::tokenizer::XmlTokenizer; -use xml5ever::tree_builder::{Tracer as XmlTracer, XmlTreeBuilder}; -pub mod html; -pub mod xml; +mod html; +mod xml; #[dom_struct] pub struct ServoParser { @@ -70,6 +62,76 @@ enum LastChunkState { } impl ServoParser { + pub fn parse_html_document( + document: &Document, + input: DOMString, + url: Url, + owner: Option<PipelineId>) { + let parser = ServoParser::new( + document, + owner, + Tokenizer::Html(self::html::Tokenizer::new(document, url, None)), + LastChunkState::NotReceived); + parser.parse_chunk(String::from(input)); + } + + // https://html.spec.whatwg.org/multipage/#parsing-html-fragments + pub fn parse_html_fragment( + context_node: &Node, + input: DOMString, + output: &Node) { + let window = window_from_node(context_node); + let context_document = document_from_node(context_node); + let url = context_document.url(); + + // Step 1. + let loader = DocumentLoader::new(&*context_document.loader()); + let document = Document::new(&window, None, Some(url.clone()), + IsHTMLDocument::HTMLDocument, + None, None, + DocumentSource::FromParser, + loader, + None, None); + + // Step 2. + document.set_quirks_mode(context_document.quirks_mode()); + + // Step 11. + let form = context_node.inclusive_ancestors() + .find(|element| element.is::<HTMLFormElement>()); + let fragment_context = FragmentContext { + context_elem: context_node, + form_elem: form.r(), + }; + + let parser = ServoParser::new( + &document, + None, + Tokenizer::Html( + self::html::Tokenizer::new(&document, url.clone(), Some(fragment_context))), + LastChunkState::Received); + parser.parse_chunk(String::from(input)); + + // Step 14. + let root_element = document.GetDocumentElement().expect("no document element"); + for child in root_element.upcast::<Node>().children() { + output.AppendChild(&child).unwrap(); + } + } + + pub fn parse_xml_document( + document: &Document, + input: DOMString, + url: Url, + owner: Option<PipelineId>) { + let parser = ServoParser::new( + document, + owner, + Tokenizer::Xml(self::xml::Tokenizer::new(document, url)), + LastChunkState::NotReceived); + parser.parse_chunk(String::from(input)); + } + #[allow(unrooted_must_root)] fn new_inherited( document: &Document, @@ -215,57 +277,11 @@ impl ServoParser { } } -#[derive(HeapSizeOf)] +#[derive(HeapSizeOf, JSTraceable)] #[must_root] enum Tokenizer { - HTML(HtmlTokenizer), - XML( - #[ignore_heap_size_of = "Defined in xml5ever"] - XmlTokenizer<XmlTreeBuilder<JS<Node>, Sink>> - ), -} - -#[derive(HeapSizeOf)] -#[must_root] -struct HtmlTokenizer { - #[ignore_heap_size_of = "Defined in html5ever"] - inner: H5ETokenizer<HtmlTreeBuilder<JS<Node>, Sink>>, - #[ignore_heap_size_of = "Defined in html5ever"] - input_buffer: BufferQueue, -} - -impl HtmlTokenizer { - #[allow(unrooted_must_root)] - fn new(inner: H5ETokenizer<HtmlTreeBuilder<JS<Node>, Sink>>) -> Self { - HtmlTokenizer { - inner: inner, - input_buffer: BufferQueue::new(), - } - } - - fn feed(&mut self, input: String) { - self.input_buffer.push_back(input.into()); - self.run(); - } - - #[allow(unrooted_must_root)] - fn run(&mut self) { - while let TokenizerResult::Script(script) = self.inner.feed(&mut self.input_buffer) { - let script = Root::from_ref(script.downcast::<HTMLScriptElement>().unwrap()); - if !script.prepare() { - break; - } - } - } - - fn end(&mut self) { - assert!(self.input_buffer.is_empty()); - self.inner.end(); - } - - fn set_plaintext_state(&mut self) { - self.inner.set_plaintext_state(); - } + Html(self::html::Tokenizer), + Xml(self::xml::Tokenizer), } #[derive(JSTraceable, HeapSizeOf)] @@ -278,70 +294,36 @@ struct Sink { impl Tokenizer { fn feed(&mut self, input: String) { match *self { - Tokenizer::HTML(ref mut tokenizer) => tokenizer.feed(input), - Tokenizer::XML(ref mut tokenizer) => tokenizer.feed(input.into()), + Tokenizer::Html(ref mut tokenizer) => tokenizer.feed(input), + Tokenizer::Xml(ref mut tokenizer) => tokenizer.feed(input.into()), } } fn run(&mut self) { match *self { - Tokenizer::HTML(ref mut tokenizer) => tokenizer.run(), - Tokenizer::XML(ref mut tokenizer) => tokenizer.run(), + Tokenizer::Html(ref mut tokenizer) => tokenizer.run(), + Tokenizer::Xml(ref mut tokenizer) => tokenizer.run(), } } fn end(&mut self) { match *self { - Tokenizer::HTML(ref mut tokenizer) => tokenizer.end(), - Tokenizer::XML(ref mut tokenizer) => tokenizer.end(), + Tokenizer::Html(ref mut tokenizer) => tokenizer.end(), + Tokenizer::Xml(ref mut tokenizer) => tokenizer.end(), } } fn set_plaintext_state(&mut self) { match *self { - Tokenizer::HTML(ref mut tokenizer) => tokenizer.set_plaintext_state(), - Tokenizer::XML(_) => { /* todo */ }, + Tokenizer::Html(ref mut tokenizer) => tokenizer.set_plaintext_state(), + Tokenizer::Xml(_) => unimplemented!(), } } fn profiler_category(&self) -> ProfilerCategory { match *self { - Tokenizer::HTML(_) => ProfilerCategory::ScriptParseHTML, - Tokenizer::XML(_) => ProfilerCategory::ScriptParseXML, - } - } -} - -impl JSTraceable for Tokenizer { - fn trace(&self, trc: *mut JSTracer) { - struct Tracer(*mut JSTracer); - let tracer = Tracer(trc); - - match *self { - Tokenizer::HTML(ref tokenizer) => { - impl HtmlTracer for Tracer { - type Handle = JS<Node>; - #[allow(unrooted_must_root)] - fn trace_handle(&self, node: &JS<Node>) { - node.trace(self.0); - } - } - let tree_builder = tokenizer.inner.sink(); - tree_builder.trace_handles(&tracer); - tree_builder.sink().trace(trc); - }, - Tokenizer::XML(ref tokenizer) => { - impl XmlTracer for Tracer { - type Handle = JS<Node>; - #[allow(unrooted_must_root)] - fn trace_handle(&self, node: JS<Node>) { - node.trace(self.0); - } - } - let tree_builder = tokenizer.sink(); - tree_builder.trace_handles(&tracer); - tree_builder.sink().trace(trc); - } + Tokenizer::Html(_) => ProfilerCategory::ScriptParseHTML, + Tokenizer::Xml(_) => ProfilerCategory::ScriptParseXML, } } } @@ -499,3 +481,8 @@ impl FetchResponseListener for ParserContext { } impl PreInvoke for ParserContext {} + +pub struct FragmentContext<'a> { + pub context_elem: &'a Node, + pub form_elem: Option<&'a Node>, +} diff --git a/components/script/dom/servoparser/xml.rs b/components/script/dom/servoparser/xml.rs index 8c1e9490df1..6f87d6a389c 100644 --- a/components/script/dom/servoparser/xml.rs +++ b/components/script/dom/servoparser/xml.rs @@ -8,6 +8,7 @@ use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, Root}; use dom::bindings::str::DOMString; +use dom::bindings::trace::JSTraceable; use dom::comment::Comment; use dom::document::Document; use dom::documenttype::DocumentType; @@ -17,13 +18,68 @@ use dom::node::Node; use dom::processinginstruction::ProcessingInstruction; use dom::text::Text; use html5ever_atoms::{Prefix, QualName}; -use msg::constellation_msg::PipelineId; +use js::jsapi::JSTracer; use std::borrow::Cow; -use super::{LastChunkState, ServoParser, Sink, Tokenizer}; +use super::Sink; use url::Url; use xml5ever::tendril::StrTendril; use xml5ever::tokenizer::{Attribute, QName, XmlTokenizer}; -use xml5ever::tree_builder::{NextParserState, NodeOrText, TreeSink, XmlTreeBuilder}; +use xml5ever::tree_builder::{NextParserState, NodeOrText}; +use xml5ever::tree_builder::{Tracer as XmlTracer, TreeSink, XmlTreeBuilder}; + +#[derive(HeapSizeOf, JSTraceable)] +#[must_root] +pub struct Tokenizer { + #[ignore_heap_size_of = "Defined in xml5ever"] + inner: XmlTokenizer<XmlTreeBuilder<JS<Node>, Sink>>, +} + +impl Tokenizer { + pub fn new(document: &Document, url: Url) -> Self { + let sink = Sink { + base_url: url, + document: JS::from_ref(document), + }; + + let tb = XmlTreeBuilder::new(sink); + let tok = XmlTokenizer::new(tb, Default::default()); + + Tokenizer { + inner: tok, + } + } + + pub fn feed(&mut self, input: String) { + self.inner.feed(input.into()) + } + + pub fn run(&mut self) { + self.inner.run() + } + + pub fn end(&mut self) { + self.inner.end() + } +} + +impl JSTraceable for XmlTokenizer<XmlTreeBuilder<JS<Node>, Sink>> { + fn trace(&self, trc: *mut JSTracer) { + struct Tracer(*mut JSTracer); + let tracer = Tracer(trc); + + impl XmlTracer for Tracer { + type Handle = JS<Node>; + #[allow(unrooted_must_root)] + fn trace_handle(&self, node: JS<Node>) { + node.trace(self.0); + } + } + + let tree_builder = self.sink(); + tree_builder.trace_handles(&tracer); + tree_builder.sink().trace(trc); + } +} impl<'a> TreeSink for Sink { type Handle = JS<Node>; @@ -119,28 +175,3 @@ impl<'a> TreeSink for Sink { NextParserState::Continue } } - - -pub enum ParseContext { - Owner(Option<PipelineId>) -} - - -pub fn parse_xml(document: &Document, - input: DOMString, - url: Url, - context: ParseContext) { - let parser = match context { - ParseContext::Owner(owner) => { - let tb = XmlTreeBuilder::new(Sink { - base_url: url, - document: JS::from_ref(document), - }); - let tok = XmlTokenizer::new(tb, Default::default()); - - ServoParser::new( - document, owner, Tokenizer::XML(tok), LastChunkState::NotReceived) - } - }; - parser.parse_chunk(String::from(input)); -} |