aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/net_traits/lib.rs18
-rw-r--r--components/script/document_loader.rs13
-rw-r--r--components/script/dom/document.rs25
-rw-r--r--components/script/dom/domparser.rs4
-rw-r--r--components/script/dom/htmlscriptelement.rs101
-rw-r--r--components/script/dom/servohtmlparser.rs237
-rw-r--r--components/script/parse/html.rs109
-rw-r--r--components/script/parse/mod.rs4
-rw-r--r--components/script/script_task.rs96
-rw-r--r--tests/wpt/metadata/html/semantics/scripting-1/the-script-element/async_006.htm.ini5
-rw-r--r--tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/015.html.ini5
-rw-r--r--tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/015a.html.ini5
-rw-r--r--tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/017.html.ini5
-rw-r--r--tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/020.html.ini5
-rw-r--r--tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/022.html.ini5
-rw-r--r--tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/023.html.ini6
-rw-r--r--tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/024.html.ini5
-rw-r--r--tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/038.html.ini5
-rw-r--r--tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/046.html.ini5
-rw-r--r--tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/047.html.ini5
-rw-r--r--tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/049.html.ini5
-rw-r--r--tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/050.html.ini5
-rw-r--r--tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/051.html.ini5
-rw-r--r--tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/053.html.ini5
-rw-r--r--tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/069.html.ini5
-rw-r--r--tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/076.html.ini5
-rw-r--r--tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/078.html.ini5
-rw-r--r--tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/095.html.ini4
-rw-r--r--tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/105.html.ini4
-rw-r--r--tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/123.html.ini4
-rw-r--r--tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/126.html.ini4
-rw-r--r--tests/wpt/metadata/old-tests/submission/Opera/script_scheduling/134.html.ini5
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
-