aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/window.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom/window.rs')
-rw-r--r--components/script/dom/window.rs145
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);
+}