New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement document load tracking. #3714
Closed
+522
−139
Closed
Changes from 1 commit
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.
Implement a DocumentLoader type that tracks pending loads and notifie…
…s the script task when the queue is empty. Replace the current DOM load event with DOMContentLoaded and dispatch the real load event based on the DocumentLoader's notification.
- Loading branch information
commit 72932f90aff72d5ec8d087cf652e062dea83d670
| @@ -0,0 +1,96 @@ | ||
| /* 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/syntax.html#the-end | ||
|
|
||
| use script_task::{ScriptMsg, ScriptChan}; | ||
| use servo_msg::constellation_msg::{PipelineId}; | ||
| use servo_net::resource_task::{LoadResponse, Metadata, load_whole_resource, ResourceTask}; | ||
| use servo_net::resource_task::{ControlMsg, LoadData}; | ||
| use url::Url; | ||
|
|
||
| #[jstraceable] | ||
| #[deriving(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, | ||
| script_chan: Box<ScriptChan + Send>, | ||
| blocking_loads: Vec<LoadType>, | ||
| pipeline: PipelineId, | ||
| notify: bool, | ||
| } | ||
|
|
||
| impl DocumentLoader { | ||
| pub fn new(existing: &DocumentLoader) -> DocumentLoader { | ||
| DocumentLoader::new_with_task(existing.resource_task.clone(), | ||
| existing.script_chan.clone(), | ||
| existing.pipeline) | ||
| } | ||
|
|
||
| pub fn new_with_task(resource_task: ResourceTask, script_chan: Box<ScriptChan + Send>, | ||
| pipeline: PipelineId) -> DocumentLoader { | ||
| DocumentLoader { | ||
| resource_task: resource_task, | ||
| script_chan: script_chan, | ||
| pipeline: pipeline, | ||
| blocking_loads: vec!(), | ||
| notify: true, | ||
| } | ||
| } | ||
|
|
||
| pub fn load_async(&mut self, load: LoadType) -> Receiver<LoadResponse> { | ||
| self.load_async_with(load, |_| {}) | ||
| } | ||
|
|
||
| pub fn load_async_with(&mut self, load: LoadType, cb: |load_data: &mut LoadData|) -> Receiver<LoadResponse> { | ||
| let (tx, rx) = channel(); | ||
| self.blocking_loads.push(load.clone()); | ||
| let mut load_data = LoadData::new(load.url().clone(), tx); | ||
| cb(&mut load_data); | ||
| self.resource_task.send(ControlMsg::Load(load_data)); | ||
| 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 !self.is_blocked() && self.notify { | ||
| self.script_chan.send(ScriptMsg::DocumentLoadsComplete(self.pipeline)); | ||
| } | ||
| } | ||
|
|
||
| pub fn is_blocked(&self) -> bool { | ||
| !self.blocking_loads.is_empty() | ||
| } | ||
|
|
||
| pub fn inhibit_events(&mut self) { | ||
| self.notify = false; | ||
| } | ||
| } |
| @@ -2,6 +2,7 @@ | ||
| * License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
|
||
| use document_loader::{DocumentLoader, LoadType}; | ||
| use dom::attr::{Attr, AttrHelpers, AttrValue}; | ||
| use dom::bindings::cell::DOMRefCell; | ||
| use dom::bindings::codegen::Bindings::DocumentBinding; | ||
| @@ -55,6 +56,7 @@ use dom::range::Range; | ||
| use dom::treewalker::TreeWalker; | ||
| use dom::uievent::UIEvent; | ||
| use dom::window::{Window, WindowHelpers}; | ||
| use servo_net::resource_task::{LoadResponse, Metadata, LoadData}; | ||
| use servo_util::namespace; | ||
| use servo_util::str::{DOMString, split_html_space_chars}; | ||
|
|
||
| @@ -66,7 +68,7 @@ use url::Url; | ||
| use std::collections::HashMap; | ||
| use std::collections::hash_map::{Vacant, Occupied}; | ||
| use std::ascii::AsciiExt; | ||
| use std::cell::{Cell, Ref}; | ||
| use std::cell::{Cell, Ref, RefMut}; | ||
| use std::default::Default; | ||
| use time; | ||
|
|
||
| @@ -101,6 +103,8 @@ pub struct Document { | ||
| possibly_focused: MutNullableJS<Element>, | ||
| /// The element that currently has the document focus context. | ||
| focused: MutNullableJS<Element>, | ||
|
|
||
| loader: DOMRefCell<DocumentLoader>, | ||
| } | ||
|
|
||
| impl DocumentDerived for EventTarget { | ||
| @@ -167,6 +171,8 @@ impl CollectionFilter for AppletsFilter { | ||
| } | ||
|
|
||
| pub trait DocumentHelpers<'a> { | ||
| fn loader(&self) -> Ref<DocumentLoader>; | ||
| fn mut_loader(&self) -> RefMut<DocumentLoader>; | ||
| fn window(self) -> Temporary<Window>; | ||
| fn encoding_name(self) -> Ref<'a, DOMString>; | ||
| fn is_html_document(self) -> bool; | ||
| @@ -188,9 +194,23 @@ pub trait DocumentHelpers<'a> { | ||
| fn commit_focus_transaction(self); | ||
| fn send_title_to_compositor(self); | ||
| fn dirty_all_nodes(self); | ||
| fn load_async(self, load: LoadType) -> Receiver<LoadResponse>; | ||
| fn load_async_with(self, load: LoadType, cb: |&mut LoadData|) -> 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> { | ||
| #[inline] | ||
| fn loader(&self) -> Ref<DocumentLoader> { | ||
| self.loader.borrow() | ||
| } | ||
|
|
||
| #[inline] | ||
| fn mut_loader(&self) -> RefMut<DocumentLoader> { | ||
| self.loader.borrow_mut() | ||
| } | ||
|
|
||
| #[inline] | ||
| fn window(self) -> Temporary<Window> { | ||
| Temporary::new(self.window) | ||
| @@ -382,6 +402,22 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { | ||
| node.dirty(NodeDamage::OtherNodeDamage) | ||
| } | ||
| } | ||
|
|
||
| fn load_async(self, load: LoadType) -> Receiver<LoadResponse> { | ||
| self.load_async_with(load, |_| {}) | ||
| } | ||
|
|
||
| fn load_async_with(self, load: LoadType, cb: |load_data: &mut LoadData|) -> Receiver<LoadResponse> { | ||
| self.loader.borrow_mut().load_async_with(load, cb) | ||
| } | ||
|
|
||
| fn load_sync(self, load: LoadType) -> Result<(Metadata, Vec<u8>), String> { | ||
jdm
Author
Member
|
||
| self.loader.borrow_mut().load_sync(load) | ||
| } | ||
|
|
||
| fn finish_load(self, load: LoadType) { | ||
| self.loader.borrow_mut().finish_load(load); | ||
| } | ||
| } | ||
|
|
||
| #[deriving(PartialEq)] | ||
| @@ -407,7 +443,8 @@ impl Document { | ||
| url: Option<Url>, | ||
| is_html_document: IsHTMLDocument, | ||
| content_type: Option<DOMString>, | ||
| source: DocumentSource) -> Document { | ||
| source: DocumentSource, | ||
| doc_loader: DocumentLoader) -> Document { | ||
| let url = url.unwrap_or_else(|| Url::parse("about:blank").unwrap()); | ||
|
|
||
| let ready_state = if source == DocumentSource::FromParser { | ||
| @@ -447,23 +484,29 @@ impl Document { | ||
| ready_state: Cell::new(ready_state), | ||
| possibly_focused: Default::default(), | ||
| focused: Default::default(), | ||
| loader: DOMRefCell::new(doc_loader), | ||
| } | ||
| } | ||
|
|
||
| // http://dom.spec.whatwg.org/#dom-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 docloader = DocumentLoader::new(&*doc.loader()); | ||
| Ok(Document::new(win, None, | ||
| IsHTMLDocument::NonHTMLDocument, None, | ||
| DocumentSource::NotFromParser)) | ||
| DocumentSource::NotFromParser, docloader)) | ||
| } | ||
|
|
||
| pub fn new(window: JSRef<Window>, | ||
| url: Option<Url>, | ||
| doctype: IsHTMLDocument, | ||
| content_type: Option<DOMString>, | ||
| source: DocumentSource) -> Temporary<Document> { | ||
| source: DocumentSource, | ||
| docloader: DocumentLoader) -> Temporary<Document> { | ||
| let document = reflect_dom_object(box Document::new_inherited(window, url, doctype, | ||
| content_type, source), | ||
| content_type, source, | ||
| docloader), | ||
| GlobalRef::Window(window), | ||
| DocumentBinding::Wrap).root(); | ||
|
|
||
Oops, something went wrong.
ProTip!
Use n and p to navigate between commits in a pull request.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
The very existence of machinery to load something synchronously worries me. If this is mean to be sync only from the Rust task point of view and never from the JS point of view, it would be good to have some comment to that effect to make this less scary.