Implement a DocumentLoader type that tracks pending loads and notifies the script task when the queue is empty. Dispatch the document load event based on the DocumentLoader's notification.

This commit is contained in:
Josh Matthews 2014-10-13 08:20:06 -04:00
parent 29a43a00b3
commit 7f0706ed42
11 changed files with 243 additions and 38 deletions

View file

@ -0,0 +1,106 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Tracking of pending loads in a document.
//! https://html.spec.whatwg.org/multipage/#the-end
use script_task::{ScriptMsg, ScriptChan};
use msg::constellation_msg::{PipelineId};
use net_traits::{LoadResponse, Metadata, load_whole_resource, ResourceTask};
use net_traits::{ControlMsg, LoadData, LoadConsumer};
use url::Url;
use std::sync::mpsc::{Receiver, channel};
#[jstraceable]
#[derive(PartialEq, Clone)]
pub enum LoadType {
Image(Url),
Script(Url),
Subframe(Url),
Stylesheet(Url),
PageSource(Url),
}
impl LoadType {
fn url(&self) -> &Url {
match *self {
LoadType::Image(ref url) |
LoadType::Script(ref url) |
LoadType::Subframe(ref url) |
LoadType::Stylesheet(ref url) |
LoadType::PageSource(ref url) => url,
}
}
}
#[jstraceable]
pub struct DocumentLoader {
pub resource_task: ResourceTask,
notifier_data: Option<NotifierData>,
blocking_loads: Vec<LoadType>,
}
#[jstraceable]
pub struct NotifierData {
pub script_chan: Box<ScriptChan + Send>,
pub pipeline: PipelineId,
}
impl DocumentLoader {
pub fn new(existing: &DocumentLoader) -> DocumentLoader {
DocumentLoader::new_with_task(existing.resource_task.clone(), None, None)
}
pub fn new_with_task(resource_task: ResourceTask,
data: Option<NotifierData>,
initial_load: Option<Url>,)
-> DocumentLoader {
let mut initial_loads = vec!();
if let Some(load) = initial_load {
initial_loads.push(LoadType::PageSource(load));
}
DocumentLoader {
resource_task: resource_task,
notifier_data: data,
blocking_loads: initial_loads,
}
}
pub fn load_async(&mut self, load: LoadType) -> Receiver<LoadResponse> {
let (tx, rx) = channel();
self.blocking_loads.push(load.clone());
let pipeline = self.notifier_data.as_ref().map(|data| data.pipeline);
let load_data = LoadData::new(load.url().clone(), pipeline);
self.resource_task.send(ControlMsg::Load(load_data, LoadConsumer::Channel(tx))).unwrap();
rx
}
pub fn load_sync(&mut self, load: LoadType) -> Result<(Metadata, Vec<u8>), String> {
self.blocking_loads.push(load.clone());
let result = load_whole_resource(&self.resource_task, load.url().clone());
self.finish_load(load);
result
}
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"));
if let Some(NotifierData { ref script_chan, pipeline }) = self.notifier_data {
if !self.is_blocked() {
script_chan.send(ScriptMsg::DocumentLoadsComplete(pipeline)).unwrap();
}
}
}
pub fn is_blocked(&self) -> bool {
!self.blocking_loads.is_empty()
}
pub fn inhibit_events(&mut self) {
self.notifier_data = None;
}
}

View file

