diff options
author | Anthony Ramine <n.oxyde@gmail.com> | 2016-10-10 16:11:00 +0200 |
---|---|---|
committer | Anthony Ramine <n.oxyde@gmail.com> | 2016-10-11 15:08:37 +0200 |
commit | 1405be691776e48836f651c3c616dc12322a0932 (patch) | |
tree | ae98c4b4e517879cfec1742464e1e44dca1afb4e /components/script | |
parent | 609299e1e45e93939f75f8439fc7ac3276ca5881 (diff) | |
download | servo-1405be691776e48836f651c3c616dc12322a0932.tar.gz servo-1405be691776e48836f651c3c616dc12322a0932.zip |
Unify ServoHTMLParser and ServoXMLParser in ServoParser
Diffstat (limited to 'components/script')
-rw-r--r-- | components/script/dom/document.rs | 8 | ||||
-rw-r--r-- | components/script/dom/mod.rs | 2 | ||||
-rw-r--r-- | components/script/dom/servohtmlparser.rs | 235 | ||||
-rw-r--r-- | components/script/dom/servoparser.rs | 215 | ||||
-rw-r--r-- | components/script/dom/servoxmlparser.rs | 185 | ||||
-rw-r--r-- | components/script/dom/webidls/ServoHTMLParser.webidl | 11 | ||||
-rw-r--r-- | components/script/dom/webidls/ServoParser.webidl | 2 | ||||
-rw-r--r-- | components/script/dom/webidls/ServoXMLParser.webidl | 11 | ||||
-rw-r--r-- | components/script/parse/html.rs | 54 | ||||
-rw-r--r-- | components/script/parse/mod.rs | 192 | ||||
-rw-r--r-- | components/script/parse/xml.rs | 22 | ||||
-rw-r--r-- | components/script/script_thread.rs | 9 |
12 files changed, 300 insertions, 646 deletions
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 2fdd36667cf..242d2bf98c1 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -76,6 +76,7 @@ use dom::popstateevent::PopStateEvent; use dom::processinginstruction::ProcessingInstruction; use dom::progressevent::ProgressEvent; use dom::range::Range; +use dom::servoparser::ServoParser; use dom::storageevent::StorageEvent; use dom::stylesheetlist::StyleSheetList; use dom::text::Text; @@ -103,7 +104,6 @@ use net_traits::request::RequestInit; use net_traits::response::HttpsState; use num_traits::ToPrimitive; use origin::Origin; -use parse::{MutNullableParserField, ParserRef, ParserRoot}; use script_layout_interface::message::{Msg, ReflowQueryType}; use script_thread::{MainThreadScriptMsg, Runnable}; use script_traits::{AnimationState, CompositorEvent, MouseButton, MouseEventType, MozBrowserEvent}; @@ -226,7 +226,7 @@ pub struct Document { /// Tracks all outstanding loads related to this document. loader: DOMRefCell<DocumentLoader>, /// The current active HTML parser, to allow resuming after interruptions. - current_parser: MutNullableParserField, + current_parser: MutNullableHeap<JS<ServoParser>>, /// When we should kick off a reflow. This happens during parsing. reflow_timeout: Cell<Option<u64>>, /// The cached first `base` element with an `href` attribute. @@ -1627,11 +1627,11 @@ impl Document { global_scope.constellation_chan().send(load_event).unwrap(); } - pub fn set_current_parser(&self, script: Option<ParserRef>) { + pub fn set_current_parser(&self, script: Option<&ServoParser>) { self.current_parser.set(script); } - pub fn get_current_parser(&self) -> Option<ParserRoot> { + pub fn get_current_parser(&self) -> Option<Root<ServoParser>> { self.current_parser.get() } diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index bf68603ff13..afc91be7c4b 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -386,9 +386,7 @@ pub mod serviceworker; pub mod serviceworkercontainer; pub mod serviceworkerglobalscope; pub mod serviceworkerregistration; -pub mod servohtmlparser; pub mod servoparser; -pub mod servoxmlparser; pub mod storage; pub mod storageevent; pub mod stylesheet; diff --git a/components/script/dom/servohtmlparser.rs b/components/script/dom/servohtmlparser.rs deleted file mode 100644 index ec01187e2b4..00000000000 --- a/components/script/dom/servohtmlparser.rs +++ /dev/null @@ -1,235 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -//! The bulk of the HTML parser integration is in `script::parse::html`. -//! This module is mostly about its interaction with DOM memory management. - -use dom::bindings::cell::DOMRefCell; -use dom::bindings::codegen::Bindings::ServoHTMLParserBinding; -use dom::bindings::inheritance::Castable; -use dom::bindings::js::{JS, Root}; -use dom::bindings::reflector::reflect_dom_object; -use dom::bindings::trace::JSTraceable; -use dom::document::Document; -use dom::globalscope::GlobalScope; -use dom::node::Node; -use dom::servoparser::ServoParser; -use dom::window::Window; -use html5ever::tokenizer; -use html5ever::tree_builder; -use html5ever::tree_builder::{TreeBuilder, TreeBuilderOpts}; -use js::jsapi::JSTracer; -use msg::constellation_msg::PipelineId; -use parse::{Parser, ParserRef}; -use profile_traits::time::{TimerMetadata, TimerMetadataFrameType, TimerMetadataReflowType, profile}; -use profile_traits::time::ProfilerCategory; -use script_thread::ScriptThread; -use std::cell::Cell; -use std::default::Default; -use url::Url; - -#[must_root] -#[derive(JSTraceable, HeapSizeOf)] -pub struct Sink { - pub base_url: Option<Url>, - pub document: JS<Document>, -} - -/// 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 type Tokenizer = tokenizer::Tokenizer<TreeBuilder<JS<Node>, Sink>>; - -#[dom_struct] -pub struct ServoHTMLParser { - servoparser: ServoParser, - #[ignore_heap_size_of = "Defined in html5ever"] - tokenizer: DOMRefCell<Tokenizer>, - /// True if this parser should avoid passing any further data to the tokenizer. - suspended: Cell<bool>, -} - -impl<'a> Parser for &'a ServoHTMLParser { - fn parse_chunk(self, input: String) { - self.upcast().document().set_current_parser(Some(ParserRef::HTML(self))); - self.upcast().push_input_chunk(input); - if !self.is_suspended() { - self.parse_sync(); - } - } - - fn finish(self) { - assert!(!self.suspended.get()); - assert!(!self.upcast().has_pending_input()); - - self.tokenizer.borrow_mut().end(); - debug!("finished parsing"); - - self.upcast().document().set_current_parser(None); - - if let Some(pipeline) = self.upcast().pipeline() { - ScriptThread::parsing_complete(pipeline); - } - } -} - -impl ServoHTMLParser { - #[allow(unrooted_must_root)] - pub fn new(base_url: Option<Url>, document: &Document, pipeline: Option<PipelineId>) - -> Root<ServoHTMLParser> { - let sink = Sink { - base_url: base_url, - document: JS::from_ref(document), - }; - - let tb = TreeBuilder::new(sink, TreeBuilderOpts { - ignore_missing_rules: true, - .. Default::default() - }); - - let tok = tokenizer::Tokenizer::new(tb, Default::default()); - - let parser = ServoHTMLParser { - servoparser: ServoParser::new_inherited(document, pipeline, false), - tokenizer: DOMRefCell::new(tok), - suspended: Cell::new(false), - }; - - reflect_dom_object(box parser, document.window(), ServoHTMLParserBinding::Wrap) - } - - #[allow(unrooted_must_root)] - pub fn new_for_fragment(base_url: Option<Url>, document: &Document, - fragment_context: FragmentContext) -> Root<ServoHTMLParser> { - let sink = Sink { - base_url: base_url, - document: JS::from_ref(document), - }; - - let tb_opts = TreeBuilderOpts { - ignore_missing_rules: true, - .. Default::default() - }; - let tb = TreeBuilder::new_for_fragment(sink, - JS::from_ref(fragment_context.context_elem), - fragment_context.form_elem.map(|n| JS::from_ref(n)), - tb_opts); - - let tok_opts = tokenizer::TokenizerOpts { - initial_state: Some(tb.tokenizer_state_for_context_elem()), - .. Default::default() - }; - let tok = tokenizer::Tokenizer::new(tb, tok_opts); - - let parser = ServoHTMLParser { - servoparser: ServoParser::new_inherited(document, None, true), - tokenizer: DOMRefCell::new(tok), - suspended: Cell::new(false), - }; - - reflect_dom_object(box parser, document.window(), ServoHTMLParserBinding::Wrap) - } - - #[inline] - pub fn tokenizer(&self) -> &DOMRefCell<Tokenizer> { - &self.tokenizer - } - - pub fn set_plaintext_state(&self) { - self.tokenizer.borrow_mut().set_plaintext_state() - } - - pub fn end_tokenizer(&self) { - self.tokenizer.borrow_mut().end() - } -} - -impl ServoHTMLParser { - pub fn parse_sync(&self) { - let metadata = TimerMetadata { - url: self.upcast().document().url().as_str().into(), - iframe: TimerMetadataFrameType::RootWindow, - incremental: TimerMetadataReflowType::FirstReflow, - }; - profile(ProfilerCategory::ScriptParseHTML, - Some(metadata), - self.upcast().document().window().upcast::<GlobalScope>().time_profiler_chan().clone(), - || self.do_parse_sync()) - } - - fn do_parse_sync(&self) { - // This parser will continue to parse while there is either pending input or - // the parser remains unsuspended. - loop { - self.upcast().document().reflow_if_reflow_timer_expired(); - if let Some(chunk) = self.upcast().take_next_input_chunk() { - self.tokenizer.borrow_mut().feed(chunk.into()); - } else { - self.tokenizer.borrow_mut().run(); - } - - // Document parsing is blocked on an external resource. - if self.suspended.get() { - return; - } - - if !self.upcast().has_pending_input() { - break; - } - } - - if self.upcast().last_chunk_received() { - self.finish(); - } - } - - pub fn window(&self) -> &Window { - self.upcast().document().window() - } - - pub fn suspend(&self) { - assert!(!self.suspended.get()); - self.suspended.set(true); - } - - pub fn resume(&self) { - assert!(self.suspended.get()); - self.suspended.set(false); - self.parse_sync(); - } - - pub fn is_suspended(&self) -> bool { - self.suspended.get() - } -} - -struct Tracer { - trc: *mut JSTracer, -} - -impl tree_builder::Tracer for Tracer { - type Handle = JS<Node>; - #[allow(unrooted_must_root)] - fn trace_handle(&self, node: &JS<Node>) { - node.trace(self.trc); - } -} - -impl JSTraceable for Tokenizer { - fn trace(&self, trc: *mut JSTracer) { - let tracer = Tracer { - trc: trc, - }; - let tracer = &tracer as &tree_builder::Tracer<Handle=JS<Node>>; - - let tree_builder = self.sink(); - tree_builder.trace_handles(tracer); - tree_builder.sink().trace(trc); - } -} diff --git a/components/script/dom/servoparser.rs b/components/script/dom/servoparser.rs index 4a2bc0de400..9566919b9f4 100644 --- a/components/script/dom/servoparser.rs +++ b/components/script/dom/servoparser.rs @@ -3,11 +3,27 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use dom::bindings::cell::DOMRefCell; -use dom::bindings::reflector::Reflector; -use dom::bindings::js::JS; +use dom::bindings::codegen::Bindings::ServoParserBinding; +use dom::bindings::inheritance::Castable; +use dom::bindings::js::{JS, Root}; +use dom::bindings::reflector::{Reflector, reflect_dom_object}; +use dom::bindings::trace::JSTraceable; use dom::document::Document; +use dom::globalscope::GlobalScope; +use dom::node::Node; +use dom::window::Window; +use html5ever::tokenizer::Tokenizer as HtmlTokenizer; +use html5ever::tree_builder::Tracer as HtmlTracer; +use html5ever::tree_builder::TreeBuilder as HtmlTreeBuilder; +use js::jsapi::JSTracer; use msg::constellation_msg::PipelineId; +use parse::Sink; +use profile_traits::time::{TimerMetadata, TimerMetadataFrameType}; +use profile_traits::time::{TimerMetadataReflowType, ProfilerCategory, profile}; +use script_thread::ScriptThread; use std::cell::Cell; +use xml5ever::tokenizer::XmlTokenizer; +use xml5ever::tree_builder::{Tracer as XmlTracer, XmlTreeBuilder}; #[dom_struct] pub struct ServoParser { @@ -19,14 +35,20 @@ pub struct ServoParser { pipeline: Option<PipelineId>, /// Input chunks received but not yet passed to the parser. pending_input: DOMRefCell<Vec<String>>, + /// The tokenizer of this parser. + tokenizer: DOMRefCell<Tokenizer>, /// Whether to expect any further input from the associated network request. last_chunk_received: Cell<bool>, + /// Whether this parser should avoid passing any further data to the tokenizer. + suspended: Cell<bool>, } impl ServoParser { - pub fn new_inherited( + #[allow(unrooted_must_root)] + fn new_inherited( document: &Document, pipeline: Option<PipelineId>, + tokenizer: Tokenizer, last_chunk_received: bool) -> Self { ServoParser { @@ -34,10 +56,25 @@ impl ServoParser { document: JS::from_ref(document), pipeline: pipeline, pending_input: DOMRefCell::new(vec![]), + tokenizer: DOMRefCell::new(tokenizer), last_chunk_received: Cell::new(last_chunk_received), + suspended: Default::default(), } } + #[allow(unrooted_must_root)] + pub fn new( + document: &Document, + pipeline: Option<PipelineId>, + tokenizer: Tokenizer, + last_chunk_received: bool) + -> Root<Self> { + reflect_dom_object( + box ServoParser::new_inherited(document, pipeline, tokenizer, last_chunk_received), + document.window(), + ServoParserBinding::Wrap) + } + pub fn document(&self) -> &Document { &self.document } @@ -70,4 +107,176 @@ impl ServoParser { pub fn mark_last_chunk_received(&self) { self.last_chunk_received.set(true) } + + pub fn set_plaintext_state(&self) { + self.tokenizer.borrow_mut().set_plaintext_state() + } + + pub fn end_tokenizer(&self) { + self.tokenizer.borrow_mut().end() + } + + pub fn window(&self) -> &Window { + self.document().window() + } + + pub fn suspend(&self) { + assert!(!self.suspended.get()); + self.suspended.set(true); + } + + pub fn resume(&self) { + assert!(self.suspended.get()); + self.suspended.set(false); + self.parse_sync(); + } + + pub fn is_suspended(&self) -> bool { + self.suspended.get() + } + + pub fn parse_sync(&self) { + let metadata = TimerMetadata { + url: self.document().url().as_str().into(), + iframe: TimerMetadataFrameType::RootWindow, + incremental: TimerMetadataReflowType::FirstReflow, + }; + let profiler_category = self.tokenizer.borrow().profiler_category(); + profile(profiler_category, + Some(metadata), + self.document().window().upcast::<GlobalScope>().time_profiler_chan().clone(), + || self.do_parse_sync()) + } + + fn do_parse_sync(&self) { + // This parser will continue to parse while there is either pending input or + // the parser remains unsuspended. + loop { + self.document().reflow_if_reflow_timer_expired(); + if let Some(chunk) = self.take_next_input_chunk() { + self.tokenizer.borrow_mut().feed(chunk); + } else { + self.tokenizer.borrow_mut().run(); + } + + // Document parsing is blocked on an external resource. + if self.suspended.get() { + return; + } + + if !self.has_pending_input() { + break; + } + } + + if self.last_chunk_received() { + self.finish(); + } + } + + pub fn parse_chunk(&self, input: String) { + self.document().set_current_parser(Some(self)); + self.push_input_chunk(input); + if !self.is_suspended() { + self.parse_sync(); + } + } + + pub fn finish(&self) { + assert!(!self.suspended.get()); + assert!(!self.has_pending_input()); + + self.tokenizer.borrow_mut().end(); + debug!("finished parsing"); + + self.document().set_current_parser(None); + + if let Some(pipeline) = self.pipeline() { + ScriptThread::parsing_complete(pipeline); + } + } +} + +#[derive(HeapSizeOf)] +#[must_root] +pub enum Tokenizer { + HTML( + #[ignore_heap_size_of = "Defined in html5ever"] + HtmlTokenizer<HtmlTreeBuilder<JS<Node>, Sink>> + ), + XML( + #[ignore_heap_size_of = "Defined in xml5ever"] + XmlTokenizer<XmlTreeBuilder<JS<Node>, Sink>> + ), +} + +impl Tokenizer { + pub fn feed(&mut self, input: String) { + match *self { + Tokenizer::HTML(ref mut tokenizer) => tokenizer.feed(input.into()), + Tokenizer::XML(ref mut tokenizer) => tokenizer.feed(input.into()), + } + } + + pub fn run(&mut self) { + match *self { + Tokenizer::HTML(ref mut tokenizer) => tokenizer.run(), + Tokenizer::XML(ref mut tokenizer) => tokenizer.run(), + } + } + + pub fn end(&mut self) { + match *self { + Tokenizer::HTML(ref mut tokenizer) => tokenizer.end(), + Tokenizer::XML(ref mut tokenizer) => tokenizer.end(), + } + } + + pub fn set_plaintext_state(&mut self) { + match *self { + Tokenizer::HTML(ref mut tokenizer) => tokenizer.set_plaintext_state(), + Tokenizer::XML(_) => { /* todo */ }, + } + } + + pub 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.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); + } + } + } } diff --git a/components/script/dom/servoxmlparser.rs b/components/script/dom/servoxmlparser.rs deleted file mode 100644 index 919dcfdb946..00000000000 --- a/components/script/dom/servoxmlparser.rs +++ /dev/null @@ -1,185 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * 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 dom::bindings::cell::DOMRefCell; -use dom::bindings::codegen::Bindings::ServoXMLParserBinding; -use dom::bindings::inheritance::Castable; -use dom::bindings::js::{JS, Root}; -use dom::bindings::reflector::reflect_dom_object; -use dom::bindings::trace::JSTraceable; -use dom::document::Document; -use dom::globalscope::GlobalScope; -use dom::node::Node; -use dom::servoparser::ServoParser; -use dom::window::Window; -use js::jsapi::JSTracer; -use msg::constellation_msg::PipelineId; -use parse::{Parser, ParserRef}; -use profile_traits::time::{ProfilerCategory, TimerMetadata}; -use profile_traits::time::{TimerMetadataFrameType, TimerMetadataReflowType, profile}; -use script_thread::ScriptThread; -use std::cell::Cell; -use url::Url; -use xml5ever::tokenizer; -use xml5ever::tree_builder::{self, XmlTreeBuilder}; - -pub type Tokenizer = tokenizer::XmlTokenizer<XmlTreeBuilder<JS<Node>, Sink>>; - -#[must_root] -#[derive(JSTraceable, HeapSizeOf)] -pub struct Sink { - pub base_url: Option<Url>, - pub document: JS<Document>, -} - -#[must_root] -#[dom_struct] -pub struct ServoXMLParser { - servoparser: ServoParser, - #[ignore_heap_size_of = "Defined in xml5ever"] - tokenizer: DOMRefCell<Tokenizer>, - /// True if this parser should avoid passing any further data to the tokenizer. - suspended: Cell<bool>, -} - -impl<'a> Parser for &'a ServoXMLParser { - fn parse_chunk(self, input: String) { - self.upcast().document().set_current_parser(Some(ParserRef::XML(self))); - self.upcast().push_input_chunk(input); - if !self.is_suspended() { - self.parse_sync(); - } - } - - fn finish(self) { - assert!(!self.suspended.get()); - assert!(!self.upcast().has_pending_input()); - - self.tokenizer.borrow_mut().end(); - debug!("finished parsing"); - - self.upcast().document().set_current_parser(None); - - if let Some(pipeline) = self.upcast().pipeline() { - ScriptThread::parsing_complete(pipeline); - } - } -} - -impl ServoXMLParser { - #[allow(unrooted_must_root)] - pub fn new(base_url: Option<Url>, document: &Document, pipeline: Option<PipelineId>) - -> Root<ServoXMLParser> { - let sink = Sink { - base_url: base_url, - document: JS::from_ref(document), - }; - - let tb = XmlTreeBuilder::new(sink); - - let tok = tokenizer::XmlTokenizer::new(tb, Default::default()); - - let parser = ServoXMLParser { - servoparser: ServoParser::new_inherited(document, pipeline, false), - tokenizer: DOMRefCell::new(tok), - suspended: Cell::new(false), - }; - - reflect_dom_object(box parser, document.window(), ServoXMLParserBinding::Wrap) - } - - pub fn window(&self) -> &Window { - self.upcast().document().window() - } - - pub fn resume(&self) { - assert!(self.suspended.get()); - self.suspended.set(false); - self.parse_sync(); - } - - pub fn suspend(&self) { - assert!(!self.suspended.get()); - self.suspended.set(true); - } - - pub fn is_suspended(&self) -> bool { - self.suspended.get() - } - - pub fn parse_sync(&self) { - let metadata = TimerMetadata { - url: self.upcast().document().url().as_str().into(), - iframe: TimerMetadataFrameType::RootWindow, - incremental: TimerMetadataReflowType::FirstReflow, - }; - profile(ProfilerCategory::ScriptParseXML, - Some(metadata), - self.upcast().document().window().upcast::<GlobalScope>().time_profiler_chan().clone(), - || self.do_parse_sync()) - } - - fn do_parse_sync(&self) { - // This parser will continue to parse while there is either pending input or - // the parser remains unsuspended. - loop { - self.upcast().document().reflow_if_reflow_timer_expired(); - if let Some(chunk) = self.upcast().take_next_input_chunk() { - self.tokenizer.borrow_mut().feed(chunk.into()); - } else { - self.tokenizer.borrow_mut().run(); - } - - // Document parsing is blocked on an external resource. - if self.suspended.get() { - return; - } - - if !self.upcast().has_pending_input() { - break; - } - } - - if self.upcast().last_chunk_received() { - self.finish(); - } - } - - pub fn set_plaintext_state(&self) { - //self.tokenizer.borrow_mut().set_plaintext_state() - } - - pub fn end_tokenizer(&self) { - self.tokenizer.borrow_mut().end() - } - - pub fn tokenizer(&self) -> &DOMRefCell<Tokenizer> { - &self.tokenizer - } -} - -struct Tracer { - trc: *mut JSTracer, -} - -impl tree_builder::Tracer for Tracer { - type Handle = JS<Node>; - #[allow(unrooted_must_root)] - fn trace_handle(&self, node: JS<Node>) { - node.trace(self.trc); - } -} - -impl JSTraceable for Tokenizer { - fn trace(&self, trc: *mut JSTracer) { - let tracer = Tracer { - trc: trc, - }; - let tracer = &tracer as &tree_builder::Tracer<Handle=JS<Node>>; - - let tree_builder = self.sink(); - tree_builder.trace_handles(tracer); - tree_builder.sink().trace(trc); - } -} diff --git a/components/script/dom/webidls/ServoHTMLParser.webidl b/components/script/dom/webidls/ServoHTMLParser.webidl deleted file mode 100644 index ddf9382ff9a..00000000000 --- a/components/script/dom/webidls/ServoHTMLParser.webidl +++ /dev/null @@ -1,11 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -// This interface is entirely internal to Servo, and should not be accessible to -// web pages. - -// FIXME: find a better way to hide this from content (#3688) -[NoInterfaceObject, Exposed=(Window,Worker)] -interface ServoHTMLParser : ServoParser { -}; diff --git a/components/script/dom/webidls/ServoParser.webidl b/components/script/dom/webidls/ServoParser.webidl index 435ca1fb90f..c3b0926d824 100644 --- a/components/script/dom/webidls/ServoParser.webidl +++ b/components/script/dom/webidls/ServoParser.webidl @@ -6,5 +6,5 @@ // web pages. [Exposed=(Window,Worker), - Inline] + NoInterfaceObject] interface ServoParser {}; diff --git a/components/script/dom/webidls/ServoXMLParser.webidl b/components/script/dom/webidls/ServoXMLParser.webidl deleted file mode 100644 index 3f6f03822ad..00000000000 --- a/components/script/dom/webidls/ServoXMLParser.webidl +++ /dev/null @@ -1,11 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -// This interface is entirely internal to Servo, and should not be accessible to -// web pages. - -[NoInterfaceObject, Exposed=(Window,Worker)] -interface ServoXMLParser : ServoParser { -}; - diff --git a/components/script/parse/html.rs b/components/script/parse/html.rs index 39684bd1b3e..dd4a17c40c8 100644 --- a/components/script/parse/html.rs +++ b/components/script/parse/html.rs @@ -23,17 +23,18 @@ use dom::htmltemplateelement::HTMLTemplateElement; use dom::node::{document_from_node, window_from_node}; use dom::node::Node; use dom::processinginstruction::ProcessingInstruction; -use dom::servohtmlparser; -use dom::servohtmlparser::{FragmentContext, ServoHTMLParser}; +use dom::servoparser::{ServoParser, Tokenizer}; use dom::text::Text; use html5ever::Attribute; use html5ever::serialize::{AttrRef, Serializable, Serializer}; use html5ever::serialize::TraversalScope; use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode}; use html5ever::tendril::StrTendril; -use html5ever::tree_builder::{NextParserState, NodeOrText, QuirksMode, TreeSink}; +use html5ever::tokenizer::{Tokenizer as HtmlTokenizer, TokenizerOpts}; +use html5ever::tree_builder::{NextParserState, NodeOrText, QuirksMode}; +use html5ever::tree_builder::{TreeBuilder, TreeBuilderOpts, TreeSink}; use msg::constellation_msg::PipelineId; -use parse::Parser; +use parse::Sink; use std::borrow::Cow; use std::io::{self, Write}; use string_cache::QualName; @@ -53,7 +54,7 @@ fn insert(parent: &Node, reference_child: Option<&Node>, child: NodeOrText<JS<No } } -impl<'a> TreeSink for servohtmlparser::Sink { +impl<'a> TreeSink for Sink { type Output = Self; fn finish(self) -> Self { self } @@ -246,6 +247,14 @@ 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>), @@ -255,11 +264,38 @@ 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) => - ServoHTMLParser::new(Some(url), document, owner), - ParseContext::Fragment(fc) => - ServoHTMLParser::new_for_fragment(Some(url), document, fc), + ParseContext::Owner(owner) => { + let tb = TreeBuilder::new(sink, options); + let tok = HtmlTokenizer::new(tb, Default::default()); + + ServoParser::new(document, owner, Tokenizer::HTML(tok), false) + }, + 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 = HtmlTokenizer::new(tb, tok_options); + + ServoParser::new(document, None, Tokenizer::HTML(tok), true) + } }; parser.parse_chunk(String::from(input)); } diff --git a/components/script/parse/mod.rs b/components/script/parse/mod.rs index dc874baef45..6ef786eaa93 100644 --- a/components/script/parse/mod.rs +++ b/components/script/parse/mod.rs @@ -6,16 +6,13 @@ use document_loader::LoadType; use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::codegen::Bindings::HTMLImageElementBinding::HTMLImageElementMethods; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; -use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, Root}; use dom::bindings::refcounted::Trusted; use dom::bindings::str::DOMString; +use dom::document::Document; use dom::htmlimageelement::HTMLImageElement; use dom::node::Node; -use dom::servohtmlparser::ServoHTMLParser; use dom::servoparser::ServoParser; -use dom::servoxmlparser::ServoXMLParser; -use dom::window::Window; use encoding::all::UTF_8; use encoding::types::{DecoderTrap, Encoding}; use hyper::header::ContentType; @@ -25,164 +22,17 @@ use msg::constellation_msg::PipelineId; use net_traits::{AsyncResponseListener, Metadata, NetworkError}; use network_listener::PreInvoke; use script_thread::ScriptThread; -use std::cell::UnsafeCell; -use std::ptr; use url::Url; use util::resource_files::read_resource_file; pub mod html; pub mod xml; -pub trait Parser { - fn parse_chunk(self, input: String); - fn finish(self); -} - -#[must_root] -#[derive(JSTraceable, HeapSizeOf)] -pub enum ParserField { - HTML(JS<ServoHTMLParser>), - XML(JS<ServoXMLParser>), -} - -#[must_root] -#[derive(JSTraceable, HeapSizeOf)] -pub struct MutNullableParserField { - #[ignore_heap_size_of = "XXXjdm"] - ptr: UnsafeCell<Option<ParserField>>, -} - -impl Default for MutNullableParserField { - #[allow(unrooted_must_root)] - fn default() -> MutNullableParserField { - MutNullableParserField { - ptr: UnsafeCell::new(None), - } - } -} - -impl MutNullableParserField { - #[allow(unsafe_code)] - pub fn set(&self, val: Option<ParserRef>) { - unsafe { - *self.ptr.get() = val.map(|val| { - match val { - ParserRef::HTML(parser) => ParserField::HTML(JS::from_ref(parser)), - ParserRef::XML(parser) => ParserField::XML(JS::from_ref(parser)), - } - }); - } - } - - #[allow(unsafe_code, unrooted_must_root)] - pub fn get(&self) -> Option<ParserRoot> { - unsafe { - ptr::read(self.ptr.get()).map(|o| { - match o { - ParserField::HTML(parser) => ParserRoot::HTML(Root::from_ref(&*parser)), - ParserField::XML(parser) => ParserRoot::XML(Root::from_ref(&*parser)), - } - }) - } - } -} - -pub enum ParserRoot { - HTML(Root<ServoHTMLParser>), - XML(Root<ServoXMLParser>), -} - -impl ParserRoot { - pub fn r(&self) -> ParserRef { - match *self { - ParserRoot::HTML(ref parser) => ParserRef::HTML(parser.r()), - ParserRoot::XML(ref parser) => ParserRef::XML(parser.r()), - } - } -} - -pub enum TrustedParser { - HTML(Trusted<ServoHTMLParser>), - XML(Trusted<ServoXMLParser>), -} - -impl TrustedParser { - pub fn root(&self) -> ParserRoot { - match *self { - TrustedParser::HTML(ref parser) => ParserRoot::HTML(parser.root()), - TrustedParser::XML(ref parser) => ParserRoot::XML(parser.root()), - } - } -} - -pub enum ParserRef<'a> { - HTML(&'a ServoHTMLParser), - XML(&'a ServoXMLParser), -} - -impl<'a> ParserRef<'a> { - pub fn as_servo_parser(&self) -> &ServoParser { - match *self { - ParserRef::HTML(parser) => parser.upcast(), - ParserRef::XML(parser) => parser.upcast(), - } - } - - pub fn parse_chunk(&self, input: String) { - match *self { - ParserRef::HTML(parser) => parser.parse_chunk(input), - ParserRef::XML(parser) => parser.parse_chunk(input), - } - } - - pub fn window(&self) -> &Window { - match *self { - ParserRef::HTML(parser) => parser.window(), - ParserRef::XML(parser) => parser.window(), - } - } - - pub fn resume(&self) { - match *self { - ParserRef::HTML(parser) => parser.resume(), - ParserRef::XML(parser) => parser.resume(), - } - } - - pub fn suspend(&self) { - match *self { - ParserRef::HTML(parser) => parser.suspend(), - ParserRef::XML(parser) => parser.suspend(), - } - } - - pub fn is_suspended(&self) -> bool { - match *self { - ParserRef::HTML(parser) => parser.is_suspended(), - ParserRef::XML(parser) => parser.is_suspended(), - } - } - - pub fn set_plaintext_state(&self) { - match *self { - ParserRef::HTML(parser) => parser.set_plaintext_state(), - ParserRef::XML(parser) => parser.set_plaintext_state(), - } - } - - pub fn parse_sync(&self) { - match *self { - ParserRef::HTML(parser) => parser.parse_sync(), - ParserRef::XML(parser) => parser.parse_sync(), - } - } -} - /// The context required for asynchronously fetching a document /// and parsing it progressively. pub struct ParserContext { /// The parser that initiated the request. - parser: Option<TrustedParser>, + parser: Option<Trusted<ServoParser>>, /// Is this a synthesized document is_synthesized_document: bool, /// The pipeline associated with this document. @@ -224,23 +74,16 @@ impl AsyncResponseListener for ParserContext { None => return, }; - let parser = parser.r(); - let servo_parser = parser.as_servo_parser(); - self.parser = Some(match parser { - ParserRef::HTML(parser) => TrustedParser::HTML( - Trusted::new(parser)), - ParserRef::XML(parser) => TrustedParser::XML( - Trusted::new(parser)), - }); + self.parser = Some(Trusted::new(&*parser)); match content_type { Some(ContentType(Mime(TopLevel::Image, _, _))) => { self.is_synthesized_document = true; let page = "<html><body></body></html>".into(); - servo_parser.push_input_chunk(page); + parser.push_input_chunk(page); parser.parse_sync(); - let doc = servo_parser.document(); + let doc = parser.document(); let doc_body = Root::upcast::<Node>(doc.GetBody().unwrap()); let img = HTMLImageElement::new(atom!("img"), None, doc); img.SetSrc(DOMString::from(self.url.to_string())); @@ -250,7 +93,7 @@ impl AsyncResponseListener for ParserContext { Some(ContentType(Mime(TopLevel::Text, SubLevel::Plain, _))) => { // https://html.spec.whatwg.org/multipage/#read-text let page = "<pre>\n".into(); - servo_parser.push_input_chunk(page); + parser.push_input_chunk(page); parser.parse_sync(); parser.set_plaintext_state(); }, @@ -260,7 +103,7 @@ impl AsyncResponseListener for ParserContext { let page_bytes = read_resource_file("badcert.html").unwrap(); let page = String::from_utf8(page_bytes).unwrap(); let page = page.replace("${reason}", &reason); - servo_parser.push_input_chunk(page); + parser.push_input_chunk(page); parser.parse_sync(); } }, @@ -275,7 +118,7 @@ impl AsyncResponseListener for ParserContext { let page = format!("<html><body><p>Unknown content type ({}/{}).</p></body></html>", toplevel.as_str(), sublevel.as_str()); self.is_synthesized_document = true; - servo_parser.push_input_chunk(page); + parser.push_input_chunk(page); parser.parse_sync(); }, None => { @@ -293,7 +136,7 @@ impl AsyncResponseListener for ParserContext { Some(parser) => parser.root(), None => return, }; - parser.r().parse_chunk(data); + parser.parse_chunk(data); } } @@ -307,24 +150,20 @@ impl AsyncResponseListener for ParserContext { // Show an error page for network errors, // certificate errors are handled earlier. self.is_synthesized_document = true; - let parser = parser.r(); let page_bytes = read_resource_file("neterror.html").unwrap(); let page = String::from_utf8(page_bytes).unwrap(); let page = page.replace("${reason}", reason); - parser.as_servo_parser().push_input_chunk(page); + parser.push_input_chunk(page); parser.parse_sync(); } else if let Err(err) = status { // TODO(Savago): we should send a notification to callers #5463. debug!("Failed to load page URL {}, error: {:?}", self.url, err); } - let parser = parser.r(); - let servo_parser = parser.as_servo_parser(); - - servo_parser.document() + parser.document() .finish_load(LoadType::PageSource(self.url.clone())); - servo_parser.mark_last_chunk_received(); + parser.mark_last_chunk_received(); if !parser.is_suspended() { parser.parse_sync(); } @@ -332,3 +171,10 @@ impl AsyncResponseListener for ParserContext { } impl PreInvoke for ParserContext {} + +#[derive(JSTraceable, HeapSizeOf)] +#[must_root] +pub struct Sink { + pub base_url: Url, + pub document: JS<Document>, +} diff --git a/components/script/parse/xml.rs b/components/script/parse/xml.rs index ee0959d04ef..3777b7f497c 100644 --- a/components/script/parse/xml.rs +++ b/components/script/parse/xml.rs @@ -15,20 +15,19 @@ use dom::element::{Element, ElementCreator}; use dom::htmlscriptelement::HTMLScriptElement; use dom::node::Node; use dom::processinginstruction::ProcessingInstruction; -use dom::servoxmlparser; -use dom::servoxmlparser::ServoXMLParser; +use dom::servoparser::{ServoParser, Tokenizer}; use dom::text::Text; use html5ever; use msg::constellation_msg::PipelineId; -use parse::Parser; +use parse::Sink; use std::borrow::Cow; use string_cache::{Atom, QualName, Namespace}; use url::Url; use xml5ever::tendril::StrTendril; -use xml5ever::tokenizer::{Attribute, QName}; -use xml5ever::tree_builder::{NextParserState, NodeOrText, TreeSink}; +use xml5ever::tokenizer::{Attribute, QName, XmlTokenizer}; +use xml5ever::tree_builder::{NextParserState, NodeOrText, TreeSink, XmlTreeBuilder}; -impl<'a> TreeSink for servoxmlparser::Sink { +impl<'a> TreeSink for Sink { type Handle = JS<Node>; fn parse_error(&mut self, msg: Cow<'static, str>) { @@ -134,8 +133,15 @@ pub fn parse_xml(document: &Document, url: Url, context: ParseContext) { let parser = match context { - ParseContext::Owner(owner) => - ServoXMLParser::new(Some(url), document, owner), + 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), false) + } }; parser.parse_chunk(String::from(input)); } diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index b52fe1da126..ceb6842a13b 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -44,6 +44,7 @@ use dom::htmlanchorelement::HTMLAnchorElement; use dom::node::{Node, NodeDamage, window_from_node}; use dom::serviceworker::TrustedServiceWorkerAddress; use dom::serviceworkerregistration::ServiceWorkerRegistration; +use dom::servoparser::ServoParser; use dom::uievent::UIEvent; use dom::window::{ReflowReason, Window}; use dom::worker::TrustedWorkerAddress; @@ -70,7 +71,7 @@ use net_traits::{IpcSend, LoadData as NetLoadData}; use net_traits::bluetooth_thread::BluetoothMethodMsg; use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheResult, ImageCacheThread}; use network_listener::NetworkListener; -use parse::{ParserContext, ParserRoot}; +use parse::ParserContext; use parse::html::{ParseContext, parse_html}; use parse::xml::{self, parse_xml}; use profile_traits::mem::{self, OpaqueSender, Report, ReportKind, ReportsChan}; @@ -489,7 +490,7 @@ impl ScriptThreadFactory for ScriptThread { impl ScriptThread { pub fn page_headers_available(id: &PipelineId, metadata: Option<Metadata>) - -> Option<ParserRoot> { + -> Option<Root<ServoParser>> { SCRIPT_THREAD_ROOT.with(|root| { let script_thread = unsafe { &*root.get().unwrap() }; script_thread.handle_page_headers_available(id, metadata) @@ -1413,7 +1414,7 @@ impl ScriptThread { /// We have received notification that the response associated with a load has completed. /// Kick off the document and frame tree creation process using the result. fn handle_page_headers_available(&self, id: &PipelineId, - metadata: Option<Metadata>) -> Option<ParserRoot> { + metadata: Option<Metadata>) -> Option<Root<ServoParser>> { let idx = self.incomplete_loads.borrow().iter().position(|load| { load.pipeline_id == *id }); // The matching in progress load structure may not exist if // the pipeline exited before the page load completed. @@ -1543,7 +1544,7 @@ impl ScriptThread { /// The entry point to document loading. Defines bindings, sets up the window and document /// objects, parses HTML and CSS, and kicks off initial layout. - fn load(&self, metadata: Metadata, incomplete: InProgressLoad) -> ParserRoot { + fn load(&self, metadata: Metadata, incomplete: InProgressLoad) -> Root<ServoParser> { let final_url = metadata.final_url.clone(); { // send the final url to the layout thread. |