diff options
32 files changed, 442 insertions, 277 deletions
diff --git a/components/net_traits/lib.rs b/components/net_traits/lib.rs index 46fcc476843..d2ecae532be 100644 --- a/components/net_traits/lib.rs +++ b/components/net_traits/lib.rs @@ -132,8 +132,6 @@ pub struct PendingAsyncLoad { resource_task: ResourceTask, url: Url, pipeline: Option<PipelineId>, - input_sender: Sender<LoadResponse>, - input_receiver: Receiver<LoadResponse>, guard: PendingLoadGuard, } @@ -156,13 +154,10 @@ impl Drop for PendingLoadGuard { impl PendingAsyncLoad { pub fn new(resource_task: ResourceTask, url: Url, pipeline: Option<PipelineId>) -> PendingAsyncLoad { - let (sender, receiver) = channel(); PendingAsyncLoad { resource_task: resource_task, url: url, pipeline: pipeline, - input_sender: sender, - input_receiver: receiver, guard: PendingLoadGuard { loaded: false, }, } } @@ -171,9 +166,18 @@ impl PendingAsyncLoad { pub fn load(mut self) -> Receiver<LoadResponse> { self.guard.neuter(); let load_data = LoadData::new(self.url, self.pipeline); - let consumer = LoadConsumer::Channel(self.input_sender); + let (sender, receiver) = channel(); + let consumer = LoadConsumer::Channel(sender); + self.resource_task.send(ControlMsg::Load(load_data, consumer)).unwrap(); + receiver + } + + /// Initiate the network request associated with this pending load, using the provided target. + pub fn load_async(mut self, listener: Box<AsyncResponseTarget + Send>) { + self.guard.neuter(); + let load_data = LoadData::new(self.url, self.pipeline); + let consumer = LoadConsumer::Listener(listener); self.resource_task.send(ControlMsg::Load(load_data, consumer)).unwrap(); - self.input_receiver } } diff --git a/components/script/document_loader.rs b/components/script/document_loader.rs index 6b8dfcea801..14e107e9d02 100644 --- a/components/script/document_loader.rs +++ b/components/script/document_loader.rs @@ -7,13 +7,12 @@ use script_task::{ScriptMsg, ScriptChan}; use msg::constellation_msg::{PipelineId}; -use net_traits::{LoadResponse, Metadata, load_whole_resource, ResourceTask, PendingAsyncLoad}; +use net_traits::{Metadata, load_whole_resource, ResourceTask, PendingAsyncLoad}; +use net_traits::AsyncResponseTarget; use url::Url; -use std::sync::mpsc::Receiver; - #[jstraceable] -#[derive(PartialEq, Clone)] +#[derive(PartialEq, Clone, Debug)] pub enum LoadType { Image(Url), Script(Url), @@ -75,9 +74,9 @@ impl DocumentLoader { } /// Create and initiate a new network request. - pub fn load_async(&mut self, load: LoadType) -> Receiver<LoadResponse> { + pub fn load_async(&mut self, load: LoadType, listener: Box<AsyncResponseTarget + Send>) { let pending = self.prepare_async_load(load); - pending.load() + pending.load_async(listener) } /// Create, initiate, and await the response for a new network request. @@ -91,7 +90,7 @@ impl DocumentLoader { /// Mark an in-progress network request complete. pub fn finish_load(&mut self, load: LoadType) { let idx = self.blocking_loads.iter().position(|unfinished| *unfinished == load); - self.blocking_loads.remove(idx.expect("unknown completed load")); + self.blocking_loads.remove(idx.expect(&format!("unknown completed load {:?}", load))); if let Some(NotifierData { ref script_chan, pipeline }) = self.notifier_data { if !self.is_blocked() { diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 19901c43796..7c7347772ba 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -61,6 +61,7 @@ use dom::nodelist::NodeList; use dom::text::Text; use dom::processinginstruction::ProcessingInstruction; use dom::range::Range; +use dom::servohtmlparser::ServoHTMLParser; use dom::treewalker::TreeWalker; use dom::uievent::UIEvent; use dom::window::{Window, WindowHelpers, ReflowReason}; @@ -73,7 +74,7 @@ use msg::constellation_msg::{ConstellationChan, FocusType, Key, KeyState, KeyMod use msg::constellation_msg::{SUPER, ALT, SHIFT, CONTROL}; use net_traits::CookieSource::NonHTTP; use net_traits::ControlMsg::{SetCookiesForUrl, GetCookiesForUrl}; -use net_traits::{Metadata, LoadResponse, PendingAsyncLoad}; +use net_traits::{Metadata, PendingAsyncLoad, AsyncResponseTarget}; use script_task::Runnable; use script_traits::{MouseButton, UntrustedNodeAddress}; use util::opts; @@ -96,7 +97,7 @@ use std::ascii::AsciiExt; use std::cell::{Cell, Ref, RefMut, RefCell}; use std::default::Default; use std::ptr; -use std::sync::mpsc::{Receiver, channel}; +use std::sync::mpsc::channel; use time; #[derive(PartialEq)] @@ -145,6 +146,8 @@ pub struct Document { animation_frame_list: RefCell<HashMap<i32, Box<Fn(f64)>>>, /// 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>>, } impl DocumentDerived for EventTarget { @@ -263,9 +266,11 @@ pub trait DocumentHelpers<'a> { /// http://w3c.github.io/animation-timing/#dfn-invoke-callbacks-algorithm fn invoke_animation_callbacks(self); fn prepare_async_load(self, load: LoadType) -> PendingAsyncLoad; - fn load_async(self, load: LoadType) -> Receiver<LoadResponse>; + fn load_async(self, load: LoadType, listener: Box<AsyncResponseTarget + Send>); fn load_sync(self, load: LoadType) -> Result<(Metadata, Vec<u8>), String>; fn finish_load(self, load: LoadType); + fn set_current_parser(self, script: Option<JSRef<ServoHTMLParser>>); + fn get_current_parser(self) -> Option<Temporary<ServoHTMLParser>>; } impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { @@ -892,9 +897,9 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { loader.prepare_async_load(load) } - fn load_async(self, load: LoadType) -> Receiver<LoadResponse> { + fn load_async(self, load: LoadType, listener: Box<AsyncResponseTarget + Send>) { let mut loader = self.loader.borrow_mut(); - loader.load_async(load) + loader.load_async(load, listener) } fn load_sync(self, load: LoadType) -> Result<(Metadata, Vec<u8>), String> { @@ -906,6 +911,14 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { let mut loader = self.loader.borrow_mut(); loader.finish_load(load); } + + fn set_current_parser(self, script: Option<JSRef<ServoHTMLParser>>) { + self.current_parser.set(script.map(JS::from_rooted)); + } + + fn get_current_parser(self) -> Option<Temporary<ServoHTMLParser>> { + self.current_parser.get().map(Temporary::from_rooted) + } } pub enum MouseEventType { @@ -914,6 +927,7 @@ pub enum MouseEventType { MouseUp, } + #[derive(PartialEq)] pub enum DocumentSource { FromParser, @@ -987,6 +1001,7 @@ impl Document { animation_frame_ident: Cell::new(0), animation_frame_list: RefCell::new(HashMap::new()), loader: DOMRefCell::new(doc_loader), + current_parser: Default::default(), } } diff --git a/components/script/dom/domparser.rs b/components/script/dom/domparser.rs index 5685c3f324f..40c9d8f436c 100644 --- a/components/script/dom/domparser.rs +++ b/components/script/dom/domparser.rs @@ -15,7 +15,7 @@ use dom::bindings::utils::{Reflector, reflect_dom_object}; use dom::document::{Document, DocumentHelpers, IsHTMLDocument}; use dom::document::DocumentSource; use dom::window::{Window, WindowHelpers}; -use parse::html::{HTMLInput, parse_html}; +use parse::html::{ParseContext, parse_html}; use util::str::DOMString; use std::borrow::ToOwned; @@ -64,7 +64,7 @@ impl<'a> DOMParserMethods for JSRef<'a, DOMParser> { None, DocumentSource::FromParser, loader).root(); - parse_html(document.r(), HTMLInput::InputString(s), &url, None); + parse_html(document.r(), s, &url, ParseContext::Owner(None)); document.r().set_ready_state(DocumentReadyState::Complete); Ok(Temporary::from_rooted(document.r())) } diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index 2f7a52ec272..365d1a75e85 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -28,17 +28,21 @@ use dom::event::{Event, EventBubbles, EventCancelable, EventHelpers}; use dom::element::ElementTypeId; use dom::htmlelement::{HTMLElement, HTMLElementTypeId}; use dom::node::{Node, NodeHelpers, NodeTypeId, document_from_node, window_from_node, CloneChildrenFlag}; +use dom::servohtmlparser::ServoHTMLParserHelpers; use dom::virtualmethods::VirtualMethods; use dom::window::{WindowHelpers, ScriptHelpers}; -use script_task::{ScriptMsg, Runnable}; +use network_listener::{NetworkListener, PreInvoke}; +use script_task::{ScriptChan, ScriptMsg, Runnable}; use encoding::all::UTF_8; use encoding::label::encoding_from_whatwg_label; use encoding::types::{Encoding, EncodingRef, DecoderTrap}; -use net_traits::Metadata; +use net_traits::{Metadata, AsyncResponseListener}; use util::str::{DOMString, HTML_SPACE_CHARACTERS, StaticStringVec}; -use std::borrow::ToOwned; -use std::cell::Cell; +use html5ever::tree_builder::NextParserState; +use std::cell::{RefCell, Cell}; +use std::mem; +use std::sync::{Arc, Mutex}; use string_cache::Atom; use url::{Url, UrlParser}; @@ -99,7 +103,7 @@ impl HTMLScriptElement { pub trait HTMLScriptElementHelpers { /// Prepare a script (<https://www.whatwg.org/html/#prepare-a-script>) - fn prepare(self); + fn prepare(self) -> NextParserState; /// [Execute a script block] /// (https://html.spec.whatwg.org/multipage/#execute-the-script-block) @@ -153,12 +157,52 @@ pub enum ScriptOrigin { External(Result<(Metadata, Vec<u8>), String>), } +/// The context required for asynchronously loading an external script source. +struct ScriptContext { + /// The element that initiated the request. + elem: Trusted<HTMLScriptElement>, + /// The response body received to date. + data: RefCell<Vec<u8>>, + /// The response metadata received to date. + metadata: RefCell<Option<Metadata>>, + /// Whether the owning document's parser should resume once the response completes. + resume_on_completion: bool, +} + +impl AsyncResponseListener for ScriptContext { + fn headers_available(&self, metadata: Metadata) { + *self.metadata.borrow_mut() = Some(metadata); + } + + fn data_available(&self, payload: Vec<u8>) { + self.data.borrow_mut().extend(payload.into_iter()); + } + + fn response_complete(&self, status: Result<(), String>) { + let load = status.map(|_| { + let data = mem::replace(&mut *self.data.borrow_mut(), vec!()); + let metadata = self.metadata.borrow_mut().take().unwrap(); + (metadata, data) + }); + let elem = self.elem.to_temporary().root(); + + elem.r().execute(ScriptOrigin::External(load)); + + if self.resume_on_completion { + let document = document_from_node(elem.r()).root(); + document.r().get_current_parser().unwrap().root().r().resume(); + } + } +} + +impl PreInvoke for ScriptContext {} + impl<'a> HTMLScriptElementHelpers for JSRef<'a, HTMLScriptElement> { - fn prepare(self) { + fn prepare(self) -> NextParserState { // https://html.spec.whatwg.org/multipage/#prepare-a-script // Step 1. if self.already_started.get() { - return; + return NextParserState::Continue; } // Step 2. let was_parser_inserted = self.parser_inserted.get(); @@ -172,16 +216,16 @@ impl<'a> HTMLScriptElementHelpers for JSRef<'a, HTMLScriptElement> { // Step 4. let text = self.Text(); if text.len() == 0 && !element.has_attribute(&atom!("src")) { - return; + return NextParserState::Continue; } // Step 5. let node: JSRef<Node> = NodeCast::from_ref(self); if !node.is_in_doc() { - return; + return NextParserState::Continue; } // Step 6, 7. if !self.is_javascript() { - return; + return NextParserState::Continue; } // Step 8. if was_parser_inserted { @@ -195,12 +239,12 @@ impl<'a> HTMLScriptElementHelpers for JSRef<'a, HTMLScriptElement> { let document_from_node_ref = document_from_node(self).root(); let document_from_node_ref = document_from_node_ref.r(); if self.parser_inserted.get() && self.parser_document.root().r() != document_from_node_ref { - return; + return NextParserState::Continue; } // Step 11. if !document_from_node_ref.is_scripting_enabled() { - return; + return NextParserState::Continue; } // Step 12. @@ -212,13 +256,13 @@ impl<'a> HTMLScriptElementHelpers for JSRef<'a, HTMLScriptElement> { .to_ascii_lowercase(); let for_value = for_value.trim_matches(HTML_SPACE_CHARACTERS); if for_value != "window" { - return; + return NextParserState::Continue; } let event_value = event_attribute.Value().to_ascii_lowercase(); let event_value = event_value.trim_matches(HTML_SPACE_CHARACTERS); if event_value != "onload" && event_value != "onload()" { - return; + return NextParserState::Continue; } }, (_, _) => (), @@ -245,7 +289,7 @@ impl<'a> HTMLScriptElementHelpers for JSRef<'a, HTMLScriptElement> { // Step 14.2 if src.is_empty() { self.queue_error_event(); - return; + return NextParserState::Continue; } // Step 14.3 @@ -254,7 +298,7 @@ impl<'a> HTMLScriptElementHelpers for JSRef<'a, HTMLScriptElement> { // Step 14.4 error!("error parsing URL for script {}", src); self.queue_error_event(); - return; + return NextParserState::Continue; } Ok(url) => { // Step 14.5 @@ -263,8 +307,28 @@ impl<'a> HTMLScriptElementHelpers for JSRef<'a, HTMLScriptElement> { // the origin of the script element's node document, and the default origin // behaviour set to taint. let doc = document_from_node(self).root(); - let contents = doc.r().load_sync(LoadType::Script(url)); - ScriptOrigin::External(contents) + + let script_chan = window.script_chan(); + let elem = Trusted::new(window.get_cx(), self, script_chan.clone()); + + let context = Arc::new(Mutex::new(ScriptContext { + elem: elem, + data: RefCell::new(vec!()), + metadata: RefCell::new(None), + resume_on_completion: self.parser_inserted.get(), + })); + + let listener = box NetworkListener { + context: context, + script_chan: script_chan, + }; + + doc.r().load_async(LoadType::Script(url), listener); + + if self.parser_inserted.get() { + doc.r().get_current_parser().unwrap().root().r().suspend(); + } + return NextParserState::Suspend; } } }, @@ -275,6 +339,7 @@ impl<'a> HTMLScriptElementHelpers for JSRef<'a, HTMLScriptElement> { // TODO: Add support for the `defer` and `async` attributes. (For now, we fetch all // scripts synchronously and execute them immediately.) self.execute(load); + NextParserState::Continue } fn execute(self, load: ScriptOrigin) { diff --git a/components/script/dom/servohtmlparser.rs b/components/script/dom/servohtmlparser.rs index 199c06dca35..c4779a5666d 100644 --- a/components/script/dom/servohtmlparser.rs +++ b/components/script/dom/servohtmlparser.rs @@ -5,24 +5,35 @@ //! 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::ServoHTMLParserBinding; use dom::bindings::global::GlobalRef; use dom::bindings::trace::JSTraceable; use dom::bindings::js::{JS, JSRef, Rootable, Temporary}; +use dom::bindings::refcounted::Trusted; use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object}; use dom::document::{Document, DocumentHelpers}; -use dom::node::Node; +use dom::node::{window_from_node, Node}; +use dom::window::Window; +use network_listener::PreInvoke; use parse::Parser; +use script_task::{ScriptTask, ScriptChan}; -use util::task_state; +use msg::constellation_msg::{PipelineId, SubpageId}; +use net_traits::{Metadata, AsyncResponseListener}; +use encoding::all::UTF_8; +use encoding::types::{Encoding, DecoderTrap}; +use std::cell::{Cell, RefCell}; use std::default::Default; use url::Url; use js::jsapi::JSTracer; use html5ever::tokenizer; use html5ever::tree_builder; use html5ever::tree_builder::{TreeBuilder, TreeBuilderOpts}; +use hyper::header::ContentType; +use hyper::mime::{Mime, TopLevel, SubLevel}; #[must_root] #[jstraceable] @@ -41,6 +52,110 @@ pub struct FragmentContext<'a> { pub type Tokenizer = tokenizer::Tokenizer<TreeBuilder<JS<Node>, Sink>>; +/// The context required for asynchronously fetching a document and parsing it progressively. +pub struct ParserContext { + /// The parser that initiated the request. + parser: RefCell<Option<Trusted<ServoHTMLParser>>>, + /// Is this document a synthesized document for a single image? + is_image_document: Cell<bool>, + /// The pipeline associated with this document. + id: PipelineId, + /// The subpage associated with this document. + subpage: Option<SubpageId>, + /// The target event loop for the response notifications. + script_chan: Box<ScriptChan+Send>, + /// The URL for this document. + url: Url, +} + +impl ParserContext { + pub fn new(id: PipelineId, subpage: Option<SubpageId>, script_chan: Box<ScriptChan+Send>, + url: Url) -> ParserContext { + ParserContext { + parser: RefCell::new(None), + is_image_document: Cell::new(false), + id: id, + subpage: subpage, + script_chan: script_chan, + url: url, + } + } +} + +impl AsyncResponseListener for ParserContext { + fn headers_available(&self, metadata: Metadata) { + let content_type = metadata.content_type.clone(); + + let parser = ScriptTask::page_fetch_complete(self.id.clone(), self.subpage.clone(), + metadata); + let parser = match parser { + Some(parser) => parser, + None => return, + }.root(); + + let parser = parser.r(); + let win = parser.window().root(); + *self.parser.borrow_mut() = Some(Trusted::new(win.r().get_cx(), parser, + self.script_chan.clone())); + + match content_type { + Some(ContentType(Mime(TopLevel::Image, _, _))) => { + self.is_image_document.set(true); + let page = format!("<html><body><img src='{}' /></body></html>", + self.url.serialize()); + parser.pending_input.borrow_mut().push(page); + parser.parse_sync(); + } + Some(ContentType(Mime(TopLevel::Text, SubLevel::Plain, _))) => { + // FIXME: When servo/html5ever#109 is fixed remove <plaintext> usage and + // replace with fix from that issue. + + // text/plain documents require setting the tokenizer into PLAINTEXT mode. + // This is done by using a <plaintext> element as the html5ever tokenizer + // provides no other way to change to that state. + // Spec for text/plain handling is: + // https://html.spec.whatwg.org/multipage/#read-text + let page = format!("<pre>\u{000A}<plaintext>"); + parser.pending_input.borrow_mut().push(page); + parser.parse_sync(); + }, + _ => {} + } + } + + fn data_available(&self, payload: Vec<u8>) { + if !self.is_image_document.get() { + // FIXME: use Vec<u8> (html5ever #34) + let data = UTF_8.decode(&payload, DecoderTrap::Replace).unwrap(); + let parser = match self.parser.borrow().as_ref() { + Some(parser) => parser.to_temporary(), + None => return, + }.root(); + parser.r().parse_chunk(data); + } + } + + fn response_complete(&self, status: Result<(), String>) { + let parser = match self.parser.borrow().as_ref() { + Some(parser) => parser.to_temporary(), + None => return, + }.root(); + let doc = parser.r().document.root(); + doc.r().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.r().last_chunk_received.set(true); + parser.r().parse_sync(); + } +} + +impl PreInvoke for ParserContext { +} + // NB: JSTraceable is *not* auto-derived. // You must edit the impl below if you add fields! #[must_root] @@ -48,20 +163,46 @@ pub type Tokenizer = tokenizer::Tokenizer<TreeBuilder<JS<Node>, Sink>>; pub struct ServoHTMLParser { reflector_: Reflector, 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 Parser for ServoHTMLParser{ - fn parse_chunk(&self, input: String) { - self.tokenizer().borrow_mut().feed(input); +impl<'a> Parser for JSRef<'a, ServoHTMLParser> { + fn parse_chunk(self, input: String) { + self.document.root().r().set_current_parser(Some(self)); + self.pending_input.borrow_mut().push(input); + self.parse_sync(); } - fn finish(&self){ + + fn finish(self) { + assert!(!self.suspended.get()); + assert!(self.pending_input.borrow().is_empty()); + self.tokenizer().borrow_mut().end(); + debug!("finished parsing"); + + let document = self.document.root(); + document.r().set_current_parser(None); + + if let Some(pipeline) = self.pipeline { + ScriptTask::parsing_complete(pipeline); + } } } impl ServoHTMLParser { #[allow(unrooted_must_root)] - pub fn new(base_url: Option<Url>, document: JSRef<Document>) -> Temporary<ServoHTMLParser> { + pub fn new(base_url: Option<Url>, document: JSRef<Document>, pipeline: Option<PipelineId>) + -> Temporary<ServoHTMLParser> { let window = document.window().root(); let sink = Sink { base_url: base_url, @@ -78,6 +219,11 @@ impl ServoHTMLParser { let parser = ServoHTMLParser { reflector_: Reflector::new(), tokenizer: DOMRefCell::new(tok), + pending_input: DOMRefCell::new(vec!()), + document: JS::from_rooted(document), + suspended: Cell::new(false), + last_chunk_received: Cell::new(false), + pipeline: pipeline, }; reflect_dom_object(box parser, GlobalRef::Window(window.r()), @@ -111,6 +257,11 @@ impl ServoHTMLParser { let parser = ServoHTMLParser { reflector_: Reflector::new(), tokenizer: DOMRefCell::new(tok), + pending_input: DOMRefCell::new(vec!()), + document: JS::from_rooted(document), + suspended: Cell::new(false), + last_chunk_received: Cell::new(true), + pipeline: None, }; reflect_dom_object(box parser, GlobalRef::Window(window.r()), @@ -129,6 +280,73 @@ impl Reflectable for ServoHTMLParser { } } +trait PrivateServoHTMLParserHelpers { + /// Synchronously run the tokenizer parse loop until explicitly suspended or + /// the tokenizer runs out of input. + fn parse_sync(self); + /// Retrieve the window object associated with this parser. + fn window(self) -> Temporary<Window>; +} + +impl<'a> PrivateServoHTMLParserHelpers for JSRef<'a, ServoHTMLParser> { + fn parse_sync(self) { + let mut first = true; + + // This parser will continue to parse while there is either pending input or + // the parser remains unsuspended. + loop { + if self.suspended.get() { + return; + } + + if self.pending_input.borrow().is_empty() && !first { + break; + } + + 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); + } else { + self.tokenizer.borrow_mut().run(); + } + + first = false; + } + + if self.last_chunk_received.get() { + self.finish(); + } + } + + fn window(self) -> Temporary<Window> { + let doc = self.document.root(); + window_from_node(doc.r()) + } +} + +pub trait ServoHTMLParserHelpers { + /// Cause the parser to interrupt next time the tokenizer reaches a quiescent state. + /// No further parsing will occur after that point until the `resume` method is called. + /// Panics if the parser is already suspended. + fn suspend(self); + /// Immediately resume a suspended parser. Panics if the parser is not suspended. + fn resume(self); +} + +impl<'a> ServoHTMLParserHelpers for JSRef<'a, ServoHTMLParser> { + fn suspend(self) { + assert!(!self.suspended.get()); + self.suspended.set(true); + } + + fn resume(self) { + assert!(self.suspended.get()); + self.suspended.set(false); + self.parse_sync(); + } +} + struct Tracer { trc: *mut JSTracer, } @@ -152,11 +370,6 @@ impl JSTraceable for ServoHTMLParser { let tracer = &tracer as &tree_builder::Tracer<Handle=JS<Node>>; unsafe { - // Assertion: If the parser is mutably borrowed, we're in the - // parsing code paths. - debug_assert!(task_state::get().contains(task_state::IN_HTML_PARSER) - || !self.tokenizer.is_mutably_borrowed()); - let tokenizer = self.tokenizer.borrow_for_gc_trace(); let tree_builder = tokenizer.sink(); tree_builder.trace_handles(tracer); diff --git a/components/script/parse/html.rs b/components/script/parse/html.rs index 80885acf300..a9e125f7da9 100644 --- a/components/script/parse/html.rs +++ b/components/script/parse/html.rs @@ -4,7 +4,7 @@ #![allow(unsafe_code, unrooted_must_root)] -use document_loader::{DocumentLoader, LoadType}; +use document_loader::DocumentLoader; use dom::attr::AttrHelpers; use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; @@ -32,13 +32,10 @@ use dom::servohtmlparser::{ServoHTMLParser, FragmentContext}; use dom::text::Text; use parse::Parser; -use encoding::all::UTF_8; -use encoding::types::{Encoding, DecoderTrap}; +use encoding::types::Encoding; -use net_traits::{ProgressMsg, LoadResponse}; +use msg::constellation_msg::PipelineId; use util::str::DOMString; -use util::task_state; -use util::task_state::IN_HTML_PARSER; use std::borrow::Cow; use std::io::{self, Write}; use url::Url; @@ -49,14 +46,6 @@ use html5ever::serialize::TraversalScope::{IncludeNode, ChildrenOnly}; use html5ever::tree_builder::{TreeSink, QuirksMode, NodeOrText, AppendNode, AppendText, NextParserState}; use string_cache::QualName; -use hyper::header::ContentType; -use hyper::mime::{Mime, TopLevel, SubLevel}; - -pub enum HTMLInput { - InputString(String), - InputUrl(LoadResponse), -} - trait SinkHelpers { fn get_or_create(&self, child: NodeOrText<JS<Node>>) -> Temporary<Node>; } @@ -183,7 +172,9 @@ impl<'a> TreeSink for servohtmlparser::Sink { fn complete_script(&mut self, node: JS<Node>) -> NextParserState { let node: Root<Node> = node.root(); let script: Option<JSRef<HTMLScriptElement>> = HTMLScriptElementCast::to_ref(node.r()); - script.map(|script| script.prepare()); + if let Some(script) = script { + return script.prepare(); + } NextParserState::Continue } @@ -272,80 +263,22 @@ impl<'a> Serializable for JSRef<'a, Node> { } } +pub enum ParseContext<'a> { + Fragment(FragmentContext<'a>), + Owner(Option<PipelineId>), +} + pub fn parse_html(document: JSRef<Document>, - input: HTMLInput, + input: String, url: &Url, - fragment_context: Option<FragmentContext>) { - let parser = match fragment_context { - None => ServoHTMLParser::new(Some(url.clone()), document).root(), - Some(fc) => ServoHTMLParser::new_for_fragment(Some(url.clone()), document, fc).root(), - }; - let parser: JSRef<ServoHTMLParser> = parser.r(); - - let nested_parse = task_state::get().contains(task_state::IN_HTML_PARSER); - if !nested_parse { - task_state::enter(IN_HTML_PARSER); - } - - fn parse_progress(document: JSRef<Document>, parser: JSRef<ServoHTMLParser>, url: &Url, load_response: &LoadResponse) { - for msg in load_response.progress_port.iter() { - match msg { - ProgressMsg::Payload(data) => { - // FIXME: use Vec<u8> (html5ever #34) - let data = UTF_8.decode(&data, DecoderTrap::Replace).unwrap(); - parser.parse_chunk(data); - } - ProgressMsg::Done(Err(err)) => { - debug!("Failed to load page URL {}, error: {}", url.serialize(), err); - // TODO(Savago): we should send a notification to callers #5463. - break; - } - ProgressMsg::Done(Ok(())) => { - document.finish_load(LoadType::PageSource(url.clone())); - break; - } - } - } - }; - - match input { - HTMLInput::InputString(s) => { - parser.parse_chunk(s); - } - HTMLInput::InputUrl(load_response) => { - match load_response.metadata.content_type { - Some(ContentType(Mime(TopLevel::Image, _, _))) => { - let page = format!("<html><body><img src='{}' /></body></html>", url.serialize()); - parser.parse_chunk(page); - document.finish_load(LoadType::PageSource(url.clone())); - }, - Some(ContentType(Mime(TopLevel::Text, SubLevel::Plain, _))) => { - // FIXME: When servo/html5ever#109 is fixed remove <plaintext> usage and - // replace with fix from that issue. - - // text/plain documents require setting the tokenizer into PLAINTEXT mode. - // This is done by using a <plaintext> element as the html5ever tokenizer - // provides no other way to change to that state. - // Spec for text/plain handling is: - // https://html.spec.whatwg.org/multipage/#read-text - let page = format!("<pre>\u{000A}<plaintext>"); - parser.parse_chunk(page); - parse_progress(document, parser, url, &load_response); - }, - _ => { - parse_progress(document, parser, url, &load_response); - } - } - } - } - - parser.finish(); - - if !nested_parse { - task_state::exit(IN_HTML_PARSER); - } - - debug!("finished parsing"); + context: ParseContext) { + let parser = match context { + ParseContext::Owner(owner) => + ServoHTMLParser::new(Some(url.clone()), document, owner), + ParseContext::Fragment(fc) => + ServoHTMLParser::new_for_fragment(Some(url.clone()), document, fc), + }.root(); + parser.r().parse_chunk(input); } // https://html.spec.whatwg.org/multipage/#parsing-html-fragments @@ -376,7 +309,7 @@ pub fn parse_html_fragment(context_node: JSRef<Node>, context_elem: context_node, form_elem: form.r(), }; - parse_html(document.r(), HTMLInput::InputString(input), &url, Some(fragment_context)); + parse_html(document.r(), input, &url, ParseContext::Fragment(fragment_context)); // Step 14. let root_element = document.r().GetDocumentElement().expect("no document element").root(); diff --git a/components/script/parse/mod.rs b/components/script/parse/mod.rs index 6111f196053..fa93dbc157f 100644 --- a/components/script/parse/mod.rs +++ b/components/script/parse/mod.rs @@ -5,6 +5,6 @@ pub mod html; pub trait Parser { - fn parse_chunk(&self,input: String); - fn finish(&self); + fn parse_chunk(self, input: String); + fn finish(self); } diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 014e32f2f97..d7b785403cd 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -39,11 +39,13 @@ use dom::htmliframeelement::{HTMLIFrameElement, HTMLIFrameElementHelpers}; use dom::uievent::UIEvent; use dom::eventtarget::EventTarget; use dom::node::{Node, NodeHelpers, NodeDamage, window_from_node}; +use dom::servohtmlparser::{ServoHTMLParser, ParserContext}; use dom::window::{Window, WindowHelpers, ScriptHelpers, ReflowReason}; use dom::worker::TrustedWorkerAddress; -use parse::html::{HTMLInput, parse_html}; +use parse::html::{ParseContext, parse_html}; use layout_interface::{ScriptLayoutChan, LayoutChan, ReflowGoal, ReflowQueryType}; use layout_interface; +use network_listener::NetworkListener; use page::{Page, IterablePage, Frame}; use timers::TimerId; use devtools; @@ -65,13 +67,13 @@ use msg::constellation_msg::{ConstellationChan, FocusType}; use msg::constellation_msg::{LoadData, PipelineId, SubpageId, MozBrowserEvent, WorkerId}; use msg::constellation_msg::{Failure, WindowSizeData, PipelineExitType}; use msg::constellation_msg::Msg as ConstellationMsg; -use net_traits::{ResourceTask, LoadResponse, LoadConsumer, ControlMsg}; +use net_traits::{ResourceTask, LoadConsumer, ControlMsg, Metadata}; use net_traits::LoadData as NetLoadData; use net_traits::image_cache_task::{ImageCacheChan, ImageCacheTask, ImageCacheResult}; use net_traits::storage_task::StorageTask; use string_cache::Atom; use util::str::DOMString; -use util::task::{spawn_named, spawn_named_with_send_on_failure}; +use util::task::spawn_named_with_send_on_failure; use util::task_state; use geom::Rect; @@ -92,6 +94,7 @@ use std::option::Option; use std::ptr; use std::rc::Rc; use std::result::Result; +use std::sync::{Arc, Mutex}; use std::sync::mpsc::{channel, Sender, Receiver, Select}; use time::Tm; @@ -188,8 +191,6 @@ pub enum ScriptMsg { MainThreadRunnableMsg(Box<MainThreadRunnable+Send>), /// A DOM object's last pinned reference was removed (dispatched to all tasks). RefcountCleanup(TrustedReference), - /// The final network response for a page has arrived. - PageFetchComplete(PipelineId, Option<SubpageId>, LoadResponse), /// Notify a document that all pending loads are complete. DocumentLoadsComplete(PipelineId), } @@ -427,6 +428,21 @@ unsafe extern "C" fn debug_gc_callback(_rt: *mut JSRuntime, status: JSGCStatus) } impl ScriptTask { + pub fn page_fetch_complete(id: PipelineId, subpage: Option<SubpageId>, metadata: Metadata) + -> Option<Temporary<ServoHTMLParser>> { + SCRIPT_TASK_ROOT.with(|root| { + let script_task = unsafe { &*root.borrow().unwrap() }; + script_task.handle_page_fetch_complete(id, subpage, metadata) + }) + } + + pub fn parsing_complete(id: PipelineId) { + SCRIPT_TASK_ROOT.with(|root| { + let script_task = unsafe { &*root.borrow().unwrap() }; + script_task.handle_parsing_complete(id); + }); + } + pub fn process_event(msg: ScriptMsg) { SCRIPT_TASK_ROOT.with(|root| { if let Some(script_task) = *root.borrow() { @@ -765,8 +781,6 @@ impl ScriptTask { runnable.handler(self), ScriptMsg::RefcountCleanup(addr) => LiveDOMReferences::cleanup(self.get_cx(), addr), - ScriptMsg::PageFetchComplete(id, subpage, response) => - self.handle_page_fetch_complete(id, subpage, response), ScriptMsg::DocumentLoadsComplete(id) => self.handle_loads_complete(id), } @@ -1069,7 +1083,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>, - response: LoadResponse) { + metadata: Metadata) -> Option<Temporary<ServoHTMLParser>> { let idx = self.incomplete_loads.borrow().iter().position(|load| { load.pipeline_id == id && load.parent_info.map(|info| info.1) == subpage }); @@ -1078,10 +1092,11 @@ impl ScriptTask { match idx { Some(idx) => { let load = self.incomplete_loads.borrow_mut().remove(idx); - self.load(response, load); + Some(self.load(metadata, load)) } None => { assert!(self.closed_pipelines.borrow().contains(&id)); + None } } } @@ -1148,8 +1163,8 @@ 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, response: LoadResponse, incomplete: InProgressLoad) { - let final_url = response.metadata.final_url.clone(); + fn load(&self, metadata: Metadata, incomplete: InProgressLoad) -> Temporary<ServoHTMLParser> { + let final_url = metadata.final_url.clone(); debug!("ScriptTask: loading {} on page {:?}", incomplete.url.serialize(), incomplete.pipeline_id); // We should either be initializing a root page or loading a child page of an @@ -1249,11 +1264,11 @@ impl ScriptTask { incomplete.parent_info, incomplete.window_size).root(); - let last_modified: Option<DOMString> = response.metadata.headers.as_ref().and_then(|headers| { + let last_modified: Option<DOMString> = metadata.headers.as_ref().and_then(|headers| { headers.get().map(|&LastModified(HttpDate(ref tm))| dom_last_modified(tm)) }); - let content_type = match response.metadata.content_type { + let content_type = match metadata.content_type { Some(ContentType(Mime(TopLevel::Text, SubLevel::Plain, _))) => Some("text/plain".to_owned()), _ => None }; @@ -1264,7 +1279,7 @@ impl ScriptTask { }; let loader = DocumentLoader::new_with_task(self.resource_task.clone(), Some(notifier_data), - Some(final_url.clone())); + Some(incomplete.url.clone())); let document = Document::new(window.r(), Some(final_url.clone()), IsHTMLDocument::HTMLDocument, @@ -1288,14 +1303,17 @@ impl ScriptTask { let jsval = window.r().evaluate_js_on_global_with_result(evalstr); let strval = FromJSValConvertible::from_jsval(self.get_cx(), jsval, StringificationBehavior::Empty); - HTMLInput::InputString(strval.unwrap_or("".to_owned())) + strval.unwrap_or("".to_owned()) } else { - HTMLInput::InputUrl(response) + "".to_owned() }; - parse_html(document.r(), parse_input, &final_url, None); - self.handle_parsing_complete(incomplete.pipeline_id); + parse_html(document.r(), parse_input, &final_url, + ParseContext::Owner(Some(incomplete.pipeline_id))); + page_remover.neuter(); + + document.r().get_current_parser().unwrap() } fn notify_devtools(&self, title: DOMString, url: Url, ids: (PipelineId, Option<WorkerId>)) { @@ -1480,29 +1498,26 @@ impl ScriptTask { let script_chan = self.chan.clone(); let resource_task = self.resource_task.clone(); - spawn_named(format!("fetch for {:?}", load_data.url.serialize()), move || { - if load_data.url.scheme == "javascript" { - load_data.url = Url::parse("about:blank").unwrap(); - } + let context = Arc::new(Mutex::new(ParserContext::new(id, subpage, script_chan.clone(), + load_data.url.clone()))); + let listener = box NetworkListener { + context: context, + script_chan: script_chan.clone(), + }; - let (input_chan, input_port) = channel(); - resource_task.send(ControlMsg::Load(NetLoadData { - url: load_data.url, - method: load_data.method, - headers: Headers::new(), - preserved_headers: load_data.headers, - data: load_data.data, - cors: None, - pipeline_id: Some(id), - }, LoadConsumer::Channel(input_chan))).unwrap(); - - let load_response = input_port.recv().unwrap(); - if script_chan.send(ScriptMsg::PageFetchComplete(id, subpage, load_response)).is_err() { - // TODO(gw): This should be handled by aborting - // the load before the script task exits. - debug!("PageFetchComplete: script channel has exited"); - } - }); + if load_data.url.scheme == "javascript" { + load_data.url = Url::parse("about:blank").unwrap(); + } + + resource_task.send(ControlMsg::Load(NetLoadData { + url: load_data.url, + method: load_data.method, + headers: Headers::new(), + preserved_headers: load_data.headers, + data: load_data.data, + cors: None, + pipeline_id: Some(id), + }, LoadConsumer::Listener(listener))).unwrap(); self.incomplete_loads.borrow_mut().push(incomplete); } @@ -1541,6 +1556,7 @@ impl ScriptTask { // Kick off the initial reflow of the page. debug!("kicking off initial reflow of {:?}", final_url); + document.r().content_changed(NodeCast::from_ref(document.r()), NodeDamage::OtherNodeDamage); let window = window_from_node(document.r()).root(); diff --git a/tests/wpt/metadata/html/semantics/scripting-1/the-script-element/async_006.htm.ini b/tests/wpt/metadata/html/semantics/scripting-1/the-script-element/async_006.htm.ini deleted file mode 100644 index 37604f00b8e..00000000000 --- a/tests/wpt/metadata/html/semantics/scripting-1/the-script-element/async_006.htm.ini +++ /dev/null @@ -1,5 +0,0 @@ -[async_006.htm] - type: testharness - [dynamically created external script executes asynchronously] - expected: FAIL - diff --git a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/015.html.ini b/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/015.html.ini deleted file mode 100644 index 0eb052e7dda..00000000000 --- a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/015.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[015.html] - type: testharness - [ scheduler: DOM added inline+external+inline script earlier in document] - expected: FAIL - diff --git a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/015a.html.ini b/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/015a.html.ini deleted file mode 100644 index f13a4ad5783..00000000000 --- a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/015a.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[015a.html] - type: testharness - [ scheduler: DOM added inline+external+inline script earlier in document] - expected: FAIL - diff --git a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/017.html.ini b/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/017.html.ini deleted file mode 100644 index c53367410e5..00000000000 --- a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/017.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[017.html] - type: testharness - [ scheduler: multiple DOM added scripts later in document] - expected: FAIL - diff --git a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/020.html.ini b/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/020.html.ini deleted file mode 100644 index 5fe0007df72..00000000000 --- a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/020.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[020.html] - type: testharness - [ scheduler: DOM added script with data: URL ] - expected: FAIL - diff --git a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/022.html.ini b/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/022.html.ini deleted file mode 100644 index 86432e7f001..00000000000 --- a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/022.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[022.html] - type: testharness - [ scheduler: DOM added script, late .src ] - expected: FAIL - diff --git a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/023.html.ini b/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/023.html.ini deleted file mode 100644 index f0fbd642ec7..00000000000 --- a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/023.html.ini +++ /dev/null @@ -1,6 +0,0 @@ -[023.html] - type: testharness - expected: TIMEOUT - [ scheduler: DOM added script, even later .src ] - expected: TIMEOUT - diff --git a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/024.html.ini b/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/024.html.ini deleted file mode 100644 index 34063b39d78..00000000000 --- a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/024.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[024.html] - type: testharness - [ scheduler: DOM added script, .src set twice] - expected: FAIL - diff --git a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/038.html.ini b/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/038.html.ini deleted file mode 100644 index 174d761ea84..00000000000 --- a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/038.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[038.html] - type: testharness - [ scheduler: DOM movement with appendChild, external] - expected: FAIL - diff --git a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/046.html.ini b/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/046.html.ini deleted file mode 100644 index 75d42b69a57..00000000000 --- a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/046.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[046.html] - type: testharness - [ scheduler: no readystatechange events when adding external scripts ] - expected: FAIL - diff --git a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/047.html.ini b/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/047.html.ini deleted file mode 100644 index 9c1ceeaa4d9..00000000000 --- a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/047.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[047.html] - type: testharness - [ scheduler: adding and removing external script ] - expected: FAIL - diff --git a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/049.html.ini b/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/049.html.ini deleted file mode 100644 index fcc9435392a..00000000000 --- a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/049.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[049.html] - type: testharness - [ scheduler: adding external script but removeAttribute( src ) before it runs] - expected: FAIL - diff --git a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/050.html.ini b/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/050.html.ini deleted file mode 100644 index 2bfc46d9285..00000000000 --- a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/050.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[050.html] - type: testharness - [ scheduler: adding external script that removes all scripts from document] - expected: FAIL - diff --git a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/051.html.ini b/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/051.html.ini deleted file mode 100644 index c524ee1bc17..00000000000 --- a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/051.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[051.html] - type: testharness - [ scheduler: interaction of parsing and script execution - script added through DOM] - expected: FAIL - diff --git a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/053.html.ini b/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/053.html.ini deleted file mode 100644 index 552647fe0a4..00000000000 --- a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/053.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[053.html] - type: testharness - [ scheduler: adding external script that removes itself from document when loading] - expected: FAIL - diff --git a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/069.html.ini b/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/069.html.ini deleted file mode 100644 index 602df71772f..00000000000 --- a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/069.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[069.html] - type: testharness - [scheduler: external files added through DOM should not block further parsing while loading] - expected: FAIL - diff --git a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/076.html.ini b/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/076.html.ini deleted file mode 100644 index 6da81e6bb68..00000000000 --- a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/076.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[076.html] - type: testharness - [ scheduler: adding and removing external and inline scripts ] - expected: FAIL - diff --git a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/078.html.ini b/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/078.html.ini deleted file mode 100644 index 6ce0192273a..00000000000 --- a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/078.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[078.html] - type: testharness - [ adding several types of scripts through the DOM and removing some of them confuses scheduler (slow-loading scripts) ] - expected: FAIL - diff --git a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/095.html.ini b/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/095.html.ini new file mode 100644 index 00000000000..ceab463aa64 --- /dev/null +++ b/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/095.html.ini @@ -0,0 +1,4 @@ +[095.html] + type: testharness + [ scheduler: slow-loading script added from defer blocking load event] + expected: FAIL diff --git a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/105.html.ini b/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/105.html.ini new file mode 100644 index 00000000000..19b0eb955c4 --- /dev/null +++ b/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/105.html.ini @@ -0,0 +1,4 @@ +[105.html] + type: testharness + [ scheduler: adding async attribute at runtime] + expected: FAIL diff --git a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/123.html.ini b/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/123.html.ini new file mode 100644 index 00000000000..1d440d08c75 --- /dev/null +++ b/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/123.html.ini @@ -0,0 +1,4 @@ +[123.html] + type: testharness + [scheduler: altering the type attribute and adding/removing external script with async=false ] + expected: FAIL diff --git a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/126.html.ini b/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/126.html.ini new file mode 100644 index 00000000000..12530634ca8 --- /dev/null +++ b/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/126.html.ini @@ -0,0 +1,4 @@ +[126.html] + type: testharness + [scheduler: altering the type attribute and changing script data external script async=false ] + expected: FAIL diff --git a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/134.html.ini b/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/134.html.ini deleted file mode 100644 index a01a182559d..00000000000 --- a/tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/134.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[134.html] - type: testharness - [scheduler: external HTML script added by SVG script ] - expected: FAIL - |