diff options
Diffstat (limited to 'components/script/dom/window.rs')
-rw-r--r-- | components/script/dom/window.rs | 145 |
1 files changed, 132 insertions, 13 deletions
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index b6392fbdd19..fcf6a0019c0 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -6,6 +6,7 @@ use app_units::Au; use bluetooth_traits::BluetoothRequest; use cssparser::Parser; use devtools_traits::{ScriptToDevtoolsControlMsg, TimelineMarker, TimelineMarkerType}; +use document_loader::LoadType; use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, DocumentReadyState}; use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; @@ -42,7 +43,7 @@ use dom::location::Location; use dom::mediaquerylist::{MediaQueryList, WeakMediaQueryListVec}; use dom::messageevent::MessageEvent; use dom::navigator::Navigator; -use dom::node::{Node, from_untrusted_node_address, window_from_node}; +use dom::node::{Node, from_untrusted_node_address, window_from_node, document_from_node, NodeDamage}; use dom::performance::Performance; use dom::promise::Promise; use dom::screen::Screen; @@ -52,20 +53,25 @@ use euclid::{Point2D, Rect, Size2D}; use fetch; use gfx_traits::ScrollRootId; use ipc_channel::ipc::{self, IpcSender}; +use ipc_channel::router::ROUTER; use js::jsapi::{HandleObject, HandleValue, JSAutoCompartment, JSContext}; use js::jsapi::{JS_GC, JS_GetRuntime}; use js::jsval::UndefinedValue; use js::rust::Runtime; use msg::constellation_msg::{FrameType, PipelineId}; -use net_traits::{ResourceThreads, ReferrerPolicy}; -use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread}; +use net_traits::{ResourceThreads, ReferrerPolicy, FetchResponseListener, FetchMetadata}; +use net_traits::NetworkError; +use net_traits::image_cache_thread::{ImageResponder, ImageResponse}; +use net_traits::image_cache_thread::{PendingImageResponse, ImageCacheThread, PendingImageId}; +use net_traits::request::{Type as RequestType, RequestInit as FetchRequestInit}; use net_traits::storage_thread::StorageType; +use network_listener::{NetworkListener, PreInvoke}; use num_traits::ToPrimitive; use open; use profile_traits::mem::ProfilerChan as MemProfilerChan; use profile_traits::time::ProfilerChan as TimeProfilerChan; use rustc_serialize::base64::{FromBase64, STANDARD, ToBase64}; -use script_layout_interface::TrustedNodeAddress; +use script_layout_interface::{TrustedNodeAddress, PendingImageState}; use script_layout_interface::message::{Msg, Reflow, ReflowQueryType, ScriptReflow}; use script_layout_interface::reporter::CSSErrorReporter; use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC}; @@ -73,7 +79,7 @@ use script_layout_interface::rpc::{MarginStyleResponse, NodeScrollRootIdResponse use script_layout_interface::rpc::{ResolvedStyleResponse, TextIndexResponse}; use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, ScriptThreadEventCategory}; use script_thread::{MainThreadScriptChan, MainThreadScriptMsg, Runnable, RunnableWrapper}; -use script_thread::SendableMainThreadScriptChan; +use script_thread::{SendableMainThreadScriptChan, ImageCacheMsg}; use script_traits::{ConstellationControlMsg, LoadData, MozBrowserEvent, UntrustedNodeAddress}; use script_traits::{DocumentState, TimerEvent, TimerEventId}; use script_traits::{ScriptMsg as ConstellationMsg, TimerEventRequest, WindowSizeData, WindowSizeType}; @@ -87,6 +93,7 @@ use std::ascii::AsciiExt; use std::borrow::ToOwned; use std::cell::Cell; use std::collections::{HashMap, HashSet}; +use std::collections::hash_map::Entry; use std::default::Default; use std::io::{Write, stderr, stdout}; use std::mem; @@ -165,7 +172,7 @@ pub struct Window { #[ignore_heap_size_of = "channels are hard"] image_cache_thread: ImageCacheThread, #[ignore_heap_size_of = "channels are hard"] - image_cache_chan: ImageCacheChan, + image_cache_chan: Sender<ImageCacheMsg>, browsing_context: MutNullableJS<BrowsingContext>, document: MutNullableJS<Document>, history: MutNullableJS<History>, @@ -253,7 +260,13 @@ pub struct Window { webvr_thread: Option<IpcSender<WebVRMsg>>, /// A map for storing the previous permission state read results. - permission_state_invocation_results: DOMRefCell<HashMap<String, PermissionState>> + permission_state_invocation_results: DOMRefCell<HashMap<String, PermissionState>>, + + /// All of the elements that have an outstanding image request that was + /// initiated by layout during a reflow. They are stored in the script thread + /// to ensure that the element can be marked dirty when the image data becomes + /// available at some point in the future. + pending_layout_images: DOMRefCell<HashMap<PendingImageId, Vec<JS<Node>>>>, } impl Window { @@ -295,10 +308,6 @@ impl Window { &self.script_chan.0 } - pub fn image_cache_chan(&self) -> ImageCacheChan { - self.image_cache_chan.clone() - } - pub fn parent_info(&self) -> Option<(PipelineId, FrameType)> { self.parent_info } @@ -346,6 +355,28 @@ impl Window { pub fn permission_state_invocation_results(&self) -> &DOMRefCell<HashMap<String, PermissionState>> { &self.permission_state_invocation_results } + + pub fn pending_image_notification(&self, response: PendingImageResponse) { + //XXXjdm could be more efficient to send the responses to the layout thread, + // rather than making the layout thread talk to the image cache to + // obtain the same data. + let mut images = self.pending_layout_images.borrow_mut(); + let nodes = images.entry(response.id); + let nodes = match nodes { + Entry::Occupied(nodes) => nodes, + Entry::Vacant(_) => return, + }; + for node in nodes.get() { + node.dirty(NodeDamage::OtherNodeDamage); + } + match response.response { + ImageResponse::MetadataLoaded(_) => {} + ImageResponse::Loaded(_) | + ImageResponse::PlaceholderLoaded(_) | + ImageResponse::None => { nodes.remove(); } + } + self.add_pending_reflow(); + } } #[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))] @@ -1168,6 +1199,31 @@ impl Window { self.emit_timeline_marker(marker.end()); } + let pending_images = self.layout_rpc.pending_images(); + for image in pending_images { + let id = image.id; + let js_runtime = self.js_runtime.borrow(); + let js_runtime = js_runtime.as_ref().unwrap(); + let node = from_untrusted_node_address(js_runtime.rt(), image.node); + + if let PendingImageState::Unrequested(ref url) = image.state { + fetch_image_for_layout(url.clone(), &*node, id); + } + + let mut images = self.pending_layout_images.borrow_mut(); + let nodes = images.entry(id).or_insert(vec![]); + if nodes.iter().find(|n| &***n as *const _ == &*node as *const _).is_none() { + let (responder, responder_listener) = ipc::channel().unwrap(); + let pipeline = self.upcast::<GlobalScope>().pipeline_id(); + let image_cache_chan = self.image_cache_chan.clone(); + ROUTER.add_route(responder_listener.to_opaque(), box move |message| { + let _ = image_cache_chan.send((pipeline, message.to().unwrap())); + }); + self.image_cache_thread.add_listener(id, ImageResponder::new(responder, id)); + nodes.push(JS::from_ref(&*node)); + } + } + true } @@ -1227,7 +1283,8 @@ impl Window { let ready_state = document.ReadyState(); - if ready_state == DocumentReadyState::Complete && !reftest_wait { + let pending_images = self.pending_layout_images.borrow().is_empty(); + if ready_state == DocumentReadyState::Complete && !reftest_wait && pending_images { let global_scope = self.upcast::<GlobalScope>(); let event = ConstellationMsg::SetDocumentState(global_scope.pipeline_id(), DocumentState::Idle); global_scope.constellation_chan().send(event).unwrap(); @@ -1635,7 +1692,7 @@ impl Window { network_task_source: NetworkingTaskSource, history_task_source: HistoryTraversalTaskSource, file_task_source: FileReadingTaskSource, - image_cache_chan: ImageCacheChan, + image_cache_chan: Sender<ImageCacheMsg>, image_cache_thread: ImageCacheThread, resource_threads: ResourceThreads, bluetooth_thread: IpcSender<BluetoothRequest>, @@ -1717,6 +1774,7 @@ impl Window { test_runner: Default::default(), webvr_thread: webvr_thread, permission_state_invocation_results: DOMRefCell::new(HashMap::new()), + pending_layout_images: DOMRefCell::new(HashMap::new()), }; unsafe { @@ -1836,3 +1894,64 @@ impl Runnable for PostMessageHandler { message.handle()); } } + +struct LayoutImageContext { + node: Trusted<Node>, + data: Vec<u8>, + id: PendingImageId, + url: ServoUrl, +} + +impl FetchResponseListener for LayoutImageContext { + fn process_request_body(&mut self) {} + fn process_request_eof(&mut self) {} + fn process_response(&mut self, _metadata: Result<FetchMetadata, NetworkError>) {/*XXXjdm*/} + + fn process_response_chunk(&mut self, mut payload: Vec<u8>) { + self.data.append(&mut payload); + } + + fn process_response_eof(&mut self, _response: Result<(), NetworkError>) { + let node = self.node.root(); + let document = document_from_node(&*node); + let window = document.window(); + let image_cache = window.image_cache_thread(); + image_cache.store_complete_image_bytes(self.id, self.data.clone()); + document.finish_load(LoadType::Image(self.url.clone())); + } +} + +impl PreInvoke for LayoutImageContext {} + +fn fetch_image_for_layout(url: ServoUrl, node: &Node, id: PendingImageId) { + let context = Arc::new(Mutex::new(LayoutImageContext { + node: Trusted::new(node), + data: vec![], + id: id, + url: url.clone(), + })); + + let document = document_from_node(node); + let window = window_from_node(node); + + let (action_sender, action_receiver) = ipc::channel().unwrap(); + let listener = NetworkListener { + context: context, + task_source: window.networking_task_source(), + wrapper: Some(window.get_runnable_wrapper()), + }; + ROUTER.add_route(action_receiver.to_opaque(), box move |message| { + listener.notify_fetch(message.to().unwrap()); + }); + + let request = FetchRequestInit { + url: url.clone(), + origin: document.url().clone(), + type_: RequestType::Image, + pipeline_id: Some(document.global().pipeline_id()), + .. FetchRequestInit::default() + }; + + //XXXjdm should not block load event + document.fetch_async(LoadType::Image(url), request, action_sender); +} |