@ -7,9 +7,11 @@
//! This module contains smart pointers to global scopes, to simplify writing //! This module contains smart pointers to global scopes, to simplify writing
//! code that works in workers as well as window scopes. //! code that works in workers as well as window scopes.
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::conversions::native_from_reflector_jsmanaged; use dom::bindings::conversions::native_from_reflector_jsmanaged;
use dom::bindings::js::{JS, JSRef, Rootable, Root, Unrooted}; use dom::bindings::js::{JS, JSRef, Rootable, Root, Unrooted};
use dom::bindings::utils::{Reflectable, Reflector}; use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::DocumentHelpers;
use dom::workerglobalscope::{WorkerGlobalScope, WorkerGlobalScopeHelpers}; use dom::workerglobalscope::{WorkerGlobalScope, WorkerGlobalScopeHelpers};
use dom::window::{self, WindowHelpers}; use dom::window::{self, WindowHelpers};
use devtools_traits::DevtoolsControlChan; use devtools_traits::DevtoolsControlChan;
@ -101,7 +103,12 @@ impl<'a> GlobalRef<'a> {
/// Get the `ResourceTask` for this global scope. /// Get the `ResourceTask` for this global scope.
pub fn resource_task(&self) -> ResourceTask { pub fn resource_task(&self) -> ResourceTask {
match *self { match *self {
GlobalRef::Window(ref window) => window.resource_task().clone(), GlobalRef::Window(ref window) => {
let doc = window.Document().root();
let doc = doc.r();
let loader = doc.loader();
loader.resource_task.clone()
}
GlobalRef::Worker(ref worker) => worker.resource_task().clone(), GlobalRef::Worker(ref worker) => worker.resource_task().clone(),
} }
} }

View file

@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use document_loader::{DocumentLoader, LoadType};
use dom::attr::{Attr, AttrHelpers, AttrValue}; use dom::attr::{Attr, AttrHelpers, AttrValue};
use dom::bindings::cell::DOMRefCell; use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::DocumentBinding; use dom::bindings::codegen::Bindings::DocumentBinding;
@ -71,6 +72,7 @@ use msg::constellation_msg::{ConstellationChan, FocusType, Key, KeyState, KeyMod
use msg::constellation_msg::{SUPER, ALT, SHIFT, CONTROL}; use msg::constellation_msg::{SUPER, ALT, SHIFT, CONTROL};
use net_traits::CookieSource::NonHTTP; use net_traits::CookieSource::NonHTTP;
use net_traits::ControlMsg::{SetCookiesForUrl, GetCookiesForUrl}; use net_traits::ControlMsg::{SetCookiesForUrl, GetCookiesForUrl};
use net_traits::{Metadata, LoadResponse};
use script_task::Runnable; use script_task::Runnable;
use script_traits::{MouseButton, UntrustedNodeAddress}; use script_traits::{MouseButton, UntrustedNodeAddress};
use util::opts; use util::opts;
@ -90,9 +92,9 @@ use std::borrow::ToOwned;
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
use std::cell::{Cell, Ref, RefCell}; use std::cell::{Cell, Ref, RefMut, RefCell};
use std::default::Default; use std::default::Default;
use std::sync::mpsc::channel; use std::sync::mpsc::{Receiver, channel};
use time; use time;
#[derive(PartialEq)] #[derive(PartialEq)]
@ -139,6 +141,8 @@ pub struct Document {
/// https://html.spec.whatwg.org/multipage/#list-of-animation-frame-callbacks /// https://html.spec.whatwg.org/multipage/#list-of-animation-frame-callbacks
/// List of animation frame callbacks /// List of animation frame callbacks
animation_frame_list: RefCell<HashMap<i32, Box<Fn(f64)>>>, animation_frame_list: RefCell<HashMap<i32, Box<Fn(f64)>>>,
/// Tracks all outstanding loads related to this document.
loader: DOMRefCell<DocumentLoader>,
} }
impl DocumentDerived for EventTarget { impl DocumentDerived for EventTarget {
@ -205,6 +209,8 @@ impl CollectionFilter for AppletsFilter {
} }
pub trait DocumentHelpers<'a> { pub trait DocumentHelpers<'a> {
fn loader(&self) -> Ref<DocumentLoader>;
fn mut_loader(&self) -> RefMut<DocumentLoader>;
fn window(self) -> Temporary<Window>; fn window(self) -> Temporary<Window>;
fn encoding_name(self) -> Ref<'a, DOMString>; fn encoding_name(self) -> Ref<'a, DOMString>;
fn is_html_document(self) -> bool; fn is_html_document(self) -> bool;
@ -254,9 +260,22 @@ pub trait DocumentHelpers<'a> {
fn cancel_animation_frame(self, ident: i32); fn cancel_animation_frame(self, ident: i32);
/// http://w3c.github.io/animation-timing/#dfn-invoke-callbacks-algorithm /// http://w3c.github.io/animation-timing/#dfn-invoke-callbacks-algorithm
fn invoke_animation_callbacks(self); fn invoke_animation_callbacks(self);
fn load_async(self, load: LoadType) -> Receiver<LoadResponse>;
fn load_sync(self, load: LoadType) -> Result<(Metadata, Vec<u8>), String>;
fn finish_load(self, load: LoadType);
} }
impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> {
#[inline]
fn loader(&self) -> Ref<DocumentLoader> {
self.loader.borrow()
}
#[inline]
fn mut_loader(&self) -> RefMut<DocumentLoader> {
self.loader.borrow_mut()
}
#[inline] #[inline]
fn window(self) -> Temporary<Window> { fn window(self) -> Temporary<Window> {
Temporary::from_rooted(self.window) Temporary::from_rooted(self.window)
@ -864,6 +883,21 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> {
callback(*performance.Now()); callback(*performance.Now());
} }
} }
fn load_async(self, load: LoadType) -> Receiver<LoadResponse> {
let mut loader = self.loader.borrow_mut();
loader.load_async(load)
}
fn load_sync(self, load: LoadType) -> Result<(Metadata, Vec<u8>), String> {
let mut loader = self.loader.borrow_mut();
loader.load_sync(load)
}
fn finish_load(self, load: LoadType) {
let mut loader = self.loader.borrow_mut();
loader.finish_load(load);
}
} }
pub enum MouseEventType { pub enum MouseEventType {
@ -898,7 +932,8 @@ impl Document {
is_html_document: IsHTMLDocument, is_html_document: IsHTMLDocument,
content_type: Option<DOMString>, content_type: Option<DOMString>,
last_modified: Option<DOMString>, last_modified: Option<DOMString>,
source: DocumentSource) -> Document { source: DocumentSource,
doc_loader: DocumentLoader) -> Document {
let url = url.unwrap_or_else(|| Url::parse("about:blank").unwrap()); let url = url.unwrap_or_else(|| Url::parse("about:blank").unwrap());
let ready_state = if source == DocumentSource::FromParser { let ready_state = if source == DocumentSource::FromParser {
@ -943,14 +978,19 @@ impl Document {
scripting_enabled: Cell::new(true), scripting_enabled: Cell::new(true),
animation_frame_ident: Cell::new(0), animation_frame_ident: Cell::new(0),
animation_frame_list: RefCell::new(HashMap::new()), animation_frame_list: RefCell::new(HashMap::new()),
loader: DOMRefCell::new(doc_loader),
} }
} }
// https://dom.spec.whatwg.org/#dom-document // https://dom.spec.whatwg.org/#dom-document
pub fn Constructor(global: GlobalRef) -> Fallible<Temporary<Document>> { pub fn Constructor(global: GlobalRef) -> Fallible<Temporary<Document>> {
Ok(Document::new(global.as_window(), None, let win = global.as_window();
let doc = win.Document().root();
let doc = doc.r();
let docloader = DocumentLoader::new(&*doc.loader());
Ok(Document::new(win, None,
IsHTMLDocument::NonHTMLDocument, None, IsHTMLDocument::NonHTMLDocument, None,
None, DocumentSource::NotFromParser)) None, DocumentSource::NotFromParser, docloader))
} }
pub fn new(window: JSRef<Window>, pub fn new(window: JSRef<Window>,
@ -958,10 +998,11 @@ impl Document {
doctype: IsHTMLDocument, doctype: IsHTMLDocument,
content_type: Option<DOMString>, content_type: Option<DOMString>,
last_modified: Option<DOMString>, last_modified: Option<DOMString>,
source: DocumentSource) -> Temporary<Document> { source: DocumentSource,
doc_loader: DocumentLoader) -> Temporary<Document> {
let document = reflect_dom_object(box Document::new_inherited(window, url, doctype, let document = reflect_dom_object(box Document::new_inherited(window, url, doctype,
content_type, last_modified, content_type, last_modified,
source), source, doc_loader),
GlobalRef::Window(window), GlobalRef::Window(window),
DocumentBinding::Wrap).root(); DocumentBinding::Wrap).root();

View file

@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use document_loader::DocumentLoader;
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::DOMImplementationBinding; use dom::bindings::codegen::Bindings::DOMImplementationBinding;
use dom::bindings::codegen::Bindings::DOMImplementationBinding::DOMImplementationMethods; use dom::bindings::codegen::Bindings::DOMImplementationBinding::DOMImplementationMethods;
@ -63,11 +64,13 @@ impl<'a> DOMImplementationMethods for JSRef<'a, DOMImplementation> {
fn CreateDocument(self, namespace: Option<DOMString>, qname: DOMString, fn CreateDocument(self, namespace: Option<DOMString>, qname: DOMString,
maybe_doctype: Option<JSRef<DocumentType>>) -> Fallible<Temporary<Document>> { maybe_doctype: Option<JSRef<DocumentType>>) -> Fallible<Temporary<Document>> {
let doc = self.document.root(); let doc = self.document.root();
let win = doc.r().window().root(); let doc = doc.r();
let win = doc.window().root();
let loader = DocumentLoader::new(&*doc.loader());
// Step 1. // Step 1.
let doc = Document::new(win.r(), None, IsHTMLDocument::NonHTMLDocument, let doc = Document::new(win.r(), None, IsHTMLDocument::NonHTMLDocument,
None, None, DocumentSource::NotFromParser).root(); None, None, DocumentSource::NotFromParser, loader).root();
// Step 2-3. // Step 2-3.
let maybe_elem = if qname.is_empty() { let maybe_elem = if qname.is_empty() {
None None
@ -109,11 +112,13 @@ impl<'a> DOMImplementationMethods for JSRef<'a, DOMImplementation> {
// https://dom.spec.whatwg.org/#dom-domimplementation-createhtmldocument // https://dom.spec.whatwg.org/#dom-domimplementation-createhtmldocument
fn CreateHTMLDocument(self, title: Option<DOMString>) -> Temporary<Document> { fn CreateHTMLDocument(self, title: Option<DOMString>) -> Temporary<Document> {
let document = self.document.root(); let document = self.document.root();
let win = document.r().window().root(); let document = document.r();
let win = document.window().root();
let loader = DocumentLoader::new(&*document.loader());
// Step 1-2. // Step 1-2.
let doc = Document::new(win.r(), None, IsHTMLDocument::HTMLDocument, None, None, let doc = Document::new(win.r(), None, IsHTMLDocument::HTMLDocument, None, None,
DocumentSource::NotFromParser).root(); DocumentSource::NotFromParser, loader).root();
let doc_node: JSRef<Node> = NodeCast::from_ref(doc.r()); let doc_node: JSRef<Node> = NodeCast::from_ref(doc.r());
{ {

View file

@ -2,10 +2,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use document_loader::DocumentLoader;
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentReadyState; use dom::bindings::codegen::Bindings::DocumentBinding::DocumentReadyState;
use dom::bindings::codegen::Bindings::DOMParserBinding; use dom::bindings::codegen::Bindings::DOMParserBinding;
use dom::bindings::codegen::Bindings::DOMParserBinding::DOMParserMethods; use dom::bindings::codegen::Bindings::DOMParserBinding::DOMParserMethods;
use dom::bindings::codegen::Bindings::DOMParserBinding::SupportedType::{Text_html, Text_xml}; use dom::bindings::codegen::Bindings::DOMParserBinding::SupportedType::{Text_html, Text_xml};
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::error::Fallible; use dom::bindings::error::Fallible;
use dom::bindings::global::GlobalRef; use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, JSRef, Rootable, Temporary}; use dom::bindings::js::{JS, JSRef, Rootable, Temporary};
@ -51,13 +53,17 @@ impl<'a> DOMParserMethods for JSRef<'a, DOMParser> {
let window = self.window.root(); let window = self.window.root();
let url = window.r().get_url(); let url = window.r().get_url();
let content_type = DOMParserBinding::SupportedTypeValues::strings[ty as usize].to_owned(); let content_type = DOMParserBinding::SupportedTypeValues::strings[ty as usize].to_owned();
let doc = window.r().Document().root();
let doc = doc.r();
let loader = DocumentLoader::new(&*doc.loader());
match ty { match ty {
Text_html => { Text_html => {
let document = Document::new(window.r(), Some(url.clone()), let document = Document::new(window.r(), Some(url.clone()),
IsHTMLDocument::HTMLDocument, IsHTMLDocument::HTMLDocument,
Some(content_type), Some(content_type),
None, None,
DocumentSource::FromParser).root(); DocumentSource::FromParser,
loader).root();
parse_html(document.r(), HTMLInput::InputString(s), &url, None); parse_html(document.r(), HTMLInput::InputString(s), &url, None);
document.r().set_ready_state(DocumentReadyState::Complete); document.r().set_ready_state(DocumentReadyState::Complete);
Ok(Temporary::from_rooted(document.r())) Ok(Temporary::from_rooted(document.r()))
@ -68,7 +74,8 @@ impl<'a> DOMParserMethods for JSRef<'a, DOMParser> {
IsHTMLDocument::NonHTMLDocument, IsHTMLDocument::NonHTMLDocument,
Some(content_type), Some(content_type),
None, None,
DocumentSource::NotFromParser)) DocumentSource::NotFromParser,
loader))
} }
} }
} }

View file

@ -4,6 +4,7 @@
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
use document_loader::LoadType;
use dom::attr::Attr; use dom::attr::Attr;
use dom::attr::AttrHelpers; use dom::attr::AttrHelpers;
use dom::bindings::cell::DOMRefCell; use dom::bindings::cell::DOMRefCell;
@ -34,7 +35,7 @@ use script_task::{ScriptMsg, Runnable};
use encoding::all::UTF_8; use encoding::all::UTF_8;
use encoding::label::encoding_from_whatwg_label; use encoding::label::encoding_from_whatwg_label;
use encoding::types::{Encoding, EncodingRef, DecoderTrap}; use encoding::types::{Encoding, EncodingRef, DecoderTrap};
use net_traits::{load_whole_resource, Metadata}; use net_traits::Metadata;
use util::str::{DOMString, HTML_SPACE_CHARACTERS, StaticStringVec}; use util::str::{DOMString, HTML_SPACE_CHARACTERS, StaticStringVec};
use std::borrow::ToOwned; use std::borrow::ToOwned;
use std::cell::Cell; use std::cell::Cell;
@ -261,7 +262,9 @@ impl<'a> HTMLScriptElementHelpers for JSRef<'a, HTMLScriptElement> {
// state of the element's `crossorigin` content attribute, the origin being // state of the element's `crossorigin` content attribute, the origin being
// the origin of the script element's node document, and the default origin // the origin of the script element's node document, and the default origin
// behaviour set to taint. // behaviour set to taint.
ScriptOrigin::External(load_whole_resource(&window.resource_task(), url)) let doc = document_from_node(self).root();
let contents = doc.r().load_sync(LoadType::Script(url));
ScriptOrigin::External(contents)
} }
} }
}, },

View file

@ -4,6 +4,7 @@
//! The core DOM types. Defines the basic DOM hierarchy as well as all the HTML elements. //! The core DOM types. Defines the basic DOM hierarchy as well as all the HTML elements.
use document_loader::DocumentLoader;
use dom::attr::{Attr, AttrHelpers}; use dom::attr::{Attr, AttrHelpers};
use dom::bindings::cell::DOMRefCell; use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods; use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
@ -1711,9 +1712,10 @@ impl Node {
false => IsHTMLDocument::NonHTMLDocument, false => IsHTMLDocument::NonHTMLDocument,
}; };
let window = document.window().root(); let window = document.window().root();
let loader = DocumentLoader::new(&*document.loader());
let document = Document::new(window.r(), Some(document.url()), let document = Document::new(window.r(), Some(document.url()),
is_html_doc, None, is_html_doc, None,
None, DocumentSource::NotFromParser); None, DocumentSource::NotFromParser, loader);
NodeCast::from_temporary(document) NodeCast::from_temporary(document)
}, },
NodeTypeId::Element(..) => { NodeTypeId::Element(..) => {

View file

@ -54,6 +54,7 @@ extern crate string_cache;
extern crate webdriver_traits; extern crate webdriver_traits;
pub mod cors; pub mod cors;
pub mod document_loader;
#[macro_use] #[macro_use]
pub mod dom; pub mod dom;

View file

@ -71,6 +71,10 @@ impl Page {
} }
} }
pub fn pipeline(&self) -> PipelineId {
self.id
}
pub fn window(&self) -> Temporary<Window> { pub fn window(&self) -> Temporary<Window> {
Temporary::from_rooted(self.frame.borrow().as_ref().unwrap().window.clone()) Temporary::from_rooted(self.frame.borrow().as_ref().unwrap().window.clone())
} }

View file

@ -4,6 +4,7 @@
#![allow(unsafe_code, unrooted_must_root)] #![allow(unsafe_code, unrooted_must_root)]
use document_loader::{DocumentLoader, LoadType};
use dom::attr::AttrHelpers; use dom::attr::AttrHelpers;
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
@ -286,7 +287,7 @@ pub fn parse_html(document: JSRef<Document>,
task_state::enter(IN_HTML_PARSER); task_state::enter(IN_HTML_PARSER);
} }
fn parse_progress(parser: JSRef<ServoHTMLParser>, url: &Url, load_response: &LoadResponse) { fn parse_progress(document: JSRef<Document>, parser: JSRef<ServoHTMLParser>, url: &Url, load_response: &LoadResponse) {
for msg in load_response.progress_port.iter() { for msg in load_response.progress_port.iter() {
match msg { match msg {
ProgressMsg::Payload(data) => { ProgressMsg::Payload(data) => {
@ -299,7 +300,10 @@ pub fn parse_html(document: JSRef<Document>,
// TODO(Savago): we should send a notification to callers #5463. // TODO(Savago): we should send a notification to callers #5463.
break; break;
} }
ProgressMsg::Done(Ok(())) => break, ProgressMsg::Done(Ok(())) => {
document.finish_load(LoadType::PageSource(url.clone()));
break;
}
} }
} }
}; };
@ -313,6 +317,7 @@ pub fn parse_html(document: JSRef<Document>,
Some(ContentType(Mime(TopLevel::Image, _, _))) => { Some(ContentType(Mime(TopLevel::Image, _, _))) => {
let page = format!("<html><body><img src='{}' /></body></html>", url.serialize()); let page = format!("<html><body><img src='{}' /></body></html>", url.serialize());
parser.parse_chunk(page); parser.parse_chunk(page);
document.finish_load(LoadType::PageSource(url.clone()));
}, },
Some(ContentType(Mime(TopLevel::Text, SubLevel::Plain, _))) => { Some(ContentType(Mime(TopLevel::Text, SubLevel::Plain, _))) => {
// FIXME: When servo/html5ever#109 is fixed remove <plaintext> usage and // FIXME: When servo/html5ever#109 is fixed remove <plaintext> usage and
@ -325,10 +330,10 @@ pub fn parse_html(document: JSRef<Document>,
// https://html.spec.whatwg.org/multipage/#read-text // https://html.spec.whatwg.org/multipage/#read-text
let page = format!("<pre>\u{000A}<plaintext>"); let page = format!("<pre>\u{000A}<plaintext>");
parser.parse_chunk(page); parser.parse_chunk(page);
parse_progress(parser, url, &load_response); parse_progress(document, parser, url, &load_response);
}, },
_ => { _ => {
parse_progress(parser, url, &load_response); parse_progress(document, parser, url, &load_response);
} }
} }
} }
@ -349,16 +354,19 @@ pub fn parse_html_fragment(context_node: JSRef<Node>,
output: &mut RootedVec<JS<Node>>) { output: &mut RootedVec<JS<Node>>) {
let window = window_from_node(context_node).root(); let window = window_from_node(context_node).root();
let context_document = document_from_node(context_node).root(); let context_document = document_from_node(context_node).root();
let url = context_document.r().url(); let context_document = context_document.r();
let url = context_document.url();
// Step 1. // Step 1.
let loader = DocumentLoader::new(&*context_document.loader());
let document = Document::new(window.r(), Some(url.clone()), let document = Document::new(window.r(), Some(url.clone()),
IsHTMLDocument::HTMLDocument, IsHTMLDocument::HTMLDocument,
None, None, None, None,
DocumentSource::FromParser).root(); DocumentSource::FromParser,
loader).root();
// Step 2. // Step 2.
document.r().set_quirks_mode(context_document.r().quirks_mode()); document.r().set_quirks_mode(context_document.quirks_mode());
// Step 11. // Step 11.
let form = context_node.inclusive_ancestors() let form = context_node.inclusive_ancestors()

View file

@ -19,6 +19,7 @@
#![allow(unsafe_code)] #![allow(unsafe_code)]
use document_loader::{DocumentLoader, NotifierData};
use dom::bindings::cell::DOMRefCell; use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, DocumentReadyState}; use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, DocumentReadyState};
use dom::bindings::codegen::InheritTypes::{ElementCast, EventTargetCast, HTMLIFrameElementCast, NodeCast, EventCast}; use dom::bindings::codegen::InheritTypes::{ElementCast, EventTargetCast, HTMLIFrameElementCast, NodeCast, EventCast};
@ -65,7 +66,7 @@ use msg::constellation_msg::{ConstellationChan, FocusType};
use msg::constellation_msg::{LoadData, PipelineId, SubpageId, MozBrowserEvent, WorkerId}; use msg::constellation_msg::{LoadData, PipelineId, SubpageId, MozBrowserEvent, WorkerId};
use msg::constellation_msg::{Failure, WindowSizeData, PipelineExitType}; use msg::constellation_msg::{Failure, WindowSizeData, PipelineExitType};
use msg::constellation_msg::Msg as ConstellationMsg; use msg::constellation_msg::Msg as ConstellationMsg;
use net_traits::{ResourceTask, ControlMsg, LoadResponse, LoadConsumer}; use net_traits::{ResourceTask, LoadResponse, LoadConsumer, ControlMsg};
use net_traits::LoadData as NetLoadData; use net_traits::LoadData as NetLoadData;
use net_traits::image_cache_task::{ImageCacheChan, ImageCacheTask, ImageCacheResult}; use net_traits::image_cache_task::{ImageCacheChan, ImageCacheTask, ImageCacheResult};
use net_traits::storage_task::StorageTask; use net_traits::storage_task::StorageTask;
@ -190,6 +191,8 @@ pub enum ScriptMsg {
RefcountCleanup(TrustedReference), RefcountCleanup(TrustedReference),
/// The final network response for a page has arrived. /// The final network response for a page has arrived.
PageFetchComplete(PipelineId, Option<SubpageId>, LoadResponse), PageFetchComplete(PipelineId, Option<SubpageId>, LoadResponse),
/// Notify a document that all pending loads are complete.
DocumentLoadsComplete(PipelineId),
} }
/// A cloneable interface for communicating with an event loop. /// A cloneable interface for communicating with an event loop.
@ -753,6 +756,8 @@ impl ScriptTask {
LiveDOMReferences::cleanup(self.get_cx(), addr), LiveDOMReferences::cleanup(self.get_cx(), addr),
ScriptMsg::PageFetchComplete(id, subpage, response) => ScriptMsg::PageFetchComplete(id, subpage, response) =>
self.handle_page_fetch_complete(id, subpage, response), self.handle_page_fetch_complete(id, subpage, response),
ScriptMsg::DocumentLoadsComplete(id) =>
self.handle_loads_complete(id),
} }
} }
@ -858,6 +863,25 @@ impl ScriptTask {
self.start_page_load(new_load, load_data); self.start_page_load(new_load, load_data);
} }
fn handle_loads_complete(&self, pipeline: PipelineId) {
let page = get_page(&self.root_page(), pipeline);
let doc = page.document().root();
let doc = doc.r();
if doc.loader().is_blocked() {
return;
}
doc.mut_loader().inhibit_events();
// https://html.spec.whatwg.org/multipage/#the-end step 7
let addr: Trusted<Document> = Trusted::new(self.get_cx(), doc, self.chan.clone());
let handler = box DocumentProgressHandler::new(addr.clone(), DocumentProgressTask::Load);
self.chan.send(ScriptMsg::RunnableMsg(handler)).unwrap();
let ConstellationChan(ref chan) = self.constellation_chan;
chan.send(ConstellationMsg::LoadComplete).unwrap();
}
/// Handles a timer that fired. /// Handles a timer that fired.
fn handle_fire_timer_msg(&self, id: PipelineId, timer_id: TimerId) { fn handle_fire_timer_msg(&self, id: PipelineId, timer_id: TimerId) {
let page = self.root_page(); let page = self.root_page();
@ -1142,12 +1166,20 @@ impl ScriptTask {
_ => None _ => None
}; };
let notifier_data = NotifierData {
script_chan: self.chan.clone(),
pipeline: page.pipeline(),
};
let loader = DocumentLoader::new_with_task(self.resource_task.clone(),
Some(notifier_data),
Some(final_url.clone()));
let document = Document::new(window.r(), let document = Document::new(window.r(),
Some(final_url.clone()), Some(final_url.clone()),
IsHTMLDocument::HTMLDocument, IsHTMLDocument::HTMLDocument,
content_type, content_type,
last_modified, last_modified,
DocumentSource::FromParser).root(); DocumentSource::FromParser,
loader).root();
let frame_element = frame_element.r().map(|elem| ElementCast::from_ref(elem)); let frame_element = frame_element.r().map(|elem| ElementCast::from_ref(elem));
window.r().init_browser_context(document.r(), frame_element); window.r().init_browser_context(document.r(), frame_element);
@ -1422,22 +1454,11 @@ impl ScriptTask {
// https://html.spec.whatwg.org/multipage/#the-end step 4 // https://html.spec.whatwg.org/multipage/#the-end step 4
let addr: Trusted<Document> = Trusted::new(self.get_cx(), document.r(), self.chan.clone()); let addr: Trusted<Document> = Trusted::new(self.get_cx(), document.r(), self.chan.clone());
let handler = box DocumentProgressHandler::new(addr.clone(), DocumentProgressTask::DOMContentLoaded); let handler = box DocumentProgressHandler::new(addr, DocumentProgressTask::DOMContentLoaded);
self.chan.send(ScriptMsg::RunnableMsg(handler)).unwrap();
// We have no concept of a document loader right now, so just dispatch the
// "load" event as soon as we've finished executing all scripts parsed during
// the initial load.
// https://html.spec.whatwg.org/multipage/#the-end step 7
let handler = box DocumentProgressHandler::new(addr, DocumentProgressTask::Load);
self.chan.send(ScriptMsg::RunnableMsg(handler)).unwrap(); self.chan.send(ScriptMsg::RunnableMsg(handler)).unwrap();
window.r().set_fragment_name(final_url.fragment.clone()); window.r().set_fragment_name(final_url.fragment.clone());
let ConstellationChan(ref chan) = self.constellation_chan;
chan.send(ConstellationMsg::LoadComplete).unwrap();
// Notify devtools that a new script global exists. // Notify devtools that a new script global exists.
self.notify_devtools(document.r().Title(), final_url, (id, None)); self.notify_devtools(document.r().Title(), final_url, (id, None));
} }