diff options
-rw-r--r-- | components/script/dom/document.rs | 14 | ||||
-rw-r--r-- | components/script/dom/htmlscriptelement.rs | 2 | ||||
-rw-r--r-- | components/script/dom/servohtmlparser.rs | 220 | ||||
-rw-r--r-- | components/script/dom/servoxmlparser.rs | 199 | ||||
-rw-r--r-- | components/script/lib.rs | 1 | ||||
-rw-r--r-- | components/script/parse/xml.rs | 102 | ||||
-rw-r--r-- | components/script/script_task.rs | 44 | ||||
-rw-r--r-- | tests/wpt/metadata/dom/nodes/Document-contentType/contentType/contenttype_mimeheader_01.html.ini | 5 |
8 files changed, 539 insertions, 48 deletions
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 4c1a7f12791..35a668ca196 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -65,7 +65,7 @@ use dom::nodeiterator::NodeIterator; use dom::nodelist::NodeList; use dom::processinginstruction::ProcessingInstruction; use dom::range::Range; -use dom::servohtmlparser::ServoHTMLParser; +use dom::servohtmlparser::{ParserRoot, ParserRef, MutNullableParserField}; use dom::text::Text; use dom::touch::Touch; use dom::touchevent::TouchEvent; @@ -184,7 +184,7 @@ pub struct Document { /// Tracks all outstanding loads related to this document. loader: DOMRefCell<DocumentLoader>, /// The current active HTML parser, to allow resuming after interruptions. - current_parser: MutNullableHeap<JS<ServoHTMLParser>>, + current_parser: MutNullableParserField, /// When we should kick off a reflow. This happens during parsing. reflow_timeout: Cell<Option<u64>>, /// The cached first `base` element with an `href` attribute. @@ -1224,9 +1224,9 @@ impl Document { // A finished resource load can potentially unblock parsing. In that case, resume the // parser so its loop can find out. - if let Some(parser) = self.current_parser.get() { - if parser.is_suspended() { - parser.resume(); + if let Some(parser) = self.get_current_parser() { + if parser.r().is_suspended() { + parser.r().resume(); } } else if self.reflow_timeout.get().is_none() { // If we don't have a parser, and the reflow timer has been reset, explicitly @@ -1347,11 +1347,11 @@ impl Document { } - pub fn set_current_parser(&self, script: Option<&ServoHTMLParser>) { + pub fn set_current_parser(&self, script: Option<ParserRef>) { self.current_parser.set(script); } - pub fn get_current_parser(&self) -> Option<Root<ServoHTMLParser>> { + pub fn get_current_parser(&self) -> Option<ParserRoot> { self.current_parser.get() } diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index 0d2da397406..5bc8b1bbc60 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -349,7 +349,7 @@ impl HTMLScriptElement { // TODO: make this suspension happen automatically. if was_parser_inserted { if let Some(parser) = doc.get_current_parser() { - parser.suspend(); + parser.r().suspend(); } } return NextParserState::Suspend; diff --git a/components/script/dom/servohtmlparser.rs b/components/script/dom/servohtmlparser.rs index 68920e19c22..6584dbe5cb5 100644 --- a/components/script/dom/servohtmlparser.rs +++ b/components/script/dom/servohtmlparser.rs @@ -15,6 +15,7 @@ use dom::bindings::reflector::{Reflector, reflect_dom_object}; use dom::bindings::trace::JSTraceable; use dom::document::Document; use dom::node::Node; +use dom::servoxmlparser::ServoXMLParser; use dom::text::Text; use dom::window::Window; use encoding::all::UTF_8; @@ -31,7 +32,9 @@ use network_listener::PreInvoke; use parse::Parser; use script_task::{ScriptChan, ScriptTask}; use std::cell::Cell; +use std::cell::UnsafeCell; use std::default::Default; +use std::ptr; use url::Url; use util::str::DOMString; @@ -67,10 +70,164 @@ pub struct FragmentContext<'a> { pub type Tokenizer = tokenizer::Tokenizer<TreeBuilder<JS<Node>, Sink>>; +#[must_root] +#[derive(JSTraceable, HeapSizeOf)] +pub enum ParserField { + HTML(JS<ServoHTMLParser>), + XML(JS<ServoXMLParser>), +} + +#[must_root] +#[derive(JSTraceable, HeapSizeOf)] +pub struct MutNullableParserField { + #[ignore_heap_size_of = "XXXjdm"] + ptr: UnsafeCell<Option<ParserField>>, +} + +impl Default for MutNullableParserField { + #[allow(unrooted_must_root)] + fn default() -> MutNullableParserField { + MutNullableParserField { + ptr: UnsafeCell::new(None), + } + } +} + +impl MutNullableParserField { + #[allow(unsafe_code)] + pub fn set(&self, val: Option<ParserRef>) { + unsafe { + *self.ptr.get() = val.map(|val| { + match val { + ParserRef::HTML(parser) => ParserField::HTML(JS::from_ref(parser)), + ParserRef::XML(parser) => ParserField::XML(JS::from_ref(parser)), + } + }); + } + } + + #[allow(unsafe_code, unrooted_must_root)] + pub fn get(&self) -> Option<ParserRoot> { + unsafe { + ptr::read(self.ptr.get()).map(|o| { + match o { + ParserField::HTML(parser) => ParserRoot::HTML(Root::from_ref(&*parser)), + ParserField::XML(parser) => ParserRoot::XML(Root::from_ref(&*parser)), + } + }) + } + } +} + +pub enum ParserRoot { + HTML(Root<ServoHTMLParser>), + XML(Root<ServoXMLParser>), +} + +impl ParserRoot { + pub fn r(&self) -> ParserRef { + match *self { + ParserRoot::HTML(ref parser) => ParserRef::HTML(parser.r()), + ParserRoot::XML(ref parser) => ParserRef::XML(parser.r()), + } + } +} + +enum TrustedParser { + HTML(Trusted<ServoHTMLParser>), + XML(Trusted<ServoXMLParser>), +} + +impl TrustedParser { + pub fn root(&self) -> ParserRoot { + match *self { + TrustedParser::HTML(ref parser) => ParserRoot::HTML(parser.root()), + TrustedParser::XML(ref parser) => ParserRoot::XML(parser.root()), + } + } +} + +pub enum ParserRef<'a> { + HTML(&'a ServoHTMLParser), + XML(&'a ServoXMLParser), +} + +impl<'a> ParserRef<'a> { + 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 pending_input(&self) -> &DOMRefCell<Vec<String>> { + 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(), + 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(), + } + } + + pub fn document(&self) -> &Document { + match *self { + ParserRef::HTML(parser) => parser.document(), + ParserRef::XML(parser) => parser.document(), + } + } + + pub fn last_chunk_received(&self) -> &Cell<bool> { + match *self { + ParserRef::HTML(parser) => parser.last_chunk_received(), + ParserRef::XML(parser) => parser.last_chunk_received(), + } + } +} + /// The context required for asynchronously fetching a document and parsing it progressively. pub struct ParserContext { /// The parser that initiated the request. - parser: Option<Trusted<ServoHTMLParser>>, + parser: Option<TrustedParser>, /// Is this a synthesized document is_synthesized_document: bool, /// The pipeline associated with this document. @@ -110,24 +267,31 @@ impl AsyncResponseListener for ParserContext { let parser = parser.r(); let win = parser.window(); - self.parser = Some(Trusted::new(win.get_cx(), parser, self.script_chan.clone())); + self.parser = Some(match parser { + ParserRef::HTML(parser) => TrustedParser::HTML( + Trusted::new(win.get_cx(), + parser, + self.script_chan.clone())), + ParserRef::XML(parser) => TrustedParser::XML(Trusted::new(win.get_cx(), parser, self.script_chan.clone())), + }); match content_type { Some(ContentType(Mime(TopLevel::Image, _, _))) => { self.is_synthesized_document = true; let page = format!("<html><body><img src='{}' /></body></html>", self.url.serialize()); - parser.pending_input.borrow_mut().push(page); + parser.pending_input().borrow_mut().push(page); parser.parse_sync(); }, Some(ContentType(Mime(TopLevel::Text, SubLevel::Plain, _))) => { // https://html.spec.whatwg.org/multipage/#read-text let page = format!("<pre>\n"); - parser.pending_input.borrow_mut().push(page); + parser.pending_input().borrow_mut().push(page); parser.parse_sync(); - parser.tokenizer().borrow_mut().set_plaintext_state(); + parser.set_plaintext_state(); }, Some(ContentType(Mime(TopLevel::Text, SubLevel::Html, _))) => {}, // Handle text/html + 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). @@ -138,7 +302,7 @@ impl AsyncResponseListener for ParserContext { let page = format!("<html><body><p>Unknown content type ({}/{}).</p></body></html>", toplevel.as_str(), sublevel.as_str()); self.is_synthesized_document = true; - parser.pending_input.borrow_mut().push(page); + parser.pending_input().borrow_mut().push(page); parser.parse_sync(); }, None => { @@ -156,7 +320,7 @@ impl AsyncResponseListener for ParserContext { Some(parser) => parser.root(), None => return, }; - parser.parse_chunk(data); + parser.r().parse_chunk(data); } } @@ -165,16 +329,16 @@ impl AsyncResponseListener for ParserContext { Some(parser) => parser.root(), None => return, }; - parser.document.finish_load(LoadType::PageSource(self.url.clone())); + parser.r().document().finish_load(LoadType::PageSource(self.url.clone())); if let Err(err) = status { debug!("Failed to load page URL {}, error: {}", self.url.serialize(), err); // TODO(Savago): we should send a notification to callers #5463. } - parser.last_chunk_received.set(true); - if !parser.is_suspended() { - parser.parse_sync(); + parser.r().last_chunk_received().set(true); + if !parser.r().is_suspended() { + parser.r().parse_sync(); } } } @@ -202,7 +366,7 @@ pub struct ServoHTMLParser { impl<'a> Parser for &'a ServoHTMLParser { fn parse_chunk(self, input: String) { - self.document.set_current_parser(Some(self)); + self.document.set_current_parser(Some(ParserRef::HTML(self))); self.pending_input.borrow_mut().push(input); if !self.is_suspended() { self.parse_sync(); @@ -213,7 +377,7 @@ impl<'a> Parser for &'a ServoHTMLParser { assert!(!self.suspended.get()); assert!(self.pending_input.borrow().is_empty()); - self.tokenizer().borrow_mut().end(); + self.tokenizer.borrow_mut().end(); debug!("finished parsing"); self.document.set_current_parser(None); @@ -295,6 +459,19 @@ impl ServoHTMLParser { pub fn tokenizer(&self) -> &DOMRefCell<Tokenizer> { &self.tokenizer } + + pub fn set_plaintext_state(&self) { + self.tokenizer.borrow_mut().set_plaintext_state() + } + + pub fn end_tokenizer(&self) { + self.tokenizer.borrow_mut().end() + } + + pub fn pending_input(&self) -> &DOMRefCell<Vec<String>> { + &self.pending_input + } + } @@ -330,24 +507,29 @@ impl ServoHTMLParser { fn window(&self) -> &Window { self.document.window() } -} - -impl ServoHTMLParser { - pub fn suspend(&self) { + fn suspend(&self) { assert!(!self.suspended.get()); self.suspended.set(true); } - pub fn resume(&self) { + fn resume(&self) { assert!(self.suspended.get()); self.suspended.set(false); self.parse_sync(); } - pub fn is_suspended(&self) -> bool { + fn is_suspended(&self) -> bool { self.suspended.get() } + + fn document(&self) -> &Document { + &self.document + } + + fn last_chunk_received(&self) -> &Cell<bool> { + &self.last_chunk_received + } } struct Tracer { diff --git a/components/script/dom/servoxmlparser.rs b/components/script/dom/servoxmlparser.rs index 9de29cb392d..4f4b4be7d3e 100644 --- a/components/script/dom/servoxmlparser.rs +++ b/components/script/dom/servoxmlparser.rs @@ -2,15 +2,210 @@ * 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; +use dom::bindings::cell::DOMRefCell; +use dom::bindings::codegen::Bindings::ServoXMLParserBinding; +use dom::bindings::global::GlobalRef; +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::node::Node; +use dom::servohtmlparser::ParserRef; +use dom::text::Text; +use dom::window::Window; +use js::jsapi::JSTracer; +use msg::constellation_msg::PipelineId; +use parse::Parser; +use script_task::ScriptTask; +use std::cell::Cell; +use url::Url; +use util::str::DOMString; +use xml5ever::tokenizer; +use xml5ever::tree_builder::{self, NodeOrText, XmlTreeBuilder}; + +pub type Tokenizer = tokenizer::XmlTokenizer<XmlTreeBuilder<JS<Node>, Sink>>; #[must_root] +#[derive(JSTraceable, HeapSizeOf)] +pub struct Sink { + pub base_url: Option<Url>, + pub document: JS<Document>, +} + +impl Sink { + #[allow(unrooted_must_root)] // method is only run at parse time + pub fn get_or_create(&self, child: NodeOrText<JS<Node>>) -> Root<Node> { + match child { + NodeOrText::AppendNode(n) => Root::from_ref(&*n), + NodeOrText::AppendText(t) => { + let s: String = t.into(); + let text = Text::new(DOMString::from(s), &self.document); + Root::upcast(text) + } + } + } +} +#[must_root] #[dom_struct] pub struct ServoXMLParser { reflector_: Reflector, + #[ignore_heap_size_of = "Defined in xml5ever"] + tokenizer: DOMRefCell<Tokenizer>, + /// Input chunks received but not yet passed to the parser. + pending_input: DOMRefCell<Vec<String>>, + /// The document associated with this parser. + document: JS<Document>, + /// True if this parser should avoid passing any further data to the tokenizer. + suspended: Cell<bool>, + /// Whether to expect any further input from the associated network request. + last_chunk_received: Cell<bool>, + /// The pipeline associated with this parse, unavailable if this parse does not + /// correspond to a page load. + pipeline: Option<PipelineId>, +} + +impl<'a> Parser for &'a ServoXMLParser { + fn parse_chunk(self, input: String) { + self.document.set_current_parser(Some(ParserRef::XML(self))); + self.pending_input.borrow_mut().push(input); + if !self.is_suspended() { + self.parse_sync(); + } + } + + fn finish(self) { + assert!(!self.suspended.get()); + assert!(self.pending_input.borrow().is_empty()); + + self.tokenizer.borrow_mut().end(); + debug!("finished parsing"); + + self.document.set_current_parser(None); + + if let Some(pipeline) = self.pipeline { + ScriptTask::parsing_complete(pipeline); + } + } } impl ServoXMLParser { - pub fn new() { + #[allow(unrooted_must_root)] + pub fn new(base_url: Option<Url>, document: &Document, pipeline: Option<PipelineId>) + -> Root<ServoXMLParser> { + let sink = Sink { + base_url: base_url, + document: JS::from_ref(document), + }; + + let tb = XmlTreeBuilder::new(sink); + + let tok = tokenizer::XmlTokenizer::new(tb, Default::default()); + + let parser = ServoXMLParser { + reflector_: Reflector::new(), + 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, + }; + + reflect_dom_object(box parser, GlobalRef::Window(document.window()), + ServoXMLParserBinding::Wrap) + } + + pub fn window(&self) -> &Window { + self.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) { + // 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(); + let mut pending_input = self.pending_input.borrow_mut(); + if !pending_input.is_empty() { + let chunk = pending_input.remove(0); + self.tokenizer.borrow_mut().feed(chunk.into()); + } + + // Document parsing is blocked on an external resource. + if self.suspended.get() { + return; + } + + if pending_input.is_empty() { + break; + } + } + + if self.last_chunk_received.get() { + self.finish(); + } + } + + pub fn pending_input(&self) -> &DOMRefCell<Vec<String>> { + &self.pending_input + } + + 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 document(&self) -> &Document { + &self.document + } + + pub fn last_chunk_received(&self) -> &Cell<bool> { + &self.last_chunk_received + } + + pub fn tokenizer(&self) -> &DOMRefCell<Tokenizer> { + &self.tokenizer + } +} + +struct Tracer { + trc: *mut JSTracer, +} + +impl tree_builder::Tracer for Tracer { + type Handle = JS<Node>; + #[allow(unrooted_must_root)] + fn trace_handle(&self, node: JS<Node>) { + node.trace(self.trc); + } +} + +impl JSTraceable for Tokenizer { + fn trace(&self, trc: *mut JSTracer) { + let tracer = Tracer { + trc: trc, + }; + let tracer = &tracer as &tree_builder::Tracer<Handle=JS<Node>>; + + let tree_builder = self.sink(); + tree_builder.trace_handles(tracer); + tree_builder.sink().trace(trc); } } diff --git a/components/script/lib.rs b/components/script/lib.rs index f652100bc49..692e4671951 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -79,6 +79,7 @@ extern crate url; extern crate util; extern crate uuid; extern crate websocket; +extern crate xml5ever; pub mod clipboard_provider; pub mod cors; diff --git a/components/script/parse/xml.rs b/components/script/parse/xml.rs index 907c1600f0c..30cd5138348 100644 --- a/components/script/parse/xml.rs +++ b/components/script/parse/xml.rs @@ -4,17 +4,109 @@ #![allow(unrooted_must_root)] +use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; +use dom::bindings::inheritance::Castable; +use dom::bindings::js::{JS, Root, RootedReference}; +use dom::comment::Comment; use dom::document::Document; +use dom::documenttype::DocumentType; +use dom::element::{Element, ElementCreator}; +use dom::node::Node; +use dom::processinginstruction::ProcessingInstruction; +use dom::servoxmlparser; +use dom::servoxmlparser::ServoXMLParser; +use dom::text::Text; +use msg::constellation_msg::PipelineId; +use parse::Parser; +use std::borrow::Cow; +use string_cache::QualName; +use tendril::StrTendril; use url::Url; use util::str::DOMString; +use xml5ever::tokenizer::Attribute; +use xml5ever::tree_builder::{NodeOrText, TreeSink}; + +impl<'a> TreeSink for servoxmlparser::Sink { + type Handle = JS<Node>; + + fn parse_error(&mut self, msg: Cow<'static, str>) { + debug!("Parse error: {}", msg); + } + + fn get_document(&mut self) -> JS<Node> { + JS::from_ref(self.document.upcast()) + } + + fn elem_name(&self, target: &JS<Node>) -> QualName { + let elem = target.downcast::<Element>() + .expect("tried to get name of non-Element in XML parsing"); + QualName { + ns: elem.namespace().clone(), + local: elem.local_name().clone(), + } + } + + fn create_element(&mut self, name: QualName, attrs: Vec<Attribute>) + -> JS<Node> { + let elem = Element::create(name, None, &*self.document, + ElementCreator::ParserCreated); + + for attr in attrs { + elem.set_attribute_from_parser(attr.name, DOMString::from(String::from(attr.value)), None); + } + + JS::from_ref(elem.upcast()) + } + + fn create_comment(&mut self, text: StrTendril) -> JS<Node> { + let comment = Comment::new(DOMString::from(String::from(text)), &*self.document); + JS::from_ref(comment.upcast()) + } + + fn append(&mut self, parent: JS<Node>, child: NodeOrText<JS<Node>>) { + let child = match child { + NodeOrText::AppendNode(n) => Root::from_ref(&*n), + NodeOrText::AppendText(t) => { + let s: String = t.into(); + let text = Text::new(DOMString::from(s), &self.document); + Root::upcast(text) + } + }; + assert!(parent.AppendChild(child.r()).is_ok()); + } + + fn append_doctype_to_document(&mut self, name: StrTendril, public_id: StrTendril, + system_id: StrTendril) { + let doc = &*self.document; + let doctype = DocumentType::new( + DOMString::from(String::from(name)), Some(DOMString::from(String::from(public_id))), + Some(DOMString::from(String::from(system_id))), doc); + doc.upcast::<Node>().AppendChild(doctype.upcast()).expect("Appending failed"); + } + + fn create_pi(&mut self, target: StrTendril, data: StrTendril) -> JS<Node> { + let doc = &*self.document; + let pi = ProcessingInstruction::new( + DOMString::from(String::from(target)), DOMString::from(String::from(data)), + doc); + JS::from_ref(pi.upcast()) + } +} + pub enum ParseContext { - Owner(Option<i32>) + Owner(Option<PipelineId>) } -pub fn parse_xml(_document: &Document, - _input: DOMString, - _url: Url, - _context: ParseContext) { +pub fn parse_xml(document: &Document, + input: DOMString, + url: Url, + context: ParseContext) { + let parser = match context { + ParseContext::Owner(owner) => + ServoXMLParser::new(Some(url), document, owner), + }; + parser.parse_chunk(String::from(input)); } + diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 736c95b0d2d..5563a0a2ca9 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -27,7 +27,7 @@ use dom::bindings::conversions::{FromJSValConvertible, StringificationBehavior}; use dom::bindings::global::GlobalRef; use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, RootCollection, trace_roots}; -use dom::bindings::js::{Root, RootCollectionPtr, RootedReference}; +use dom::bindings::js::{RootCollectionPtr, RootedReference}; use dom::bindings::refcounted::{LiveDOMReferences, Trusted, TrustedReference, trace_refcounted_objects}; use dom::bindings::trace::{JSTraceable, RootedVec, trace_traceables}; use dom::bindings::utils::{DOM_CALLBACKS, WRAP_CALLBACKS}; @@ -36,7 +36,7 @@ use dom::element::Element; use dom::event::{Event, EventBubbles, EventCancelable}; use dom::htmlanchorelement::HTMLAnchorElement; use dom::node::{Node, NodeDamage, window_from_node}; -use dom::servohtmlparser::{ParserContext, ServoHTMLParser}; +use dom::servohtmlparser::{ParserContext, ParserRoot}; use dom::uievent::UIEvent; use dom::window::{ReflowReason, ScriptHelpers, Window}; use dom::worker::TrustedWorkerAddress; @@ -75,6 +75,7 @@ use net_traits::{AsyncResponseTarget, ControlMsg, LoadConsumer, Metadata, Resour use network_listener::NetworkListener; use page::{Frame, IterablePage, Page}; 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_traits::CompositorEvent::{KeyEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent}; @@ -594,7 +595,7 @@ pub unsafe extern "C" fn shadow_check_callback(_cx: *mut JSContext, impl ScriptTask { pub fn page_fetch_complete(id: PipelineId, subpage: Option<SubpageId>, metadata: Metadata) - -> Option<Root<ServoHTMLParser>> { + -> Option<ParserRoot> { SCRIPT_TASK_ROOT.with(|root| { let script_task = unsafe { &*root.borrow().unwrap() }; script_task.handle_page_fetch_complete(id, subpage, metadata) @@ -1450,7 +1451,7 @@ impl ScriptTask { /// 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_fetch_complete(&self, id: PipelineId, subpage: Option<SubpageId>, - metadata: Metadata) -> Option<Root<ServoHTMLParser>> { + metadata: Metadata) -> Option<ParserRoot> { let idx = self.incomplete_loads.borrow().iter().position(|load| { load.pipeline_id == id && load.parent_info.map(|info| info.1) == subpage }); @@ -1546,7 +1547,7 @@ impl ScriptTask { /// 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) -> Root<ServoHTMLParser> { + fn load(&self, metadata: Metadata, incomplete: InProgressLoad) -> ParserRoot { let final_url = metadata.final_url.clone(); debug!("ScriptTask: loading {} on page {:?}", incomplete.url.serialize(), incomplete.pipeline_id); @@ -1659,18 +1660,30 @@ impl ScriptTask { }); let content_type = match metadata.content_type { + Some(ContentType(Mime(TopLevel::Text, SubLevel::Xml, _))) => { + Some(DOMString::from("text/xml")) + } + Some(ContentType(Mime(TopLevel::Text, SubLevel::Plain, _))) => { Some(DOMString::from("text/plain")) } + _ => None }; let loader = DocumentLoader::new_with_task(self.resource_task.clone(), Some(page.pipeline()), Some(incomplete.url.clone())); + + let is_html_document = match metadata.content_type { + Some(ContentType(Mime(TopLevel::Text, SubLevel::Xml, _))) => + IsHTMLDocument::NonHTMLDocument, + _ => IsHTMLDocument::HTMLDocument, + }; + let document = Document::new(window.r(), Some(final_url.clone()), - IsHTMLDocument::HTMLDocument, + is_html_document, content_type, last_modified, DocumentSource::FromParser, @@ -1716,7 +1729,8 @@ impl ScriptTask { unsafe { let mut jsval = RootedValue::new(self.get_cx(), UndefinedValue()); window.evaluate_js_on_global_with_result(&script_source, jsval.handle_mut()); - let strval = DOMString::from_jsval(self.get_cx(), jsval.handle(), + let strval = DOMString::from_jsval(self.get_cx(), + jsval.handle(), StringificationBehavior::Empty); strval.unwrap_or(DOMString::new()) } @@ -1724,8 +1738,20 @@ impl ScriptTask { DOMString::new() }; - parse_html(document.r(), parse_input, final_url, - ParseContext::Owner(Some(incomplete.pipeline_id))); + match metadata.content_type { + Some(ContentType(Mime(TopLevel::Text, SubLevel::Xml, _))) => { + parse_xml(document.r(), + parse_input, + final_url, + xml::ParseContext::Owner(Some(incomplete.pipeline_id))); + } + _ => { + parse_html(document.r(), + parse_input, + final_url, + ParseContext::Owner(Some(incomplete.pipeline_id))); + } + } page_remover.neuter(); diff --git a/tests/wpt/metadata/dom/nodes/Document-contentType/contentType/contenttype_mimeheader_01.html.ini b/tests/wpt/metadata/dom/nodes/Document-contentType/contentType/contenttype_mimeheader_01.html.ini deleted file mode 100644 index 081ca04f8b4..00000000000 --- a/tests/wpt/metadata/dom/nodes/Document-contentType/contentType/contenttype_mimeheader_01.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[contenttype_mimeheader_01.html] - type: testharness - [Custom document.contentType === 'text/xml' when explicitly set to this value] - expected: FAIL - |