diff --git a/components/compositing/pipeline.rs b/components/compositing/pipeline.rs index 06d85a2ca2a..82250d44b65 100644 --- a/components/compositing/pipeline.rs +++ b/components/compositing/pipeline.rs @@ -153,7 +153,6 @@ impl Pipeline { failure, script_chan.clone(), paint_chan.clone(), - resource_task, image_cache_task, font_cache_task, time_profiler_chan, diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs index 324c765280e..38a2e11c3a6 100644 --- a/components/layout/layout_task.rs +++ b/components/layout/layout_task.rs @@ -45,7 +45,7 @@ use msg::constellation_msg::{ConstellationChan, Failure, PipelineExitType, Pipel use profile_traits::mem::{self, Report, ReportsChan}; use profile_traits::time::{self, ProfilerMetadata, profile}; use profile_traits::time::{TimerMetadataFrameType, TimerMetadataReflowType}; -use net_traits::{load_bytes_iter, ResourceTask}; +use net_traits::{load_bytes_iter, PendingAsyncLoad}; use net_traits::image_cache_task::{ImageCacheTask, ImageCacheResult, ImageCacheChan}; use script::dom::bindings::js::LayoutJS; use script::dom::node::{LayoutData, Node}; @@ -170,8 +170,8 @@ pub struct LayoutTask { /// The name used for the task's memory reporter. pub reporter_name: String, - /// The channel on which messages can be sent to the resource task. - pub resource_task: ResourceTask, + /// The channel on which messages can be sent to the image cache. + pub image_cache_task: ImageCacheTask, /// Public interface to the font cache task. pub font_cache_task: FontCacheTask, @@ -198,7 +198,6 @@ impl LayoutTaskFactory for LayoutTask { failure_msg: Failure, script_chan: ScriptControlChan, paint_chan: PaintChan, - resource_task: ResourceTask, image_cache_task: ImageCacheTask, font_cache_task: FontCacheTask, time_profiler_chan: time::ProfilerChan, @@ -217,7 +216,6 @@ impl LayoutTaskFactory for LayoutTask { constellation_chan, script_chan, paint_chan, - resource_task, image_cache_task, font_cache_task, time_profiler_chan, @@ -270,7 +268,6 @@ impl LayoutTask { constellation_chan: ConstellationChan, script_chan: ScriptControlChan, paint_chan: PaintChan, - resource_task: ResourceTask, image_cache_task: ImageCacheTask, font_cache_task: FontCacheTask, time_profiler_chan: time::ProfilerChan, @@ -311,7 +308,7 @@ impl LayoutTask { time_profiler_chan: time_profiler_chan, mem_profiler_chan: mem_profiler_chan, reporter_name: reporter_name, - resource_task: resource_task, + image_cache_task: image_cache_task.clone(), font_cache_task: font_cache_task, first_reflow: Cell::new(true), image_cache_receiver: image_cache_receiver, @@ -489,8 +486,8 @@ impl LayoutTask { Msg::AddStylesheet(sheet, mq) => { self.handle_add_stylesheet(sheet, mq, possibly_locked_rw_data) } - Msg::LoadStylesheet(url, mq) => { - self.handle_load_stylesheet(url, mq, possibly_locked_rw_data) + Msg::LoadStylesheet(url, mq, pending) => { + self.handle_load_stylesheet(url, mq, pending, possibly_locked_rw_data) } Msg::SetQuirksMode => self.handle_set_quirks_mode(possibly_locked_rw_data), Msg::GetRPC(response_chan) => { @@ -595,13 +592,14 @@ impl LayoutTask { fn handle_load_stylesheet<'a>(&'a self, url: Url, mq: MediaQueryList, + pending: PendingAsyncLoad, possibly_locked_rw_data: &mut Option>) { // TODO: Get the actual value. http://dev.w3.org/csswg/css-syntax/#environment-encoding let environment_encoding = UTF_8 as EncodingRef; // TODO we don't really even need to load this if mq does not match - let (metadata, iter) = load_bytes_iter(&self.resource_task, url); + let (metadata, iter) = load_bytes_iter(pending); let protocol_encoding_label = metadata.charset.as_ref().map(|s| &**s); let final_url = metadata.final_url; @@ -610,6 +608,11 @@ impl LayoutTask { protocol_encoding_label, Some(environment_encoding), Origin::Author); + + //TODO: mark critical subresources as blocking load as well + let ScriptControlChan(ref chan) = self.script_chan; + chan.send(ConstellationControlMsg::StylesheetLoadComplete(self.id, url)).unwrap(); + self.handle_add_stylesheet(sheet, mq, possibly_locked_rw_data); } diff --git a/components/layout_traits/lib.rs b/components/layout_traits/lib.rs index f37cfa194bb..47732f36285 100644 --- a/components/layout_traits/lib.rs +++ b/components/layout_traits/lib.rs @@ -20,7 +20,6 @@ use gfx::paint_task::PaintChan; use msg::constellation_msg::{ConstellationChan, Failure, PipelineId, PipelineExitType}; use profile_traits::mem; use profile_traits::time; -use net_traits::ResourceTask; use net_traits::image_cache_task::ImageCacheTask; use url::Url; use script_traits::{ScriptControlChan, OpaqueScriptLayoutChannel}; @@ -49,7 +48,6 @@ pub trait LayoutTaskFactory { failure_msg: Failure, script_chan: ScriptControlChan, paint_chan: PaintChan, - resource_task: ResourceTask, image_cache_task: ImageCacheTask, font_cache_task: FontCacheTask, time_profiler_chan: time::ProfilerChan, diff --git a/components/net_traits/lib.rs b/components/net_traits/lib.rs index c5f5fb884e8..533e4fc42fb 100644 --- a/components/net_traits/lib.rs +++ b/components/net_traits/lib.rs @@ -125,6 +125,56 @@ pub enum ControlMsg { Exit } +/// Initialized but unsent request. Encapsulates everything necessary to instruct +/// the resource task to make a new request. +pub struct PendingAsyncLoad { + resource_task: ResourceTask, + url: Url, + pipeline: Option, + input_chan: Sender, + input_port: Receiver, + guard: PendingLoadGuard, +} + +struct PendingLoadGuard { + loaded: bool, +} + +impl PendingLoadGuard { + fn neuter(&mut self) { + self.loaded = true; + } +} + +impl Drop for PendingLoadGuard { + fn drop(&mut self) { + assert!(self.loaded) + } +} + +impl PendingAsyncLoad { + pub fn new(resource_task: ResourceTask, url: Url, pipeline: Option) + -> PendingAsyncLoad { + let (tx, rx) = channel(); + PendingAsyncLoad { + resource_task: resource_task, + url: url, + pipeline: pipeline, + input_chan: tx, + input_port: rx, + guard: PendingLoadGuard { loaded: false, }, + } + } + + /// Initiate the network request associated with this pending load. + pub fn load(mut self) -> Receiver { + self.guard.neuter(); + let load_data = LoadData::new(self.url, self.pipeline); + self.resource_task.send(ControlMsg::Load(load_data, LoadConsumer::Channel(self.input_chan))).unwrap(); + self.input_port + } +} + /// Message sent in response to `Load`. Contains metadata, and a port /// for receiving the data. /// @@ -230,10 +280,8 @@ pub fn load_whole_resource(resource_task: &ResourceTask, url: Url) } /// Load a URL asynchronously and iterate over chunks of bytes from the response. -pub fn load_bytes_iter(resource_task: &ResourceTask, url: Url) -> (Metadata, ProgressMsgPortIterator) { - let (input_chan, input_port) = channel(); - resource_task.send(ControlMsg::Load(LoadData::new(url, None), LoadConsumer::Channel(input_chan))).unwrap(); - +pub fn load_bytes_iter(pending: PendingAsyncLoad) -> (Metadata, ProgressMsgPortIterator) { + let input_port = pending.load(); let response = input_port.recv().unwrap(); let iter = ProgressMsgPortIterator { progress_port: response.progress_port }; (response.metadata, iter) diff --git a/components/script/document_loader.rs b/components/script/document_loader.rs index bf759788c57..bd144ed6547 100644 --- a/components/script/document_loader.rs +++ b/components/script/document_loader.rs @@ -7,11 +7,10 @@ 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 net_traits::{LoadResponse, Metadata, load_whole_resource, ResourceTask, PendingAsyncLoad}; use url::Url; -use std::sync::mpsc::{Receiver, channel}; +use std::sync::mpsc::Receiver; #[jstraceable] #[derive(PartialEq, Clone)] @@ -69,15 +68,21 @@ impl DocumentLoader { } } - pub fn load_async(&mut self, load: LoadType) -> Receiver { - let (tx, rx) = channel(); + /// Create a new pending network request, which can be initiated at some point in + /// the future. + pub fn prep_async_load(&mut self, load: LoadType) -> PendingAsyncLoad { 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 + PendingAsyncLoad::new(self.resource_task.clone(), load.url().clone(), pipeline) } + /// Create and initiate a new network request. + pub fn load_async(&mut self, load: LoadType) -> Receiver { + let pending = self.prep_async_load(load); + pending.load() + } + + /// Create, initiate, and await the response for a new network request. pub fn load_sync(&mut self, load: LoadType) -> Result<(Metadata, Vec), String> { self.blocking_loads.push(load.clone()); let result = load_whole_resource(&self.resource_task, load.url().clone()); @@ -85,6 +90,7 @@ impl DocumentLoader { result } + /// 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")); diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 411cd1fefe7..2c11b33b04c 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -72,7 +72,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}; +use net_traits::{Metadata, LoadResponse, PendingAsyncLoad}; use script_task::Runnable; use script_traits::{MouseButton, UntrustedNodeAddress}; use util::opts; @@ -260,6 +260,7 @@ pub trait DocumentHelpers<'a> { fn cancel_animation_frame(self, ident: i32); /// http://w3c.github.io/animation-timing/#dfn-invoke-callbacks-algorithm fn invoke_animation_callbacks(self); + fn prep_async_load(self, load: LoadType) -> PendingAsyncLoad; fn load_async(self, load: LoadType) -> Receiver; fn load_sync(self, load: LoadType) -> Result<(Metadata, Vec), String>; fn finish_load(self, load: LoadType); @@ -884,6 +885,11 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { } } + fn prep_async_load(self, load: LoadType) -> PendingAsyncLoad { + let mut loader = self.loader.borrow_mut(); + loader.prep_async_load(load) + } + fn load_async(self, load: LoadType) -> Receiver { let mut loader = self.loader.borrow_mut(); loader.load_async(load) diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs index 1189b21af46..49e9bf42b1d 100644 --- a/components/script/dom/htmllinkelement.rs +++ b/components/script/dom/htmllinkelement.rs @@ -2,15 +2,17 @@ * 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::LoadType; use dom::attr::{Attr, AttrValue}; use dom::attr::AttrHelpers; use dom::bindings::codegen::Bindings::HTMLLinkElementBinding; use dom::bindings::codegen::Bindings::HTMLLinkElementBinding::HTMLLinkElementMethods; +use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::codegen::InheritTypes::HTMLLinkElementDerived; use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, NodeCast}; use dom::bindings::js::{JS, JSRef, MutNullableHeap, Rootable, Temporary}; use dom::bindings::js::{OptionalRootable, RootedReference}; -use dom::document::Document; +use dom::document::{Document, DocumentHelpers}; use dom::domtokenlist::DOMTokenList; use dom::element::{AttributeHandlers, Element}; use dom::eventtarget::{EventTarget, EventTargetTypeId}; @@ -149,8 +151,10 @@ impl<'a> PrivateHTMLLinkElementHelpers for JSRef<'a, HTMLLinkElement> { let mut css_parser = CssParser::new(&mq_str); let media = parse_media_query_list(&mut css_parser); + let doc = window.Document().root(); + let pending = doc.r().prep_async_load(LoadType::Stylesheet(url.clone())); let LayoutChan(ref layout_chan) = window.layout_chan(); - layout_chan.send(Msg::LoadStylesheet(url, media)).unwrap(); + layout_chan.send(Msg::LoadStylesheet(url, media, pending)).unwrap(); } Err(e) => debug!("Parsing url {} failed: {}", href, e) } diff --git a/components/script/layout_interface.rs b/components/script/layout_interface.rs index 34d21e7e788..22f11462db1 100644 --- a/components/script/layout_interface.rs +++ b/components/script/layout_interface.rs @@ -12,6 +12,7 @@ use geom::point::Point2D; use geom::rect::Rect; use libc::uintptr_t; use msg::constellation_msg::{PipelineExitType, WindowSizeData}; +use net_traits::PendingAsyncLoad; use profile_traits::mem::{Reporter, ReportsChan}; use script_traits::{ScriptControlChan, OpaqueScriptLayoutChannel, UntrustedNodeAddress}; use std::any::Any; @@ -30,7 +31,7 @@ pub enum Msg { AddStylesheet(Stylesheet, MediaQueryList), /// Adds the given stylesheet to the document. - LoadStylesheet(Url, MediaQueryList), + LoadStylesheet(Url, MediaQueryList, PendingAsyncLoad), /// Puts a document into quirks mode, causing the quirks mode stylesheet to be loaded. SetQuirksMode, diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 93b3c794234..3f6ce8c94fb 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -19,7 +19,7 @@ #![allow(unsafe_code)] -use document_loader::{DocumentLoader, NotifierData}; +use document_loader::{LoadType, DocumentLoader, NotifierData}; use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, DocumentReadyState}; use dom::bindings::codegen::InheritTypes::{ElementCast, EventTargetCast, HTMLIFrameElementCast, NodeCast, EventCast}; @@ -731,6 +731,8 @@ impl ScriptTask { self.handle_webdriver_msg(pipeline_id, msg), ConstellationControlMsg::TickAllAnimations(pipeline_id) => self.handle_tick_all_animations(pipeline_id), + ConstellationControlMsg::StylesheetLoadComplete(id, url) => + self.handle_resource_loaded(id, LoadType::Stylesheet(url)), } } @@ -839,6 +841,12 @@ impl ScriptTask { } /// Handle a request to load a page in a new child frame of an existing page. + fn handle_resource_loaded(&self, pipeline: PipelineId, load: LoadType) { + let page = get_page(&self.root_page(), pipeline); + let doc = page.document().root(); + doc.r().finish_load(load); + } + fn handle_new_layout(&self, new_layout_info: NewLayoutInfo) { let NewLayoutInfo { containing_pipeline_id, diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index 62143373f0a..c77ebe97c72 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -29,6 +29,7 @@ use net_traits::storage_task::StorageTask; use std::any::Any; use std::sync::mpsc::{Sender, Receiver}; use webdriver_traits::WebDriverScriptCommand; +use url::Url; use geom::point::Point2D; use geom::rect::Rect; @@ -89,6 +90,8 @@ pub enum ConstellationControlMsg { WebDriverCommand(PipelineId, WebDriverScriptCommand), /// Notifies script task that all animations are done TickAllAnimations(PipelineId), + /// Notifies script that a stylesheet has finished loading. + StylesheetLoadComplete(PipelineId, Url), } /// The mouse button involved in the event.