From ea27f9d5ec580a9e743f89e92702df1d25b580ec Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Sat, 8 Oct 2016 14:02:16 +0200 Subject: [PATCH 1/9] Introduce ServoParser This is a common inline parent to ServoHTMLParser and ServoXMLParser. --- .../script/dom/bindings/codegen/CodegenRust.py | 5 +++-- components/script/dom/mod.rs | 1 + components/script/dom/servohtmlparser.rs | 9 +++++---- components/script/dom/servoparser.rs | 18 ++++++++++++++++++ components/script/dom/servoxmlparser.rs | 7 ++++--- .../script/dom/webidls/ServoHTMLParser.webidl | 2 +- .../script/dom/webidls/ServoParser.webidl | 10 ++++++++++ .../script/dom/webidls/ServoXMLParser.webidl | 2 +- 8 files changed, 43 insertions(+), 11 deletions(-) create mode 100644 components/script/dom/servoparser.rs create mode 100644 components/script/dom/webidls/ServoParser.webidl diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index 24826ced714..d4c12f8c2ae 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -2748,7 +2748,8 @@ assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null()); interface.get()); """ % {"id": name, "name": str_to_const_array(name)}) - if len(self.descriptor.prototypeChain) == 1: + parentName = self.descriptor.getParentName() + if not parentName: if self.descriptor.interface.getExtendedAttribute("ExceptionClass"): getPrototypeProto = "prototype_proto.set(JS_GetErrorPrototype(cx))" elif self.descriptor.interface.isIteratorInterface(): @@ -2757,7 +2758,7 @@ assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null()); getPrototypeProto = "prototype_proto.set(JS_GetObjectPrototype(cx, global))" else: getPrototypeProto = ("%s::GetProtoObject(cx, global, prototype_proto.handle_mut())" % - toBindingNamespace(self.descriptor.getParentName())) + toBindingNamespace(parentName)) code = [CGGeneric("""\ rooted!(in(cx) let mut prototype_proto = ptr::null_mut()); diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index ace4f979be6..bf68603ff13 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -387,6 +387,7 @@ pub mod serviceworkercontainer; pub mod serviceworkerglobalscope; pub mod serviceworkerregistration; pub mod servohtmlparser; +pub mod servoparser; pub mod servoxmlparser; pub mod storage; pub mod storageevent; diff --git a/components/script/dom/servohtmlparser.rs b/components/script/dom/servohtmlparser.rs index a65d9e84a6d..c0a8896fde0 100644 --- a/components/script/dom/servohtmlparser.rs +++ b/components/script/dom/servohtmlparser.rs @@ -14,13 +14,14 @@ use dom::bindings::codegen::Bindings::ServoHTMLParserBinding; use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, Root}; use dom::bindings::refcounted::Trusted; -use dom::bindings::reflector::{Reflector, reflect_dom_object}; +use dom::bindings::reflector::reflect_dom_object; use dom::bindings::str::DOMString; use dom::bindings::trace::JSTraceable; use dom::document::Document; use dom::globalscope::GlobalScope; use dom::htmlimageelement::HTMLImageElement; use dom::node::Node; +use dom::servoparser::ServoParser; use dom::window::Window; use encoding::all::UTF_8; use encoding::types::{DecoderTrap, Encoding}; @@ -212,7 +213,7 @@ impl PreInvoke for ParserContext { #[dom_struct] pub struct ServoHTMLParser { - reflector_: Reflector, + servoparser: ServoParser, #[ignore_heap_size_of = "Defined in html5ever"] tokenizer: DOMRefCell, /// Input chunks received but not yet passed to the parser. @@ -269,7 +270,7 @@ impl ServoHTMLParser { let tok = tokenizer::Tokenizer::new(tb, Default::default()); let parser = ServoHTMLParser { - reflector_: Reflector::new(), + servoparser: ServoParser::new_inherited(), tokenizer: DOMRefCell::new(tok), pending_input: DOMRefCell::new(vec!()), document: JS::from_ref(document), @@ -305,7 +306,7 @@ impl ServoHTMLParser { let tok = tokenizer::Tokenizer::new(tb, tok_opts); let parser = ServoHTMLParser { - reflector_: Reflector::new(), + servoparser: ServoParser::new_inherited(), tokenizer: DOMRefCell::new(tok), pending_input: DOMRefCell::new(vec!()), document: JS::from_ref(document), diff --git a/components/script/dom/servoparser.rs b/components/script/dom/servoparser.rs new file mode 100644 index 00000000000..18cd881f812 --- /dev/null +++ b/components/script/dom/servoparser.rs @@ -0,0 +1,18 @@ +/* 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::reflector::Reflector; + +#[dom_struct] +pub struct ServoParser { + reflector: Reflector, +} + +impl ServoParser { + pub fn new_inherited() -> Self { + ServoParser { + reflector: Reflector::new(), + } + } +} diff --git a/components/script/dom/servoxmlparser.rs b/components/script/dom/servoxmlparser.rs index 2b79a5cf117..e4f2914dc15 100644 --- a/components/script/dom/servoxmlparser.rs +++ b/components/script/dom/servoxmlparser.rs @@ -5,10 +5,11 @@ use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::ServoXMLParserBinding; use dom::bindings::js::{JS, Root}; -use dom::bindings::reflector::{Reflector, reflect_dom_object}; +use dom::bindings::reflector::reflect_dom_object; use dom::bindings::trace::JSTraceable; use dom::document::Document; use dom::node::Node; +use dom::servoparser::ServoParser; use dom::window::Window; use js::jsapi::JSTracer; use msg::constellation_msg::PipelineId; @@ -31,7 +32,7 @@ pub struct Sink { #[must_root] #[dom_struct] pub struct ServoXMLParser { - reflector_: Reflector, + servoparser: ServoParser, #[ignore_heap_size_of = "Defined in xml5ever"] tokenizer: DOMRefCell, /// Input chunks received but not yet passed to the parser. @@ -85,7 +86,7 @@ impl ServoXMLParser { let tok = tokenizer::XmlTokenizer::new(tb, Default::default()); let parser = ServoXMLParser { - reflector_: Reflector::new(), + servoparser: ServoParser::new_inherited(), tokenizer: DOMRefCell::new(tok), pending_input: DOMRefCell::new(vec!()), document: JS::from_ref(document), diff --git a/components/script/dom/webidls/ServoHTMLParser.webidl b/components/script/dom/webidls/ServoHTMLParser.webidl index b05515dcf3c..ddf9382ff9a 100644 --- a/components/script/dom/webidls/ServoHTMLParser.webidl +++ b/components/script/dom/webidls/ServoHTMLParser.webidl @@ -7,5 +7,5 @@ // FIXME: find a better way to hide this from content (#3688) [NoInterfaceObject, Exposed=(Window,Worker)] -interface ServoHTMLParser { +interface ServoHTMLParser : ServoParser { }; diff --git a/components/script/dom/webidls/ServoParser.webidl b/components/script/dom/webidls/ServoParser.webidl new file mode 100644 index 00000000000..435ca1fb90f --- /dev/null +++ b/components/script/dom/webidls/ServoParser.webidl @@ -0,0 +1,10 @@ +/* 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. + +[Exposed=(Window,Worker), + Inline] +interface ServoParser {}; diff --git a/components/script/dom/webidls/ServoXMLParser.webidl b/components/script/dom/webidls/ServoXMLParser.webidl index 1111499de1b..3f6f03822ad 100644 --- a/components/script/dom/webidls/ServoXMLParser.webidl +++ b/components/script/dom/webidls/ServoXMLParser.webidl @@ -6,6 +6,6 @@ // web pages. [NoInterfaceObject, Exposed=(Window,Worker)] -interface ServoXMLParser { +interface ServoXMLParser : ServoParser { }; From 27f245e6aeb9a690bf6367789045b7ff142a7b63 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Sat, 8 Oct 2016 14:02:41 +0200 Subject: [PATCH 2/9] Introduce ServoParser::document --- components/script/dom/servohtmlparser.rs | 29 +++++++++--------------- components/script/dom/servoparser.rs | 11 ++++++++- components/script/dom/servoxmlparser.rs | 18 +++++---------- components/script/parse/mod.rs | 17 +++++++------- 4 files changed, 36 insertions(+), 39 deletions(-) diff --git a/components/script/dom/servohtmlparser.rs b/components/script/dom/servohtmlparser.rs index c0a8896fde0..3ed656c2bb4 100644 --- a/components/script/dom/servohtmlparser.rs +++ b/components/script/dom/servohtmlparser.rs @@ -121,7 +121,7 @@ impl AsyncResponseListener for ParserContext { parser.pending_input().borrow_mut().push(page); parser.parse_sync(); - let doc = parser.document(); + let doc = parser.as_servo_parser().document(); let doc_body = Root::upcast::(doc.GetBody().unwrap()); let img = HTMLImageElement::new(atom!("img"), None, doc); img.SetSrc(DOMString::from(self.url.to_string())); @@ -199,7 +199,8 @@ impl AsyncResponseListener for ParserContext { debug!("Failed to load page URL {}, error: {:?}", self.url, err); } - parser.r().document().finish_load(LoadType::PageSource(self.url.clone())); + parser.r().as_servo_parser().document() + .finish_load(LoadType::PageSource(self.url.clone())); parser.r().last_chunk_received().set(true); if !parser.r().is_suspended() { @@ -218,8 +219,6 @@ pub struct ServoHTMLParser { tokenizer: DOMRefCell, /// Input chunks received but not yet passed to the parser. pending_input: DOMRefCell>, - /// The document associated with this parser. - document: JS, /// True if this parser should avoid passing any further data to the tokenizer. suspended: Cell, /// Whether to expect any further input from the associated network request. @@ -231,7 +230,7 @@ pub struct ServoHTMLParser { impl<'a> Parser for &'a ServoHTMLParser { fn parse_chunk(self, input: String) { - self.document.set_current_parser(Some(ParserRef::HTML(self))); + self.upcast().document().set_current_parser(Some(ParserRef::HTML(self))); self.pending_input.borrow_mut().push(input); if !self.is_suspended() { self.parse_sync(); @@ -245,7 +244,7 @@ impl<'a> Parser for &'a ServoHTMLParser { self.tokenizer.borrow_mut().end(); debug!("finished parsing"); - self.document.set_current_parser(None); + self.upcast().document().set_current_parser(None); if let Some(pipeline) = self.pipeline { ScriptThread::parsing_complete(pipeline); @@ -270,10 +269,9 @@ impl ServoHTMLParser { let tok = tokenizer::Tokenizer::new(tb, Default::default()); let parser = ServoHTMLParser { - servoparser: ServoParser::new_inherited(), + servoparser: ServoParser::new_inherited(document), tokenizer: DOMRefCell::new(tok), pending_input: DOMRefCell::new(vec!()), - document: JS::from_ref(document), suspended: Cell::new(false), last_chunk_received: Cell::new(false), pipeline: pipeline, @@ -306,10 +304,9 @@ impl ServoHTMLParser { let tok = tokenizer::Tokenizer::new(tb, tok_opts); let parser = ServoHTMLParser { - servoparser: ServoParser::new_inherited(), + servoparser: ServoParser::new_inherited(document), tokenizer: DOMRefCell::new(tok), pending_input: DOMRefCell::new(vec!()), - document: JS::from_ref(document), suspended: Cell::new(false), last_chunk_received: Cell::new(true), pipeline: None, @@ -340,13 +337,13 @@ impl ServoHTMLParser { impl ServoHTMLParser { pub fn parse_sync(&self) { let metadata = TimerMetadata { - url: self.document.url().as_str().into(), + url: self.upcast().document().url().as_str().into(), iframe: TimerMetadataFrameType::RootWindow, incremental: TimerMetadataReflowType::FirstReflow, }; profile(ProfilerCategory::ScriptParseHTML, Some(metadata), - self.document.window().upcast::().time_profiler_chan().clone(), + self.upcast().document().window().upcast::().time_profiler_chan().clone(), || self.do_parse_sync()) } @@ -354,7 +351,7 @@ impl ServoHTMLParser { // 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(); + self.upcast().document().reflow_if_reflow_timer_expired(); let mut pending_input = self.pending_input.borrow_mut(); if !pending_input.is_empty() { let chunk = pending_input.remove(0); @@ -379,7 +376,7 @@ impl ServoHTMLParser { } pub fn window(&self) -> &Window { - self.document.window() + self.upcast().document().window() } pub fn suspend(&self) { @@ -397,10 +394,6 @@ impl ServoHTMLParser { self.suspended.get() } - pub fn document(&self) -> &Document { - &self.document - } - pub fn last_chunk_received(&self) -> &Cell { &self.last_chunk_received } diff --git a/components/script/dom/servoparser.rs b/components/script/dom/servoparser.rs index 18cd881f812..dc50569c3fc 100644 --- a/components/script/dom/servoparser.rs +++ b/components/script/dom/servoparser.rs @@ -3,16 +3,25 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use dom::bindings::reflector::Reflector; +use dom::bindings::js::JS; +use dom::document::Document; #[dom_struct] pub struct ServoParser { reflector: Reflector, + /// The document associated with this parser. + document: JS, } impl ServoParser { - pub fn new_inherited() -> Self { + pub fn new_inherited(document: &Document) -> Self { ServoParser { reflector: Reflector::new(), + document: JS::from_ref(document), } } + + pub fn document(&self) -> &Document { + &self.document + } } diff --git a/components/script/dom/servoxmlparser.rs b/components/script/dom/servoxmlparser.rs index e4f2914dc15..e5a1d78da7f 100644 --- a/components/script/dom/servoxmlparser.rs +++ b/components/script/dom/servoxmlparser.rs @@ -4,6 +4,7 @@ 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; @@ -37,8 +38,6 @@ pub struct ServoXMLParser { tokenizer: DOMRefCell, /// Input chunks received but not yet passed to the parser. pending_input: DOMRefCell>, - /// The document associated with this parser. - document: JS, /// True if this parser should avoid passing any further data to the tokenizer. suspended: Cell, /// Whether to expect any further input from the associated network request. @@ -50,7 +49,7 @@ pub struct ServoXMLParser { impl<'a> Parser for &'a ServoXMLParser { fn parse_chunk(self, input: String) { - self.document.set_current_parser(Some(ParserRef::XML(self))); + self.upcast().document().set_current_parser(Some(ParserRef::XML(self))); self.pending_input.borrow_mut().push(input); if !self.is_suspended() { self.parse_sync(); @@ -64,7 +63,7 @@ impl<'a> Parser for &'a ServoXMLParser { self.tokenizer.borrow_mut().end(); debug!("finished parsing"); - self.document.set_current_parser(None); + self.upcast().document().set_current_parser(None); if let Some(pipeline) = self.pipeline { ScriptThread::parsing_complete(pipeline); @@ -86,10 +85,9 @@ impl ServoXMLParser { let tok = tokenizer::XmlTokenizer::new(tb, Default::default()); let parser = ServoXMLParser { - servoparser: ServoParser::new_inherited(), + servoparser: ServoParser::new_inherited(document), tokenizer: DOMRefCell::new(tok), pending_input: DOMRefCell::new(vec!()), - document: JS::from_ref(document), suspended: Cell::new(false), last_chunk_received: Cell::new(false), pipeline: pipeline, @@ -99,7 +97,7 @@ impl ServoXMLParser { } pub fn window(&self) -> &Window { - self.document.window() + self.upcast().document().window() } pub fn resume(&self) { @@ -121,7 +119,7 @@ impl ServoXMLParser { // 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(); + self.upcast().document().reflow_if_reflow_timer_expired(); let mut pending_input = self.pending_input.borrow_mut(); if !pending_input.is_empty() { let chunk = pending_input.remove(0); @@ -157,10 +155,6 @@ impl ServoXMLParser { self.tokenizer.borrow_mut().end() } - pub fn document(&self) -> &Document { - &self.document - } - pub fn last_chunk_received(&self) -> &Cell { &self.last_chunk_received } diff --git a/components/script/parse/mod.rs b/components/script/parse/mod.rs index 1f561df0e16..52493d3bb71 100644 --- a/components/script/parse/mod.rs +++ b/components/script/parse/mod.rs @@ -3,10 +3,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use dom::bindings::cell::DOMRefCell; +use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, Root}; use dom::bindings::refcounted::Trusted; -use dom::document::Document; use dom::servohtmlparser::ServoHTMLParser; +use dom::servoparser::ServoParser; use dom::servoxmlparser::ServoXMLParser; use dom::window::Window; use std::cell::Cell; @@ -104,6 +105,13 @@ pub enum ParserRef<'a> { } 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), @@ -160,13 +168,6 @@ impl<'a> ParserRef<'a> { } } - pub fn document(&self) -> &Document { - match *self { - ParserRef::HTML(parser) => parser.document(), - ParserRef::XML(parser) => parser.document(), - } - } - pub fn last_chunk_received(&self) -> &Cell { match *self { ParserRef::HTML(parser) => parser.last_chunk_received(), From e1a1bf46cadd1787f543ed75d24c35bf2ae79092 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Sat, 8 Oct 2016 15:48:47 +0200 Subject: [PATCH 3/9] Move pending input logic to ServoParser --- components/script/dom/servohtmlparser.rs | 32 ++++++++---------------- components/script/dom/servoparser.rs | 21 ++++++++++++++++ components/script/dom/servoxmlparser.rs | 19 ++++---------- components/script/parse/mod.rs | 8 ------ 4 files changed, 37 insertions(+), 43 deletions(-) diff --git a/components/script/dom/servohtmlparser.rs b/components/script/dom/servohtmlparser.rs index 3ed656c2bb4..aa80d387dab 100644 --- a/components/script/dom/servohtmlparser.rs +++ b/components/script/dom/servohtmlparser.rs @@ -107,6 +107,7 @@ impl AsyncResponseListener for ParserContext { }; let parser = parser.r(); + let servo_parser = parser.as_servo_parser(); self.parser = Some(match parser { ParserRef::HTML(parser) => TrustedParser::HTML( Trusted::new(parser)), @@ -118,10 +119,10 @@ impl AsyncResponseListener for ParserContext { Some(ContentType(Mime(TopLevel::Image, _, _))) => { self.is_synthesized_document = true; let page = "".into(); - parser.pending_input().borrow_mut().push(page); + servo_parser.push_input_chunk(page); parser.parse_sync(); - let doc = parser.as_servo_parser().document(); + let doc = servo_parser.document(); let doc_body = Root::upcast::(doc.GetBody().unwrap()); let img = HTMLImageElement::new(atom!("img"), None, doc); img.SetSrc(DOMString::from(self.url.to_string())); @@ -131,7 +132,7 @@ impl AsyncResponseListener for ParserContext { Some(ContentType(Mime(TopLevel::Text, SubLevel::Plain, _))) => { // https://html.spec.whatwg.org/multipage/#read-text let page = "
\n".into();
-                parser.pending_input().borrow_mut().push(page);
+                servo_parser.push_input_chunk(page);
                 parser.parse_sync();
                 parser.set_plaintext_state();
             },
