From 8082df7d0da97f1951ae125956b962b92c98e69f Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Wed, 11 Mar 2015 10:44:59 -0400 Subject: Make external script sources load asynchronously, yet still block further parsing. Hook up document loading to async networking events. --- components/script/script_task.rs | 96 +++++++++++++++++++++++----------------- 1 file changed, 56 insertions(+), 40 deletions(-) (limited to 'components/script/script_task.rs') 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), /// 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, 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, metadata: Metadata) + -> Option> { + 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, - response: LoadResponse) { + metadata: Metadata) -> Option> { 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 { + 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 = response.metadata.headers.as_ref().and_then(|headers| { + let last_modified: Option = 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)) { @@ -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(); -- cgit v1.2.3