@@ -141,7 +142,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);
-                    parser.pending_input().borrow_mut().push(page);
+                    servo_parser.push_input_chunk(page);
                     parser.parse_sync();
                 }
             },
@@ -156,7 +157,7 @@ impl AsyncResponseListener for ParserContext {
                 let page = format!("

Unknown content type ({}/{}).

", toplevel.as_str(), sublevel.as_str()); self.is_synthesized_document = true; - parser.pending_input().borrow_mut().push(page); + servo_parser.push_input_chunk(page); parser.parse_sync(); }, None => { @@ -192,7 +193,7 @@ impl AsyncResponseListener for ParserContext { let page_bytes = read_resource_file("neterror.html").unwrap(); let page = String::from_utf8(page_bytes).unwrap(); let page = page.replace("${reason}", reason); - parser.pending_input().borrow_mut().push(page); + parser.as_servo_parser().push_input_chunk(page); parser.parse_sync(); } else if let Err(err) = status { // TODO(Savago): we should send a notification to callers #5463. @@ -217,8 +218,6 @@ pub struct ServoHTMLParser { servoparser: ServoParser, #[ignore_heap_size_of = "Defined in html5ever"] tokenizer: DOMRefCell, - /// Input chunks received but not yet passed to the parser. - pending_input: DOMRefCell>, /// True if this parser should avoid passing any further data to the tokenizer. suspended: Cell, /// Whether to expect any further input from the associated network request. @@ -231,7 +230,7 @@ pub struct ServoHTMLParser { impl<'a> Parser for &'a ServoHTMLParser { fn parse_chunk(self, input: String) { self.upcast().document().set_current_parser(Some(ParserRef::HTML(self))); - self.pending_input.borrow_mut().push(input); + self.upcast().push_input_chunk(input); if !self.is_suspended() { self.parse_sync(); } @@ -239,7 +238,7 @@ impl<'a> Parser for &'a ServoHTMLParser { fn finish(self) { assert!(!self.suspended.get()); - assert!(self.pending_input.borrow().is_empty()); + assert!(!self.upcast().has_pending_input()); self.tokenizer.borrow_mut().end(); debug!("finished parsing"); @@ -271,7 +270,6 @@ impl ServoHTMLParser { let parser = ServoHTMLParser { servoparser: ServoParser::new_inherited(document), tokenizer: DOMRefCell::new(tok), - pending_input: DOMRefCell::new(vec!()), suspended: Cell::new(false), last_chunk_received: Cell::new(false), pipeline: pipeline, @@ -306,7 +304,6 @@ impl ServoHTMLParser { let parser = ServoHTMLParser { servoparser: ServoParser::new_inherited(document), tokenizer: DOMRefCell::new(tok), - pending_input: DOMRefCell::new(vec!()), suspended: Cell::new(false), last_chunk_received: Cell::new(true), pipeline: None, @@ -327,11 +324,6 @@ impl ServoHTMLParser { pub fn end_tokenizer(&self) { self.tokenizer.borrow_mut().end() } - - pub fn pending_input(&self) -> &DOMRefCell> { - &self.pending_input - } - } impl ServoHTMLParser { @@ -352,9 +344,7 @@ impl ServoHTMLParser { // the parser remains unsuspended. loop { self.upcast().document().reflow_if_reflow_timer_expired(); - let mut pending_input = self.pending_input.borrow_mut(); - if !pending_input.is_empty() { - let chunk = pending_input.remove(0); + if let Some(chunk) = self.upcast().take_next_input_chunk() { self.tokenizer.borrow_mut().feed(chunk.into()); } else { self.tokenizer.borrow_mut().run(); @@ -365,7 +355,7 @@ impl ServoHTMLParser { return; } - if pending_input.is_empty() { + if !self.upcast().has_pending_input() { break; } } diff --git a/components/script/dom/servoparser.rs b/components/script/dom/servoparser.rs index dc50569c3fc..1cdef6de642 100644 --- a/components/script/dom/servoparser.rs +++ b/components/script/dom/servoparser.rs @@ -2,6 +2,7 @@ * 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::reflector::Reflector; use dom::bindings::js::JS; use dom::document::Document; @@ -11,6 +12,8 @@ pub struct ServoParser { reflector: Reflector, /// The document associated with this parser. document: JS, + /// Input chunks received but not yet passed to the parser. + pending_input: DOMRefCell>, } impl ServoParser { @@ -18,10 +21,28 @@ impl ServoParser { ServoParser { reflector: Reflector::new(), document: JS::from_ref(document), + pending_input: DOMRefCell::new(vec![]), } } pub fn document(&self) -> &Document { &self.document } + + pub fn has_pending_input(&self) -> bool { + !self.pending_input.borrow().is_empty() + } + + pub fn push_input_chunk(&self, chunk: String) { + self.pending_input.borrow_mut().push(chunk); + } + + pub fn take_next_input_chunk(&self) -> Option { + let mut pending_input = self.pending_input.borrow_mut(); + if pending_input.is_empty() { + None + } else { + Some(pending_input.remove(0)) + } + } } diff --git a/components/script/dom/servoxmlparser.rs b/components/script/dom/servoxmlparser.rs index e5a1d78da7f..dd107c8d3c4 100644 --- a/components/script/dom/servoxmlparser.rs +++ b/components/script/dom/servoxmlparser.rs @@ -36,8 +36,6 @@ pub struct ServoXMLParser { servoparser: ServoParser, #[ignore_heap_size_of = "Defined in xml5ever"] tokenizer: DOMRefCell, - /// Input chunks received but not yet passed to the parser. - pending_input: DOMRefCell>, /// True if this parser should avoid passing any further data to the tokenizer. suspended: Cell, /// Whether to expect any further input from the associated network request. @@ -50,7 +48,7 @@ pub struct ServoXMLParser { impl<'a> Parser for &'a ServoXMLParser { fn parse_chunk(self, input: String) { self.upcast().document().set_current_parser(Some(ParserRef::XML(self))); - self.pending_input.borrow_mut().push(input); + self.upcast().push_input_chunk(input); if !self.is_suspended() { self.parse_sync(); } @@ -58,7 +56,7 @@ impl<'a> Parser for &'a ServoXMLParser { fn finish(self) { assert!(!self.suspended.get()); - assert!(self.pending_input.borrow().is_empty()); + assert!(!self.upcast().has_pending_input()); self.tokenizer.borrow_mut().end(); debug!("finished parsing"); @@ -87,7 +85,6 @@ impl ServoXMLParser { let parser = ServoXMLParser { servoparser: ServoParser::new_inherited(document), tokenizer: DOMRefCell::new(tok), - pending_input: DOMRefCell::new(vec!()), suspended: Cell::new(false), last_chunk_received: Cell::new(false), pipeline: pipeline, @@ -119,10 +116,8 @@ impl ServoXMLParser { // 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(); - let mut pending_input = self.pending_input.borrow_mut(); - if !pending_input.is_empty() { - let chunk = pending_input.remove(0); + 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(); @@ -133,7 +128,7 @@ impl ServoXMLParser { return; } - if pending_input.is_empty() { + if !self.upcast().has_pending_input() { break; } } @@ -143,10 +138,6 @@ impl ServoXMLParser { } } - pub fn pending_input(&self) -> &DOMRefCell> { - &self.pending_input - } - pub fn set_plaintext_state(&self) { //self.tokenizer.borrow_mut().set_plaintext_state() } diff --git a/components/script/parse/mod.rs b/components/script/parse/mod.rs index 52493d3bb71..30f6bc158a6 100644 --- a/components/script/parse/mod.rs +++ b/components/script/parse/mod.rs @@ -2,7 +2,6 @@ * 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::inheritance::Castable; use dom::bindings::js::{JS, Root}; use dom::bindings::refcounted::Trusted; @@ -147,13 +146,6 @@ impl<'a> ParserRef<'a> { } } - pub fn pending_input(&self) -> &DOMRefCell> { - match *self { - ParserRef::HTML(parser) => parser.pending_input(), - ParserRef::XML(parser) => parser.pending_input(), - } - } - pub fn set_plaintext_state(&self) { match *self { ParserRef::HTML(parser) => parser.set_plaintext_state(), From 881f7f4de7132cbe7f5aec17f535ff501112ac3c Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Sat, 8 Oct 2016 15:55:04 +0200 Subject: [PATCH 4/9] Move last chunk received logic to ServoParser --- components/script/dom/servohtmlparser.rs | 25 ++++++++++-------------- components/script/dom/servoparser.rs | 14 ++++++++++++- components/script/dom/servoxmlparser.rs | 11 ++--------- components/script/parse/mod.rs | 8 -------- 4 files changed, 25 insertions(+), 33 deletions(-) diff --git a/components/script/dom/servohtmlparser.rs b/components/script/dom/servohtmlparser.rs index aa80d387dab..95f6004d225 100644 --- a/components/script/dom/servohtmlparser.rs +++ b/components/script/dom/servohtmlparser.rs @@ -200,12 +200,15 @@ impl AsyncResponseListener for ParserContext { debug!("Failed to load page URL {}, error: {:?}", self.url, err); } - parser.r().as_servo_parser().document() + let parser = parser.r(); + let servo_parser = parser.as_servo_parser(); + + servo_parser.document() .finish_load(LoadType::PageSource(self.url.clone())); - parser.r().last_chunk_received().set(true); - if !parser.r().is_suspended() { - parser.r().parse_sync(); + servo_parser.mark_last_chunk_received(); + if !parser.is_suspended() { + parser.parse_sync(); } } } @@ -220,8 +223,6 @@ pub struct ServoHTMLParser { tokenizer: DOMRefCell, /// True if this parser should avoid passing any further data to the tokenizer. suspended: Cell, - /// Whether to expect any further input from the associated network request. - last_chunk_received: Cell, /// The pipeline associated with this parse, unavailable if this parse does not /// correspond to a page load. pipeline: Option, @@ -268,10 +269,9 @@ impl ServoHTMLParser { let tok = tokenizer::Tokenizer::new(tb, Default::default()); let parser = ServoHTMLParser { - servoparser: ServoParser::new_inherited(document), + servoparser: ServoParser::new_inherited(document, false), tokenizer: DOMRefCell::new(tok), suspended: Cell::new(false), - last_chunk_received: Cell::new(false), pipeline: pipeline, }; @@ -302,10 +302,9 @@ impl ServoHTMLParser { let tok = tokenizer::Tokenizer::new(tb, tok_opts); let parser = ServoHTMLParser { - servoparser: ServoParser::new_inherited(document), + servoparser: ServoParser::new_inherited(document, true), tokenizer: DOMRefCell::new(tok), suspended: Cell::new(false), - last_chunk_received: Cell::new(true), pipeline: None, }; @@ -360,7 +359,7 @@ impl ServoHTMLParser { } } - if self.last_chunk_received.get() { + if self.upcast().last_chunk_received() { self.finish(); } } @@ -383,10 +382,6 @@ impl ServoHTMLParser { pub fn is_suspended(&self) -> bool { self.suspended.get() } - - pub fn last_chunk_received(&self) -> &Cell { - &self.last_chunk_received - } } struct Tracer { diff --git a/components/script/dom/servoparser.rs b/components/script/dom/servoparser.rs index 1cdef6de642..c5611a6364d 100644 --- a/components/script/dom/servoparser.rs +++ b/components/script/dom/servoparser.rs @@ -6,6 +6,7 @@ use dom::bindings::cell::DOMRefCell; use dom::bindings::reflector::Reflector; use dom::bindings::js::JS; use dom::document::Document; +use std::cell::Cell; #[dom_struct] pub struct ServoParser { @@ -14,14 +15,17 @@ pub struct ServoParser { document: JS, /// Input chunks received but not yet passed to the parser. pending_input: DOMRefCell>, + /// Whether to expect any further input from the associated network request. + last_chunk_received: Cell, } impl ServoParser { - pub fn new_inherited(document: &Document) -> Self { + pub fn new_inherited(document: &Document, last_chunk_received: bool) -> Self { ServoParser { reflector: Reflector::new(), document: JS::from_ref(document), pending_input: DOMRefCell::new(vec![]), + last_chunk_received: Cell::new(last_chunk_received), } } @@ -45,4 +49,12 @@ impl ServoParser { Some(pending_input.remove(0)) } } + + pub fn last_chunk_received(&self) -> bool { + self.last_chunk_received.get() + } + + pub fn mark_last_chunk_received(&self) { + self.last_chunk_received.set(true) + } } diff --git a/components/script/dom/servoxmlparser.rs b/components/script/dom/servoxmlparser.rs index dd107c8d3c4..4dc9b942b7b 100644 --- a/components/script/dom/servoxmlparser.rs +++ b/components/script/dom/servoxmlparser.rs @@ -38,8 +38,6 @@ pub struct ServoXMLParser { tokenizer: DOMRefCell, /// True if this parser should avoid passing any further data to the tokenizer. suspended: Cell, - /// Whether to expect any further input from the associated network request. - last_chunk_received: Cell, /// The pipeline associated with this parse, unavailable if this parse does not /// correspond to a page load. pipeline: Option, @@ -83,10 +81,9 @@ impl ServoXMLParser { let tok = tokenizer::XmlTokenizer::new(tb, Default::default()); let parser = ServoXMLParser { - servoparser: ServoParser::new_inherited(document), + servoparser: ServoParser::new_inherited(document, false), tokenizer: DOMRefCell::new(tok), suspended: Cell::new(false), - last_chunk_received: Cell::new(false), pipeline: pipeline, }; @@ -133,7 +130,7 @@ impl ServoXMLParser { } } - if self.last_chunk_received.get() { + if self.upcast().last_chunk_received() { self.finish(); } } @@ -146,10 +143,6 @@ impl ServoXMLParser { self.tokenizer.borrow_mut().end() } - pub fn last_chunk_received(&self) -> &Cell { - &self.last_chunk_received - } - pub fn tokenizer(&self) -> &DOMRefCell { &self.tokenizer } diff --git a/components/script/parse/mod.rs b/components/script/parse/mod.rs index 30f6bc158a6..ba05ec839d4 100644 --- a/components/script/parse/mod.rs +++ b/components/script/parse/mod.rs @@ -9,7 +9,6 @@ use dom::servohtmlparser::ServoHTMLParser; use dom::servoparser::ServoParser; use dom::servoxmlparser::ServoXMLParser; use dom::window::Window; -use std::cell::Cell; use std::cell::UnsafeCell; use std::ptr; @@ -159,12 +158,5 @@ impl<'a> ParserRef<'a> { ParserRef::XML(parser) => parser.parse_sync(), } } - - pub fn last_chunk_received(&self) -> &Cell { - match *self { - ParserRef::HTML(parser) => parser.last_chunk_received(), - ParserRef::XML(parser) => parser.last_chunk_received(), - } - } } From 02162a8bdae61430a1b9f4529330fad699072ff3 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Sat, 8 Oct 2016 17:59:51 +0200 Subject: [PATCH 5/9] Move ParserContext to script::parse --- components/script/dom/servohtmlparser.rs | 172 +---------------------- components/script/parse/mod.rs | 172 +++++++++++++++++++++++ components/script/script_thread.rs | 3 +- 3 files changed, 174 insertions(+), 173 deletions(-) diff --git a/components/script/dom/servohtmlparser.rs b/components/script/dom/servohtmlparser.rs index 95f6004d225..eed6efb4b20 100644 --- a/components/script/dom/servohtmlparser.rs +++ b/components/script/dom/servohtmlparser.rs @@ -5,44 +5,29 @@ //! The bulk of the HTML parser integration is in `script::parse::html`. //! This module is mostly about its interaction with DOM memory management. -use document_loader::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::ServoHTMLParserBinding; use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, Root}; -use dom::bindings::refcounted::Trusted; use dom::bindings::reflector::reflect_dom_object; -use dom::bindings::str::DOMString; use dom::bindings::trace::JSTraceable; use dom::document::Document; use dom::globalscope::GlobalScope; -use dom::htmlimageelement::HTMLImageElement; use dom::node::Node; use dom::servoparser::ServoParser; use dom::window::Window; -use encoding::all::UTF_8; -use encoding::types::{DecoderTrap, Encoding}; use html5ever::tokenizer; use html5ever::tree_builder; use html5ever::tree_builder::{TreeBuilder, TreeBuilderOpts}; -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::{AsyncResponseListener, Metadata, NetworkError}; -use network_listener::PreInvoke; -use parse::{Parser, ParserRef, TrustedParser}; +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; -use util::resource_files::read_resource_file; #[must_root] #[derive(JSTraceable, HeapSizeOf)] @@ -61,161 +46,6 @@ pub struct FragmentContext<'a> { pub type Tokenizer = tokenizer::Tokenizer, Sink>>; -/// The context required for asynchronously fetching a document and parsing it progressively. -pub struct ParserContext { - /// The parser that initiated the request. - parser: Option, - /// Is this a synthesized document - is_synthesized_document: bool, - /// The pipeline associated with this document. - id: PipelineId, - /// The URL for this document. - url: Url, -} - -impl ParserContext { - pub fn new(id: PipelineId, url: Url) -> ParserContext { - ParserContext { - parser: None, - is_synthesized_document: false, - id: id, - url: url, - } - } -} - -impl AsyncResponseListener for ParserContext { - fn headers_available(&mut self, meta_result: Result) { - let mut ssl_error = None; - let metadata = match meta_result { - Ok(meta) => Some(meta), - Err(NetworkError::SslValidation(url, reason)) => { - ssl_error = Some(reason); - let mut meta = Metadata::default(url); - let mime: Option = "text/html".parse().ok(); - meta.set_content_type(mime.as_ref()); - Some(meta) - }, - Err(_) => None, - }; - let content_type = - metadata.clone().and_then(|meta| meta.content_type).map(Serde::into_inner); - let parser = match ScriptThread::page_headers_available(&self.id, - metadata) { - Some(parser) => parser, - 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)), - }); - - match content_type { - Some(ContentType(Mime(TopLevel::Image, _, _))) => { - self.is_synthesized_document = true; - let page = "".into(); - servo_parser.push_input_chunk(page); - parser.parse_sync(); - - let doc = servo_parser.document(); - let doc_body = Root::upcast::(doc.GetBody().unwrap()); - let img = HTMLImageElement::new(atom!("img"), None, doc); - img.SetSrc(DOMString::from(self.url.to_string())); - doc_body.AppendChild(&Root::upcast::(img)).expect("Appending failed"); - - }, - Some(ContentType(Mime(TopLevel::Text, SubLevel::Plain, _))) => { - // https://html.spec.whatwg.org/multipage/#read-text - let page = "
\n".into();
-                servo_parser.push_input_chunk(page);
-                parser.parse_sync();
-                parser.set_plaintext_state();
-            },
-            Some(ContentType(Mime(TopLevel::Text, SubLevel::Html, _))) => { // Handle text/html
-                if let Some(reason) = ssl_error {
-                    self.is_synthesized_document = true;
-                    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.parse_sync();
-                }
-            },
-            Some(ContentType(Mime(TopLevel::Text, SubLevel::Xml, _))) => {}, // Handle text/xml
-            Some(ContentType(Mime(toplevel, sublevel, _))) => {
-                if toplevel.as_str() == "application" && sublevel.as_str() == "xhtml+xml" {
-                    // Handle xhtml (application/xhtml+xml).
-                    return;
-                }
-
-                // Show warning page for unknown mime types.
-                let page = format!("

Unknown content type ({}/{}).

", - toplevel.as_str(), sublevel.as_str()); - self.is_synthesized_document = true; - servo_parser.push_input_chunk(page); - parser.parse_sync(); - }, - None => { - // No content-type header. - // Merge with #4212 when fixed. - } - } - } - - fn data_available(&mut self, payload: Vec) { - if !self.is_synthesized_document { - // FIXME: use Vec (html5ever #34) - let data = UTF_8.decode(&payload, DecoderTrap::Replace).unwrap(); - let parser = match self.parser.as_ref() { - Some(parser) => parser.root(), - None => return, - }; - parser.r().parse_chunk(data); - } - } - - fn response_complete(&mut self, status: Result<(), NetworkError>) { - let parser = match self.parser.as_ref() { - Some(parser) => parser.root(), - None => return, - }; - - if let Err(NetworkError::Internal(ref reason)) = status { - // 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.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() - .finish_load(LoadType::PageSource(self.url.clone())); - - servo_parser.mark_last_chunk_received(); - if !parser.is_suspended() { - parser.parse_sync(); - } - } -} - -impl PreInvoke for ParserContext { -} - #[dom_struct] pub struct ServoHTMLParser { servoparser: ServoParser, diff --git a/components/script/parse/mod.rs b/components/script/parse/mod.rs index ba05ec839d4..dc874baef45 100644 --- a/components/script/parse/mod.rs +++ b/components/script/parse/mod.rs @@ -2,15 +2,33 @@ * 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 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::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; +use hyper::mime::{Mime, SubLevel, TopLevel}; +use hyper_serde::Serde; +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; @@ -160,3 +178,157 @@ impl<'a> ParserRef<'a> { } } +/// The context required for asynchronously fetching a document +/// and parsing it progressively. +pub struct ParserContext { + /// The parser that initiated the request. + parser: Option, + /// Is this a synthesized document + is_synthesized_document: bool, + /// The pipeline associated with this document. + id: PipelineId, + /// The URL for this document. + url: Url, +} + +impl ParserContext { + pub fn new(id: PipelineId, url: Url) -> ParserContext { + ParserContext { + parser: None, + is_synthesized_document: false, + id: id, + url: url, + } + } +} + +impl AsyncResponseListener for ParserContext { + fn headers_available(&mut self, meta_result: Result) { + let mut ssl_error = None; + let metadata = match meta_result { + Ok(meta) => Some(meta), + Err(NetworkError::SslValidation(url, reason)) => { + ssl_error = Some(reason); + let mut meta = Metadata::default(url); + let mime: Option = "text/html".parse().ok(); + meta.set_content_type(mime.as_ref()); + Some(meta) + }, + Err(_) => None, + }; + let content_type = + metadata.clone().and_then(|meta| meta.content_type).map(Serde::into_inner); + let parser = match ScriptThread::page_headers_available(&self.id, + metadata) { + Some(parser) => parser, + 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)), + }); + + match content_type { + Some(ContentType(Mime(TopLevel::Image, _, _))) => { + self.is_synthesized_document = true; + let page = "".into(); + servo_parser.push_input_chunk(page); + parser.parse_sync(); + + let doc = servo_parser.document(); + let doc_body = Root::upcast::(doc.GetBody().unwrap()); + let img = HTMLImageElement::new(atom!("img"), None, doc); + img.SetSrc(DOMString::from(self.url.to_string())); + doc_body.AppendChild(&Root::upcast::(img)).expect("Appending failed"); + + }, + Some(ContentType(Mime(TopLevel::Text, SubLevel::Plain, _))) => { + // https://html.spec.whatwg.org/multipage/#read-text + let page = "
\n".into();
+                servo_parser.push_input_chunk(page);
+                parser.parse_sync();
+                parser.set_plaintext_state();
+            },
+            Some(ContentType(Mime(TopLevel::Text, SubLevel::Html, _))) => { // Handle text/html
+                if let Some(reason) = ssl_error {
+                    self.is_synthesized_document = true;
+                    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.parse_sync();
+                }
+            },
+            Some(ContentType(Mime(TopLevel::Text, SubLevel::Xml, _))) => {}, // Handle text/xml
+            Some(ContentType(Mime(toplevel, sublevel, _))) => {
+                if toplevel.as_str() == "application" && sublevel.as_str() == "xhtml+xml" {
+                    // Handle xhtml (application/xhtml+xml).
+                    return;
+                }
+
+                // Show warning page for unknown mime types.
+                let page = format!("

Unknown content type ({}/{}).

", + toplevel.as_str(), sublevel.as_str()); + self.is_synthesized_document = true; + servo_parser.push_input_chunk(page); + parser.parse_sync(); + }, + None => { + // No content-type header. + // Merge with #4212 when fixed. + } + } + } + + fn data_available(&mut self, payload: Vec) { + if !self.is_synthesized_document { + // FIXME: use Vec (html5ever #34) + let data = UTF_8.decode(&payload, DecoderTrap::Replace).unwrap(); + let parser = match self.parser.as_ref() { + Some(parser) => parser.root(), + None => return, + }; + parser.r().parse_chunk(data); + } + } + + fn response_complete(&mut self, status: Result<(), NetworkError>) { + let parser = match self.parser.as_ref() { + Some(parser) => parser.root(), + None => return, + }; + + if let Err(NetworkError::Internal(ref reason)) = status { + // 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.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() + .finish_load(LoadType::PageSource(self.url.clone())); + + servo_parser.mark_last_chunk_received(); + if !parser.is_suspended() { + parser.parse_sync(); + } + } +} + +impl PreInvoke for ParserContext {} diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 6e18216ddc4..b52fe1da126 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -44,7 +44,6 @@ use dom::htmlanchorelement::HTMLAnchorElement; use dom::node::{Node, NodeDamage, window_from_node}; use dom::serviceworker::TrustedServiceWorkerAddress; use dom::serviceworkerregistration::ServiceWorkerRegistration; -use dom::servohtmlparser::ParserContext; use dom::uievent::UIEvent; use dom::window::{ReflowReason, Window}; use dom::worker::TrustedWorkerAddress; @@ -71,7 +70,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::ParserRoot; +use parse::{ParserContext, ParserRoot}; use parse::html::{ParseContext, parse_html}; use parse::xml::{self, parse_xml}; use profile_traits::mem::{self, OpaqueSender, Report, ReportKind, ReportsChan}; From 1f23810a341612e5293e37829f68c72491dafb04 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Sat, 8 Oct 2016 23:50:58 +0200 Subject: [PATCH 6/9] Introduce ServoParser::pipeline --- components/script/dom/servohtmlparser.rs | 11 +++-------- components/script/dom/servoparser.rs | 15 ++++++++++++++- components/script/dom/servoxmlparser.rs | 8 ++------ 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/components/script/dom/servohtmlparser.rs b/components/script/dom/servohtmlparser.rs index eed6efb4b20..ec01187e2b4 100644 --- a/components/script/dom/servohtmlparser.rs +++ b/components/script/dom/servohtmlparser.rs @@ -53,9 +53,6 @@ pub struct ServoHTMLParser { tokenizer: DOMRefCell, /// True if this parser should avoid passing any further data to the tokenizer. suspended: Cell, - /// The pipeline associated with this parse, unavailable if this parse does not - /// correspond to a page load. - pipeline: Option, } impl<'a> Parser for &'a ServoHTMLParser { @@ -76,7 +73,7 @@ impl<'a> Parser for &'a ServoHTMLParser { self.upcast().document().set_current_parser(None); - if let Some(pipeline) = self.pipeline { + if let Some(pipeline) = self.upcast().pipeline() { ScriptThread::parsing_complete(pipeline); } } @@ -99,10 +96,9 @@ impl ServoHTMLParser { let tok = tokenizer::Tokenizer::new(tb, Default::default()); let parser = ServoHTMLParser { - servoparser: ServoParser::new_inherited(document, false), + servoparser: ServoParser::new_inherited(document, pipeline, false), tokenizer: DOMRefCell::new(tok), suspended: Cell::new(false), - pipeline: pipeline, }; reflect_dom_object(box parser, document.window(), ServoHTMLParserBinding::Wrap) @@ -132,10 +128,9 @@ impl ServoHTMLParser { let tok = tokenizer::Tokenizer::new(tb, tok_opts); let parser = ServoHTMLParser { - servoparser: ServoParser::new_inherited(document, true), + servoparser: ServoParser::new_inherited(document, None, true), tokenizer: DOMRefCell::new(tok), suspended: Cell::new(false), - pipeline: None, }; reflect_dom_object(box parser, document.window(), ServoHTMLParserBinding::Wrap) diff --git a/components/script/dom/servoparser.rs b/components/script/dom/servoparser.rs index c5611a6364d..4a2bc0de400 100644 --- a/components/script/dom/servoparser.rs +++ b/components/script/dom/servoparser.rs @@ -6,6 +6,7 @@ use dom::bindings::cell::DOMRefCell; use dom::bindings::reflector::Reflector; use dom::bindings::js::JS; use dom::document::Document; +use msg::constellation_msg::PipelineId; use std::cell::Cell; #[dom_struct] @@ -13,6 +14,9 @@ pub struct ServoParser { reflector: Reflector, /// The document associated with this parser. document: JS, + /// The pipeline associated with this parse, unavailable if this parse + /// does not correspond to a page load. + pipeline: Option, /// Input chunks received but not yet passed to the parser. pending_input: DOMRefCell>, /// Whether to expect any further input from the associated network request. @@ -20,10 +24,15 @@ pub struct ServoParser { } impl ServoParser { - pub fn new_inherited(document: &Document, last_chunk_received: bool) -> Self { + pub fn new_inherited( + document: &Document, + pipeline: Option, + last_chunk_received: bool) + -> Self { ServoParser { reflector: Reflector::new(), document: JS::from_ref(document), + pipeline: pipeline, pending_input: DOMRefCell::new(vec![]), last_chunk_received: Cell::new(last_chunk_received), } @@ -33,6 +42,10 @@ impl ServoParser { &self.document } + pub fn pipeline(&self) -> Option { + self.pipeline + } + pub fn has_pending_input(&self) -> bool { !self.pending_input.borrow().is_empty() } diff --git a/components/script/dom/servoxmlparser.rs b/components/script/dom/servoxmlparser.rs index 4dc9b942b7b..119dd8f4b47 100644 --- a/components/script/dom/servoxmlparser.rs +++ b/components/script/dom/servoxmlparser.rs @@ -38,9 +38,6 @@ pub struct ServoXMLParser { tokenizer: DOMRefCell, /// True if this parser should avoid passing any further data to the tokenizer. suspended: Cell, - /// The pipeline associated with this parse, unavailable if this parse does not - /// correspond to a page load. - pipeline: Option, } impl<'a> Parser for &'a ServoXMLParser { @@ -61,7 +58,7 @@ impl<'a> Parser for &'a ServoXMLParser { self.upcast().document().set_current_parser(None); - if let Some(pipeline) = self.pipeline { + if let Some(pipeline) = self.upcast().pipeline() { ScriptThread::parsing_complete(pipeline); } } @@ -81,10 +78,9 @@ impl ServoXMLParser { let tok = tokenizer::XmlTokenizer::new(tb, Default::default()); let parser = ServoXMLParser { - servoparser: ServoParser::new_inherited(document, false), + servoparser: ServoParser::new_inherited(document, pipeline, false), tokenizer: DOMRefCell::new(tok), suspended: Cell::new(false), - pipeline: pipeline, }; reflect_dom_object(box parser, document.window(), ServoXMLParserBinding::Wrap) From 609299e1e45e93939f75f8439fc7ac3276ca5881 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Sun, 9 Oct 2016 00:19:09 +0200 Subject: [PATCH 7/9] Add time profiling to ServoXMLParser::parse_sync --- components/profile/time.rs | 1 + components/profile_traits/time.rs | 1 + components/script/dom/servoxmlparser.rs | 15 +++++++++++++++ 3 files changed, 17 insertions(+) diff --git a/components/profile/time.rs b/components/profile/time.rs index e3a550322c0..62f85b362d2 100644 --- a/components/profile/time.rs +++ b/components/profile/time.rs @@ -138,6 +138,7 @@ impl Formattable for ProfilerCategory { ProfilerCategory::ScriptInputEvent => "Script Input Event", ProfilerCategory::ScriptNetworkEvent => "Script Network Event", ProfilerCategory::ScriptParseHTML => "Script Parse HTML", + ProfilerCategory::ScriptParseXML => "Script Parse XML", ProfilerCategory::ScriptPlannedNavigation => "Script Planned Navigation", ProfilerCategory::ScriptResize => "Script Resize", ProfilerCategory::ScriptEvent => "Script Event", diff --git a/components/profile_traits/time.rs b/components/profile_traits/time.rs index 64664265a1a..ec8a3617cd6 100644 --- a/components/profile_traits/time.rs +++ b/components/profile_traits/time.rs @@ -83,6 +83,7 @@ pub enum ProfilerCategory { ScriptWebSocketEvent = 0x73, ScriptWorkerEvent = 0x74, ScriptServiceWorkerEvent = 0x75, + ScriptParseXML = 0x76, ApplicationHeartbeat = 0x90, } diff --git a/components/script/dom/servoxmlparser.rs b/components/script/dom/servoxmlparser.rs index 119dd8f4b47..919dcfdb946 100644 --- a/components/script/dom/servoxmlparser.rs +++ b/components/script/dom/servoxmlparser.rs @@ -9,12 +9,15 @@ 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; @@ -106,6 +109,18 @@ impl ServoXMLParser { } 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::().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 { From 1405be691776e48836f651c3c616dc12322a0932 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Mon, 10 Oct 2016 16:11:00 +0200 Subject: [PATCH 8/9] Unify ServoHTMLParser and ServoXMLParser in ServoParser --- components/script/dom/document.rs | 8 +- components/script/dom/mod.rs | 2 - components/script/dom/servohtmlparser.rs | 235 ------------------ components/script/dom/servoparser.rs | 215 +++++++++++++++- components/script/dom/servoxmlparser.rs | 185 -------------- .../script/dom/webidls/ServoHTMLParser.webidl | 11 - .../script/dom/webidls/ServoParser.webidl | 2 +- .../script/dom/webidls/ServoXMLParser.webidl | 11 - components/script/parse/html.rs | 54 +++- components/script/parse/mod.rs | 192 ++------------ components/script/parse/xml.rs | 22 +- components/script/script_thread.rs | 9 +- 12 files changed, 300 insertions(+), 646 deletions(-) delete mode 100644 components/script/dom/servohtmlparser.rs delete mode 100644 components/script/dom/servoxmlparser.rs delete mode 100644 components/script/dom/webidls/ServoHTMLParser.webidl delete mode 100644 components/script/dom/webidls/ServoXMLParser.webidl 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, /// The current active HTML parser, to allow resuming after interruptions. - current_parser: MutNullableParserField, + current_parser: MutNullableHeap>, /// When we should kick off a reflow. This happens during parsing. reflow_timeout: Cell>, /// 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) { + pub fn set_current_parser(&self, script: Option<&ServoParser>) { self.current_parser.set(script); } - pub fn get_current_parser(&self) -> Option { + pub fn get_current_parser(&self) -> Option> { 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, - pub document: JS, -} - -/// 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, Sink>>; - -#[dom_struct] -pub struct ServoHTMLParser { - servoparser: ServoParser, - #[ignore_heap_size_of = "Defined in html5ever"] - tokenizer: DOMRefCell, - /// True if this parser should avoid passing any further data to the tokenizer. - suspended: Cell, -} - -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, document: &Document, pipeline: Option) - -> Root { - 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, document: &Document, - fragment_context: FragmentContext) -> Root { - 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 { - &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::().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; - #[allow(unrooted_must_root)] - fn trace_handle(&self, node: &JS) { - 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>; - - 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, /// Input chunks received but not yet passed to the parser. pending_input: DOMRefCell>, + /// The tokenizer of this parser. + tokenizer: DOMRefCell, /// Whether to expect any further input from the associated network request. last_chunk_received: Cell, + /// Whether this parser should avoid passing any further data to the tokenizer. + suspended: Cell, } impl ServoParser { - pub fn new_inherited( + #[allow(unrooted_must_root)] + fn new_inherited( document: &Document, pipeline: Option, + 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, + tokenizer: Tokenizer, + last_chunk_received: bool) + -> Root { + 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::().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, Sink>> + ), + XML( + #[ignore_heap_size_of = "Defined in xml5ever"] + XmlTokenizer, 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; + #[allow(unrooted_must_root)] + fn trace_handle(&self, node: &JS) { + 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; + #[allow(unrooted_must_root)] + fn trace_handle(&self, node: JS) { + 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, Sink>>; - -#[must_root] -#[derive(JSTraceable, HeapSizeOf)] -pub struct Sink { - pub base_url: Option, - pub document: JS, -} - -#[must_root] -#[dom_struct] -pub struct ServoXMLParser { - servoparser: ServoParser, - #[ignore_heap_size_of = "Defined in xml5ever"] - tokenizer: DOMRefCell, - /// True if this parser should avoid passing any further data to the tokenizer. - suspended: Cell, -} - -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, document: &Document, pipeline: Option) - -> Root { - 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::().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 { - &self.tokenizer - } -} - -struct Tracer { - trc: *mut JSTracer, -} - -impl tree_builder::Tracer for Tracer { - type Handle = JS; - #[allow(unrooted_must_root)] - fn trace_handle(&self, node: JS) { - 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>; - - 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 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), @@ -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), - XML(JS), -} - -#[must_root] -#[derive(JSTraceable, HeapSizeOf)] -pub struct MutNullableParserField { - #[ignore_heap_size_of = "XXXjdm"] - ptr: UnsafeCell>, -} - -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) { - 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 { - 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), - XML(Root), -} - -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), - XML(Trusted), -} - -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, + parser: Option>, /// 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 = "".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::(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 = "
\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!("

Unknown content type ({}/{}).

", 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, +} 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; 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) - -> Option { + -> Option> { 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) -> Option { + metadata: Option) -> Option> { 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 { let final_url = metadata.final_url.clone(); { // send the final url to the layout thread. From 4b813e0bdc4c7e908a9ce3f145cf453c4778e2b9 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Mon, 10 Oct 2016 17:07:08 +0200 Subject: [PATCH 9/9] Merge script::parse and script::dom::servoparser --- components/script/dom/domparser.rs | 4 +- components/script/dom/node.rs | 2 +- .../script/{parse => dom/servoparser}/html.rs | 9 +- .../{servoparser.rs => servoparser/mod.rs} | 222 ++++++++++++++++-- .../script/{parse => dom/servoparser}/xml.rs | 6 +- components/script/dom/xmlhttprequest.rs | 4 +- components/script/lib.rs | 1 - components/script/parse/mod.rs | 180 -------------- components/script/script_thread.rs | 7 +- 9 files changed, 212 insertions(+), 223 deletions(-) rename components/script/{parse => dom/servoparser}/html.rs (97%) rename components/script/dom/{servoparser.rs => servoparser/mod.rs} (51%) rename components/script/{parse => dom/servoparser}/xml.rs (96%) delete mode 100644 components/script/parse/mod.rs diff --git a/components/script/dom/domparser.rs b/components/script/dom/domparser.rs index e73d9bf1aad..98019005c2b 100644 --- a/components/script/dom/domparser.rs +++ b/components/script/dom/domparser.rs @@ -18,9 +18,9 @@ use dom::bindings::str::DOMString; use dom::document::{Document, IsHTMLDocument}; use dom::document::DocumentSource; use dom::globalscope::GlobalScope; +use dom::servoparser::html::{ParseContext, parse_html}; +use dom::servoparser::xml::{self, parse_xml}; use dom::window::Window; -use parse::html::{ParseContext, parse_html}; -use parse::xml::{self, parse_xml}; #[dom_struct] pub struct DOMParser { diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index b52a8aec965..0bc6cdc267e 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -47,6 +47,7 @@ use dom::htmltextareaelement::{HTMLTextAreaElement, LayoutHTMLTextAreaElementHel use dom::nodelist::NodeList; use dom::processinginstruction::ProcessingInstruction; use dom::range::WeakRangeVec; +use dom::servoparser::html::parse_html_fragment; use dom::svgsvgelement::{SVGSVGElement, LayoutSVGSVGElementHelpers}; use dom::text::Text; use dom::virtualmethods::{VirtualMethods, vtable_for}; @@ -59,7 +60,6 @@ use html5ever::tree_builder::QuirksMode; use js::jsapi::{JSContext, JSObject, JSRuntime}; use libc::{self, c_void, uintptr_t}; use msg::constellation_msg::PipelineId; -use parse::html::parse_html_fragment; use ref_slice::ref_slice; use script_layout_interface::{HTMLCanvasData, OpaqueStyleAndLayoutData, SVGSVGData}; use script_layout_interface::{LayoutElementType, LayoutNodeType, TrustedNodeAddress}; diff --git a/components/script/parse/html.rs b/components/script/dom/servoparser/html.rs similarity index 97% rename from components/script/parse/html.rs rename to components/script/dom/servoparser/html.rs index dd4a17c40c8..46739c5dc59 100644 --- a/components/script/parse/html.rs +++ b/components/script/dom/servoparser/html.rs @@ -23,7 +23,6 @@ use dom::htmltemplateelement::HTMLTemplateElement; use dom::node::{document_from_node, window_from_node}; use dom::node::Node; use dom::processinginstruction::ProcessingInstruction; -use dom::servoparser::{ServoParser, Tokenizer}; use dom::text::Text; use html5ever::Attribute; use html5ever::serialize::{AttrRef, Serializable, Serializer}; @@ -34,10 +33,10 @@ 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::Sink; use std::borrow::Cow; use std::io::{self, Write}; use string_cache::QualName; +use super::{LastChunkState, ServoParser, Sink, Tokenizer}; use url::Url; fn insert(parent: &Node, reference_child: Option<&Node>, child: NodeOrText>) { @@ -279,7 +278,8 @@ pub fn parse_html(document: &Document, let tb = TreeBuilder::new(sink, options); let tok = HtmlTokenizer::new(tb, Default::default()); - ServoParser::new(document, owner, Tokenizer::HTML(tok), false) + ServoParser::new( + document, owner, Tokenizer::HTML(tok), LastChunkState::NotReceived) }, ParseContext::Fragment(fc) => { let tb = TreeBuilder::new_for_fragment( @@ -294,7 +294,8 @@ pub fn parse_html(document: &Document, }; let tok = HtmlTokenizer::new(tb, tok_options); - ServoParser::new(document, None, Tokenizer::HTML(tok), true) + ServoParser::new( + document, None, Tokenizer::HTML(tok), LastChunkState::Received) } }; parser.parse_chunk(String::from(input)); diff --git a/components/script/dom/servoparser.rs b/components/script/dom/servoparser/mod.rs similarity index 51% rename from components/script/dom/servoparser.rs rename to components/script/dom/servoparser/mod.rs index 9566919b9f4..252305619fc 100644 --- a/components/script/dom/servoparser.rs +++ b/components/script/dom/servoparser/mod.rs @@ -2,29 +2,46 @@ * 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 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::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::globalscope::GlobalScope; +use dom::htmlimageelement::HTMLImageElement; use dom::node::Node; -use dom::window::Window; +use encoding::all::UTF_8; +use encoding::types::{DecoderTrap, Encoding}; use html5ever::tokenizer::Tokenizer as HtmlTokenizer; 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 parse::Sink; +use net_traits::{AsyncResponseListener, Metadata, NetworkError}; +use network_listener::PreInvoke; use profile_traits::time::{TimerMetadata, TimerMetadataFrameType}; use profile_traits::time::{TimerMetadataReflowType, ProfilerCategory, profile}; use script_thread::ScriptThread; use std::cell::Cell; +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; + #[dom_struct] pub struct ServoParser { reflector: Reflector, @@ -43,13 +60,19 @@ pub struct ServoParser { suspended: Cell, } +#[derive(PartialEq)] +enum LastChunkState { + Received, + NotReceived, +} + impl ServoParser { #[allow(unrooted_must_root)] fn new_inherited( document: &Document, pipeline: Option, tokenizer: Tokenizer, - last_chunk_received: bool) + last_chunk_state: LastChunkState) -> Self { ServoParser { reflector: Reflector::new(), @@ -57,20 +80,20 @@ impl ServoParser { pipeline: pipeline, pending_input: DOMRefCell::new(vec![]), tokenizer: DOMRefCell::new(tokenizer), - last_chunk_received: Cell::new(last_chunk_received), + last_chunk_received: Cell::new(last_chunk_state == LastChunkState::Received), suspended: Default::default(), } } #[allow(unrooted_must_root)] - pub fn new( + fn new( document: &Document, pipeline: Option, tokenizer: Tokenizer, - last_chunk_received: bool) + last_chunk_state: LastChunkState) -> Root { reflect_dom_object( - box ServoParser::new_inherited(document, pipeline, tokenizer, last_chunk_received), + box ServoParser::new_inherited(document, pipeline, tokenizer, last_chunk_state), document.window(), ServoParserBinding::Wrap) } @@ -83,15 +106,15 @@ impl ServoParser { self.pipeline } - pub fn has_pending_input(&self) -> bool { + fn has_pending_input(&self) -> bool { !self.pending_input.borrow().is_empty() } - pub fn push_input_chunk(&self, chunk: String) { + fn push_input_chunk(&self, chunk: String) { self.pending_input.borrow_mut().push(chunk); } - pub fn take_next_input_chunk(&self) -> Option { + fn take_next_input_chunk(&self) -> Option { let mut pending_input = self.pending_input.borrow_mut(); if pending_input.is_empty() { None @@ -100,15 +123,15 @@ impl ServoParser { } } - pub fn last_chunk_received(&self) -> bool { + fn last_chunk_received(&self) -> bool { self.last_chunk_received.get() } - pub fn mark_last_chunk_received(&self) { + fn mark_last_chunk_received(&self) { self.last_chunk_received.set(true) } - pub fn set_plaintext_state(&self) { + fn set_plaintext_state(&self) { self.tokenizer.borrow_mut().set_plaintext_state() } @@ -116,10 +139,6 @@ impl ServoParser { 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); @@ -135,7 +154,7 @@ impl ServoParser { self.suspended.get() } - pub fn parse_sync(&self) { + fn parse_sync(&self) { let metadata = TimerMetadata { url: self.document().url().as_str().into(), iframe: TimerMetadataFrameType::RootWindow, @@ -174,7 +193,7 @@ impl ServoParser { } } - pub fn parse_chunk(&self, input: String) { + fn parse_chunk(&self, input: String) { self.document().set_current_parser(Some(self)); self.push_input_chunk(input); if !self.is_suspended() { @@ -182,7 +201,7 @@ impl ServoParser { } } - pub fn finish(&self) { + fn finish(&self) { assert!(!self.suspended.get()); assert!(!self.has_pending_input()); @@ -199,7 +218,7 @@ impl ServoParser { #[derive(HeapSizeOf)] #[must_root] -pub enum Tokenizer { +enum Tokenizer { HTML( #[ignore_heap_size_of = "Defined in html5ever"] HtmlTokenizer, Sink>> @@ -210,36 +229,43 @@ pub enum Tokenizer { ), } +#[derive(JSTraceable, HeapSizeOf)] +#[must_root] +struct Sink { + pub base_url: Url, + pub document: JS, +} + impl Tokenizer { - pub fn feed(&mut self, input: String) { + 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) { + 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) { + 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) { + 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 { + fn profiler_category(&self) -> ProfilerCategory { match *self { Tokenizer::HTML(_) => ProfilerCategory::ScriptParseHTML, Tokenizer::XML(_) => ProfilerCategory::ScriptParseXML, @@ -280,3 +306,147 @@ impl JSTraceable for Tokenizer { } } } + +/// The context required for asynchronously fetching a document +/// and parsing it progressively. +pub struct ParserContext { + /// The parser that initiated the request. + parser: Option>, + /// Is this a synthesized document + is_synthesized_document: bool, + /// The pipeline associated with this document. + id: PipelineId, + /// The URL for this document. + url: Url, +} + +impl ParserContext { + pub fn new(id: PipelineId, url: Url) -> ParserContext { + ParserContext { + parser: None, + is_synthesized_document: false, + id: id, + url: url, + } + } +} + +impl AsyncResponseListener for ParserContext { + fn headers_available(&mut self, meta_result: Result) { + let mut ssl_error = None; + let metadata = match meta_result { + Ok(meta) => Some(meta), + Err(NetworkError::SslValidation(url, reason)) => { + ssl_error = Some(reason); + let mut meta = Metadata::default(url); + let mime: Option = "text/html".parse().ok(); + meta.set_content_type(mime.as_ref()); + Some(meta) + }, + Err(_) => None, + }; + let content_type = + metadata.clone().and_then(|meta| meta.content_type).map(Serde::into_inner); + let parser = match ScriptThread::page_headers_available(&self.id, + metadata) { + Some(parser) => parser, + None => return, + }; + + self.parser = Some(Trusted::new(&*parser)); + + match content_type { + Some(ContentType(Mime(TopLevel::Image, _, _))) => { + self.is_synthesized_document = true; + let page = "".into(); + parser.push_input_chunk(page); + parser.parse_sync(); + + let doc = parser.document(); + let doc_body = Root::upcast::(doc.GetBody().unwrap()); + let img = HTMLImageElement::new(atom!("img"), None, doc); + img.SetSrc(DOMString::from(self.url.to_string())); + doc_body.AppendChild(&Root::upcast::(img)).expect("Appending failed"); + + }, + Some(ContentType(Mime(TopLevel::Text, SubLevel::Plain, _))) => { + // https://html.spec.whatwg.org/multipage/#read-text + let page = "
\n".into();
+                parser.push_input_chunk(page);
+                parser.parse_sync();
+                parser.set_plaintext_state();
+            },
+            Some(ContentType(Mime(TopLevel::Text, SubLevel::Html, _))) => { // Handle text/html
+                if let Some(reason) = ssl_error {
+                    self.is_synthesized_document = true;
+                    let page_bytes = read_resource_file("badcert.html").unwrap();
+                    let page = String::from_utf8(page_bytes).unwrap();
+                    let page = page.replace("${reason}", &reason);
+                    parser.push_input_chunk(page);
+                    parser.parse_sync();
+                }
+            },
+            Some(ContentType(Mime(TopLevel::Text, SubLevel::Xml, _))) => {}, // Handle text/xml
+            Some(ContentType(Mime(toplevel, sublevel, _))) => {
+                if toplevel.as_str() == "application" && sublevel.as_str() == "xhtml+xml" {
+                    // Handle xhtml (application/xhtml+xml).
+                    return;
+                }
+
+                // Show warning page for unknown mime types.
+                let page = format!("

Unknown content type ({}/{}).

", + toplevel.as_str(), sublevel.as_str()); + self.is_synthesized_document = true; + parser.push_input_chunk(page); + parser.parse_sync(); + }, + None => { + // No content-type header. + // Merge with #4212 when fixed. + } + } + } + + fn data_available(&mut self, payload: Vec) { + if !self.is_synthesized_document { + // FIXME: use Vec (html5ever #34) + let data = UTF_8.decode(&payload, DecoderTrap::Replace).unwrap(); + let parser = match self.parser.as_ref() { + Some(parser) => parser.root(), + None => return, + }; + parser.parse_chunk(data); + } + } + + fn response_complete(&mut self, status: Result<(), NetworkError>) { + let parser = match self.parser.as_ref() { + Some(parser) => parser.root(), + None => return, + }; + + if let Err(NetworkError::Internal(ref reason)) = status { + // Show an error page for network errors, + // certificate errors are handled earlier. + self.is_synthesized_document = true; + let page_bytes = read_resource_file("neterror.html").unwrap(); + let page = String::from_utf8(page_bytes).unwrap(); + let page = page.replace("${reason}", reason); + 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); + } + + parser.document() + .finish_load(LoadType::PageSource(self.url.clone())); + + parser.mark_last_chunk_received(); + if !parser.is_suspended() { + parser.parse_sync(); + } + } +} + +impl PreInvoke for ParserContext {} diff --git a/components/script/parse/xml.rs b/components/script/dom/servoparser/xml.rs similarity index 96% rename from components/script/parse/xml.rs rename to components/script/dom/servoparser/xml.rs index 3777b7f497c..0713fa5c58a 100644 --- a/components/script/parse/xml.rs +++ b/components/script/dom/servoparser/xml.rs @@ -15,13 +15,12 @@ use dom::element::{Element, ElementCreator}; use dom::htmlscriptelement::HTMLScriptElement; use dom::node::Node; use dom::processinginstruction::ProcessingInstruction; -use dom::servoparser::{ServoParser, Tokenizer}; use dom::text::Text; use html5ever; use msg::constellation_msg::PipelineId; -use parse::Sink; use std::borrow::Cow; use string_cache::{Atom, QualName, Namespace}; +use super::{LastChunkState, ServoParser, Sink, Tokenizer}; use url::Url; use xml5ever::tendril::StrTendril; use xml5ever::tokenizer::{Attribute, QName, XmlTokenizer}; @@ -140,7 +139,8 @@ pub fn parse_xml(document: &Document, }); let tok = XmlTokenizer::new(tb, Default::default()); - ServoParser::new(document, owner, Tokenizer::XML(tok), false) + ServoParser::new( + document, owner, Tokenizer::XML(tok), LastChunkState::NotReceived) } }; parser.parse_chunk(String::from(input)); diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs index cd2cd37c476..7d4ce833f87 100644 --- a/components/script/dom/xmlhttprequest.rs +++ b/components/script/dom/xmlhttprequest.rs @@ -28,6 +28,8 @@ use dom::globalscope::GlobalScope; use dom::headers::is_forbidden_header_name; use dom::htmlformelement::{encode_multipart_form_data, generate_boundary}; use dom::progressevent::ProgressEvent; +use dom::servoparser::html::{ParseContext, parse_html}; +use dom::servoparser::xml::{self, parse_xml}; use dom::window::Window; use dom::workerglobalscope::WorkerGlobalScope; use dom::xmlhttprequesteventtarget::XMLHttpRequestEventTarget; @@ -53,8 +55,6 @@ use net_traits::CoreResourceMsg::Fetch; use net_traits::request::{CredentialsMode, Destination, RequestInit, RequestMode}; use net_traits::trim_http_whitespace; use network_listener::{NetworkListener, PreInvoke}; -use parse::html::{ParseContext, parse_html}; -use parse::xml::{self, parse_xml}; use script_runtime::ScriptChan; use std::ascii::AsciiExt; use std::borrow::ToOwned; diff --git a/components/script/lib.rs b/components/script/lib.rs index c3832c646e2..d3161c410f3 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -105,7 +105,6 @@ pub mod layout_wrapper; mod mem; mod network_listener; pub mod origin; -pub mod parse; pub mod script_runtime; #[allow(unsafe_code)] pub mod script_thread; diff --git a/components/script/parse/mod.rs b/components/script/parse/mod.rs deleted file mode 100644 index 6ef786eaa93..00000000000 --- a/components/script/parse/mod.rs +++ /dev/null @@ -1,180 +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 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::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::servoparser::ServoParser; -use encoding::all::UTF_8; -use encoding::types::{DecoderTrap, Encoding}; -use hyper::header::ContentType; -use hyper::mime::{Mime, SubLevel, TopLevel}; -use hyper_serde::Serde; -use msg::constellation_msg::PipelineId; -use net_traits::{AsyncResponseListener, Metadata, NetworkError}; -use network_listener::PreInvoke; -use script_thread::ScriptThread; -use url::Url; -use util::resource_files::read_resource_file; - -pub mod html; -pub mod xml; - -/// The context required for asynchronously fetching a document -/// and parsing it progressively. -pub struct ParserContext { - /// The parser that initiated the request. - parser: Option>, - /// Is this a synthesized document - is_synthesized_document: bool, - /// The pipeline associated with this document. - id: PipelineId, - /// The URL for this document. - url: Url, -} - -impl ParserContext { - pub fn new(id: PipelineId, url: Url) -> ParserContext { - ParserContext { - parser: None, - is_synthesized_document: false, - id: id, - url: url, - } - } -} - -impl AsyncResponseListener for ParserContext { - fn headers_available(&mut self, meta_result: Result) { - let mut ssl_error = None; - let metadata = match meta_result { - Ok(meta) => Some(meta), - Err(NetworkError::SslValidation(url, reason)) => { - ssl_error = Some(reason); - let mut meta = Metadata::default(url); - let mime: Option = "text/html".parse().ok(); - meta.set_content_type(mime.as_ref()); - Some(meta) - }, - Err(_) => None, - }; - let content_type = - metadata.clone().and_then(|meta| meta.content_type).map(Serde::into_inner); - let parser = match ScriptThread::page_headers_available(&self.id, - metadata) { - Some(parser) => parser, - None => return, - }; - - self.parser = Some(Trusted::new(&*parser)); - - match content_type { - Some(ContentType(Mime(TopLevel::Image, _, _))) => { - self.is_synthesized_document = true; - let page = "".into(); - parser.push_input_chunk(page); - parser.parse_sync(); - - let doc = parser.document(); - let doc_body = Root::upcast::(doc.GetBody().unwrap()); - let img = HTMLImageElement::new(atom!("img"), None, doc); - img.SetSrc(DOMString::from(self.url.to_string())); - doc_body.AppendChild(&Root::upcast::(img)).expect("Appending failed"); - - }, - Some(ContentType(Mime(TopLevel::Text, SubLevel::Plain, _))) => { - // https://html.spec.whatwg.org/multipage/#read-text - let page = "
\n".into();
-                parser.push_input_chunk(page);
-                parser.parse_sync();
-                parser.set_plaintext_state();
-            },
-            Some(ContentType(Mime(TopLevel::Text, SubLevel::Html, _))) => { // Handle text/html
-                if let Some(reason) = ssl_error {
-                    self.is_synthesized_document = true;
-                    let page_bytes = read_resource_file("badcert.html").unwrap();
-                    let page = String::from_utf8(page_bytes).unwrap();
-                    let page = page.replace("${reason}", &reason);
-                    parser.push_input_chunk(page);
-                    parser.parse_sync();
-                }
-            },
-            Some(ContentType(Mime(TopLevel::Text, SubLevel::Xml, _))) => {}, // Handle text/xml
-            Some(ContentType(Mime(toplevel, sublevel, _))) => {
-                if toplevel.as_str() == "application" && sublevel.as_str() == "xhtml+xml" {
-                    // Handle xhtml (application/xhtml+xml).
-                    return;
-                }
-
-                // Show warning page for unknown mime types.
-                let page = format!("

Unknown content type ({}/{}).

", - toplevel.as_str(), sublevel.as_str()); - self.is_synthesized_document = true; - parser.push_input_chunk(page); - parser.parse_sync(); - }, - None => { - // No content-type header. - // Merge with #4212 when fixed. - } - } - } - - fn data_available(&mut self, payload: Vec) { - if !self.is_synthesized_document { - // FIXME: use Vec (html5ever #34) - let data = UTF_8.decode(&payload, DecoderTrap::Replace).unwrap(); - let parser = match self.parser.as_ref() { - Some(parser) => parser.root(), - None => return, - }; - parser.parse_chunk(data); - } - } - - fn response_complete(&mut self, status: Result<(), NetworkError>) { - let parser = match self.parser.as_ref() { - Some(parser) => parser.root(), - None => return, - }; - - if let Err(NetworkError::Internal(ref reason)) = status { - // Show an error page for network errors, - // certificate errors are handled earlier. - self.is_synthesized_document = true; - let page_bytes = read_resource_file("neterror.html").unwrap(); - let page = String::from_utf8(page_bytes).unwrap(); - let page = page.replace("${reason}", reason); - 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); - } - - parser.document() - .finish_load(LoadType::PageSource(self.url.clone())); - - parser.mark_last_chunk_received(); - if !parser.is_suspended() { - parser.parse_sync(); - } - } -} - -impl PreInvoke for ParserContext {} - -#[derive(JSTraceable, HeapSizeOf)] -#[must_root] -pub struct Sink { - pub base_url: Url, - pub document: JS, -} diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index ceb6842a13b..87d4d148253 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -44,7 +44,9 @@ 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::servoparser::{ParserContext, ServoParser}; +use dom::servoparser::html::{ParseContext, parse_html}; +use dom::servoparser::xml::{self, parse_xml}; use dom::uievent::UIEvent; use dom::window::{ReflowReason, Window}; use dom::worker::TrustedWorkerAddress; @@ -71,9 +73,6 @@ 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; -use parse::html::{ParseContext, parse_html}; -use parse::xml::{self, parse_xml}; use profile_traits::mem::{self, OpaqueSender, Report, ReportKind, ReportsChan}; use profile_traits::time::{self, ProfilerCategory, profile}; use script_layout_interface::message::{self, NewLayoutThreadInfo, ReflowQueryType};