aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJosh Matthews <josh@joshmatthews.net>2016-11-21 18:13:40 -0500
committerJosh Matthews <josh@joshmatthews.net>2017-02-22 14:19:35 -0500
commitc890c9143cc1ae82e1116b8fb0c4e9eb478b8a03 (patch)
tree131dcf8dd5a2880562101497d958c02a31903183
parent78e8c31a4d1890260dda83f2db85672f693c1e97 (diff)
downloadservo-c890c9143cc1ae82e1116b8fb0c4e9eb478b8a03.tar.gz
servo-c890c9143cc1ae82e1116b8fb0c4e9eb478b8a03.zip
Make script thread initiate requests for images needed by layout.
In support of this goal, the layout thread collects information about CSS images that are missing image data and hands it off to the script thread after layout completes. The script thread stores a list of nodes that will need to be reflowed after the associated network request is complete. The script thread ensures that the nodes are not GCed while a request is ongoing, which the layout thread is incapable of guaranteeing. The image cache's API has also been redesigned in support of this work. No network requests are made by the new image cache, since it does not possess the document-specific information necessary to initiate them. Instead, there is now a single, synchronous query operation that optionally reserves a slot when a cache entry for a URL cannot be found. This reserved slot is then the responsibility of the queryer to populate with the contents of the network response for the URL once it is complete. Any subsequent queries for the same URL will be informed that the response is pending until that occurs. The changes to layout also remove the synchronous image loading code path, which means that reftests now test the same code that non-test binaries execute. The decision to take a screenshot now considers whether there are any outstanding image requests for layout in order to avoid intermittent failures in reftests that use CSS images.
-rw-r--r--components/layout/construct.rs3
-rw-r--r--components/layout/context.rs99
-rw-r--r--components/layout/display_list_builder.rs3
-rw-r--r--components/layout/fragment.rs9
-rw-r--r--components/layout/query.rs11
-rw-r--r--components/layout_thread/lib.rs70
-rw-r--r--components/net/image_cache_thread.rs214
-rw-r--r--components/net_traits/image_cache_thread.rs103
-rw-r--r--components/script/dom/bindings/trace.rs4
-rw-r--r--components/script/dom/canvasrenderingcontext2d.rs4
-rw-r--r--components/script/dom/htmlcanvaselement.rs11
-rw-r--r--components/script/dom/htmlimageelement.rs188
-rw-r--r--components/script/dom/window.rs145
-rw-r--r--components/script/script_thread.rs26
-rw-r--r--components/script_layout_interface/lib.rs18
-rw-r--r--components/script_layout_interface/rpc.rs4
-rw-r--r--components/script_plugins/unrooted_must_root.rs1
-rw-r--r--components/servo/lib.rs3
18 files changed, 528 insertions, 388 deletions
diff --git a/components/layout/construct.rs b/components/layout/construct.rs
index bbfe17d22b7..afdf903c500 100644
--- a/components/layout/construct.rs
+++ b/components/layout/construct.rs
@@ -351,11 +351,13 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
}
Some(LayoutNodeType::Element(LayoutElementType::HTMLImageElement)) => {
let image_info = box ImageFragmentInfo::new(node.image_url(),
+ node,
&self.layout_context);
SpecificFragmentInfo::Image(image_info)
}
Some(LayoutNodeType::Element(LayoutElementType::HTMLObjectElement)) => {
let image_info = box ImageFragmentInfo::new(node.object_data(),
+ node,
&self.layout_context);
SpecificFragmentInfo::Image(image_info)
}
@@ -1219,6 +1221,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
let marker_fragments = match node.style(self.style_context()).get_list().list_style_image {
Either::First(ref url_value) => {
let image_info = box ImageFragmentInfo::new(url_value.url().map(|u| u.clone()),
+ node,
&self.layout_context);
vec![Fragment::new(node, SpecificFragmentInfo::Image(image_info), self.layout_context)]
}
diff --git a/components/layout/context.rs b/components/layout/context.rs
index e67e7da6fb1..13029dd80bf 100644
--- a/components/layout/context.rs
+++ b/components/layout/context.rs
@@ -5,22 +5,22 @@
//! Data needed by the layout thread.
use fnv::FnvHasher;
-use gfx::display_list::WebRenderImageInfo;
+use gfx::display_list::{WebRenderImageInfo, OpaqueNode};
use gfx::font_cache_thread::FontCacheThread;
use gfx::font_context::FontContext;
use heapsize::HeapSizeOf;
-use ipc_channel::ipc;
-use net_traits::image::base::Image;
-use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread, ImageResponse, ImageState};
+use net_traits::image_cache_thread::{ImageCacheThread, ImageState};
use net_traits::image_cache_thread::{ImageOrMetadataAvailable, UsePlaceholder};
+use opaque_node::OpaqueNodeMethods;
use parking_lot::RwLock;
-use servo_config::opts;
+use script_layout_interface::{PendingImage, PendingImageState};
use servo_url::ServoUrl;
use std::borrow::{Borrow, BorrowMut};
use std::cell::{RefCell, RefMut};
use std::collections::HashMap;
use std::hash::BuildHasherDefault;
use std::sync::{Arc, Mutex};
+use std::thread;
use style::context::{SharedStyleContext, ThreadLocalStyleContext};
use style::dom::TElement;
@@ -82,9 +82,6 @@ pub struct LayoutContext {
/// The shared image cache thread.
pub image_cache_thread: Mutex<ImageCacheThread>,
- /// A channel for the image cache to send responses to.
- pub image_cache_sender: Mutex<ImageCacheChan>,
-
/// Interface to the font cache thread.
pub font_cache_thread: Mutex<FontCacheThread>,
@@ -92,6 +89,18 @@ pub struct LayoutContext {
pub webrender_image_cache: Arc<RwLock<HashMap<(ServoUrl, UsePlaceholder),
WebRenderImageInfo,
BuildHasherDefault<FnvHasher>>>>,
+
+ /// A list of in-progress image loads to be shared with the script thread.
+ /// A None value means that this layout was not initiated by the script thread.
+ pub pending_images: Mutex<Vec<PendingImage>>
+}
+
+impl Drop for LayoutContext {
+ fn drop(&mut self) {
+ if !thread::panicking() {
+ assert!(self.pending_images.lock().unwrap().is_empty());
+ }
+ }
}
impl LayoutContext {
@@ -100,49 +109,11 @@ impl LayoutContext {
&self.style_context
}
- fn get_or_request_image_synchronously(&self, url: ServoUrl, use_placeholder: UsePlaceholder)
- -> Option<Arc<Image>> {
- debug_assert!(opts::get().output_file.is_some() || opts::get().exit_after_load);
-
- // See if the image is already available
- let result = self.image_cache_thread.lock().unwrap()
- .find_image(url.clone(), use_placeholder);
-
- match result {
- Ok(image) => return Some(image),
- Err(ImageState::LoadError) => {
- // Image failed to load, so just return nothing
- return None
- }
- Err(_) => {}
- }
-
- // If we are emitting an output file, then we need to block on
- // image load or we risk emitting an output file missing the image.
- let (sync_tx, sync_rx) = ipc::channel().unwrap();
- self.image_cache_thread.lock().unwrap().request_image(url, ImageCacheChan(sync_tx), None);
- loop {
- match sync_rx.recv() {
- Err(_) => return None,
- Ok(response) => {
- match response.image_response {
- ImageResponse::Loaded(image) | ImageResponse::PlaceholderLoaded(image) => {
- return Some(image)
- }
- ImageResponse::None | ImageResponse::MetadataLoaded(_) => {}
- }
- }
- }
- }
- }
-
- pub fn get_or_request_image_or_meta(&self, url: ServoUrl, use_placeholder: UsePlaceholder)
- -> Option<ImageOrMetadataAvailable> {
- // If we are emitting an output file, load the image synchronously.
- if opts::get().output_file.is_some() || opts::get().exit_after_load {
- return self.get_or_request_image_synchronously(url, use_placeholder)
- .map(|img| ImageOrMetadataAvailable::ImageAvailable(img));
- }
+ pub fn get_or_request_image_or_meta(&self,
+ node: OpaqueNode,
+ url: ServoUrl,
+ use_placeholder: UsePlaceholder)
+ -> Option<ImageOrMetadataAvailable> {
// See if the image is already available
let result = self.image_cache_thread.lock().unwrap()
.find_image_or_metadata(url.clone(),
@@ -151,20 +122,32 @@ impl LayoutContext {
Ok(image_or_metadata) => Some(image_or_metadata),
// Image failed to load, so just return nothing
Err(ImageState::LoadError) => None,
- // Not yet requested, async mode - request image or metadata from the cache
- Err(ImageState::NotRequested) => {
- let sender = self.image_cache_sender.lock().unwrap().clone();
- self.image_cache_thread.lock().unwrap()
- .request_image_and_metadata(url, sender, None);
+ // Not yet requested - request image or metadata from the cache
+ Err(ImageState::NotRequested(id)) => {
+ let image = PendingImage {
+ state: PendingImageState::Unrequested(url),
+ node: node.to_untrusted_node_address(),
+ id: id,
+ };
+ self.pending_images.lock().unwrap().push(image);
None
}
// Image has been requested, is still pending. Return no image for this paint loop.
// When the image loads it will trigger a reflow and/or repaint.
- Err(ImageState::Pending) => None,
+ Err(ImageState::Pending(id)) => {
+ let image = PendingImage {
+ state: PendingImageState::PendingResponse,
+ node: node.to_untrusted_node_address(),
+ id: id,
+ };
+ self.pending_images.lock().unwrap().push(image);
+ None
+ }
}
}
pub fn get_webrender_image_for_url(&self,
+ node: OpaqueNode,
url: ServoUrl,
use_placeholder: UsePlaceholder)
-> Option<WebRenderImageInfo> {
@@ -174,7 +157,7 @@ impl LayoutContext {
return Some((*existing_webrender_image).clone())
}
- match self.get_or_request_image_or_meta(url.clone(), use_placeholder) {
+ match self.get_or_request_image_or_meta(node, url.clone(), use_placeholder) {
Some(ImageOrMetadataAvailable::ImageAvailable(image)) => {
let image_info = WebRenderImageInfo::from_image(&*image);
if image_info.key.is_none() {
diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs
index 848188d14a0..5eb4534a0c5 100644
--- a/components/layout/display_list_builder.rs
+++ b/components/layout/display_list_builder.rs
@@ -683,7 +683,8 @@ impl FragmentDisplayListBuilding for Fragment {
index: usize) {
let background = style.get_background();
let webrender_image = state.layout_context
- .get_webrender_image_for_url(image_url.clone(),
+ .get_webrender_image_for_url(self.node,
+ image_url.clone(),
UsePlaceholder::No);
if let Some(webrender_image) = webrender_image {
diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs
index ec1e87e6dd2..9cc0efbce6e 100644
--- a/components/layout/fragment.rs
+++ b/components/layout/fragment.rs
@@ -367,11 +367,14 @@ impl ImageFragmentInfo {
///
/// FIXME(pcwalton): The fact that image fragments store the cache in the fragment makes little
/// sense to me.
- pub fn new(url: Option<ServoUrl>,
- layout_context: &LayoutContext)
+ pub fn new<N: ThreadSafeLayoutNode>(url: Option<ServoUrl>,
+ node: &N,
+ layout_context: &LayoutContext)
-> ImageFragmentInfo {
let image_or_metadata = url.and_then(|url| {
- layout_context.get_or_request_image_or_meta(url, UsePlaceholder::Yes)
+ layout_context.get_or_request_image_or_meta(node.opaque(),
+ url,
+ UsePlaceholder::Yes)
});
let (image, metadata) = match image_or_metadata {
diff --git a/components/layout/query.rs b/components/layout/query.rs
index 5c78a1bc75d..2f26fede59f 100644
--- a/components/layout/query.rs
+++ b/components/layout/query.rs
@@ -17,6 +17,7 @@ use gfx_traits::ScrollRootId;
use inline::LAST_FRAGMENT_OF_ELEMENT;
use ipc_channel::ipc::IpcSender;
use opaque_node::OpaqueNodeMethods;
+use script_layout_interface::PendingImage;
use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse};
use script_layout_interface::rpc::{HitTestResponse, LayoutRPC};
use script_layout_interface::rpc::{MarginStyleResponse, NodeGeometryResponse};
@@ -27,6 +28,7 @@ use script_traits::LayoutMsg as ConstellationMsg;
use script_traits::UntrustedNodeAddress;
use sequential;
use std::cmp::{min, max};
+use std::mem;
use std::ops::Deref;
use std::sync::{Arc, Mutex};
use style::computed_values;
@@ -89,6 +91,9 @@ pub struct LayoutThreadData {
/// Index in a text fragment. We need this do determine the insertion point.
pub text_index_response: TextIndexResponse,
+
+ /// A list of images requests that need to be initiated.
+ pub pending_images: Vec<PendingImage>,
}
pub struct LayoutRPCImpl(pub Arc<Mutex<LayoutThreadData>>);
@@ -216,6 +221,12 @@ impl LayoutRPC for LayoutRPCImpl {
let rw_data = rw_data.lock().unwrap();
rw_data.text_index_response.clone()
}
+
+ fn pending_images(&self) -> Vec<PendingImage> {
+ let &LayoutRPCImpl(ref rw_data) = self;
+ let mut rw_data = rw_data.lock().unwrap();
+ mem::replace(&mut rw_data.pending_images, vec![])
+ }
}
struct UnioningFragmentBorderBoxIterator {
diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs
index 8626385a026..af56555e399 100644
--- a/components/layout_thread/lib.rs
+++ b/components/layout_thread/lib.rs
@@ -75,7 +75,7 @@ use layout::wrapper::LayoutNodeLayoutData;
use layout::wrapper::drop_style_and_layout_data;
use layout_traits::LayoutThreadFactory;
use msg::constellation_msg::{FrameId, PipelineId};
-use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheResult, ImageCacheThread};
+use net_traits::image_cache_thread::ImageCacheThread;
use net_traits::image_cache_thread::UsePlaceholder;
use parking_lot::RwLock;
use profile_traits::mem::{self, Report, ReportKind, ReportsChan};
@@ -98,6 +98,7 @@ use servo_url::ServoUrl;
use std::borrow::ToOwned;
use std::collections::HashMap;
use std::hash::BuildHasherDefault;
+use std::mem as std_mem;
use std::ops::{Deref, DerefMut};
use std::process;
use std::sync::{Arc, Mutex, MutexGuard};
@@ -137,12 +138,6 @@ pub struct LayoutThread {
/// The port on which we receive messages from the constellation.
pipeline_port: Receiver<LayoutControlMsg>,
- /// The port on which we receive messages from the image cache
- image_cache_receiver: Receiver<ImageCacheResult>,
-
- /// The channel on which the image cache can send messages to ourself.
- image_cache_sender: ImageCacheChan,
-
/// The port on which we receive messages from the font cache thread.
font_cache_receiver: Receiver<()>,
@@ -404,11 +399,6 @@ impl LayoutThread {
// Proxy IPC messages from the pipeline to the layout thread.
let pipeline_receiver = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(pipeline_port);
- // Ask the router to proxy IPC messages from the image cache thread to the layout thread.
- let (ipc_image_cache_sender, ipc_image_cache_receiver) = ipc::channel().unwrap();
- let image_cache_receiver =
- ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_image_cache_receiver);
-
// Ask the router to proxy IPC messages from the font cache thread to the layout thread.
let (ipc_font_cache_sender, ipc_font_cache_receiver) = ipc::channel().unwrap();
let font_cache_receiver =
@@ -437,8 +427,6 @@ impl LayoutThread {
image_cache_thread: image_cache_thread,
font_cache_thread: font_cache_thread,
first_reflow: true,
- image_cache_receiver: image_cache_receiver,
- image_cache_sender: ImageCacheChan(ipc_image_cache_sender),
font_cache_receiver: font_cache_receiver,
font_cache_sender: ipc_font_cache_sender,
parallel_traversal: parallel_traversal,
@@ -470,6 +458,7 @@ impl LayoutThread {
margin_style_response: MarginStyleResponse::empty(),
stacking_context_scroll_offsets: HashMap::new(),
text_index_response: TextIndexResponse(None),
+ pending_images: vec![],
})),
error_reporter: CSSErrorReporter {
pipelineid: id,
@@ -530,9 +519,9 @@ impl LayoutThread {
default_computed_values: Arc::new(ComputedValues::initial_values().clone()),
},
image_cache_thread: Mutex::new(self.image_cache_thread.clone()),
- image_cache_sender: Mutex::new(self.image_cache_sender.clone()),
font_cache_thread: Mutex::new(self.font_cache_thread.clone()),
webrender_image_cache: self.webrender_image_cache.clone(),
+ pending_images: Mutex::new(vec![]),
}
}
@@ -541,14 +530,12 @@ impl LayoutThread {
enum Request {
FromPipeline(LayoutControlMsg),
FromScript(Msg),
- FromImageCache,
FromFontCache,
}
let request = {
let port_from_script = &self.port;
let port_from_pipeline = &self.pipeline_port;
- let port_from_image_cache = &self.image_cache_receiver;
let port_from_font_cache = &self.font_cache_receiver;
select! {
msg = port_from_pipeline.recv() => {
@@ -557,10 +544,6 @@ impl LayoutThread {
msg = port_from_script.recv() => {
Request::FromScript(msg.unwrap())
},
- msg = port_from_image_cache.recv() => {
- msg.unwrap();
- Request::FromImageCache
- },
msg = port_from_font_cache.recv() => {
msg.unwrap();
Request::FromFontCache
@@ -590,9 +573,6 @@ impl LayoutThread {
Request::FromScript(msg) => {
self.handle_request_helper(msg, possibly_locked_rw_data)
},
- Request::FromImageCache => {
- self.repaint(possibly_locked_rw_data)
- },
Request::FromFontCache => {
let _rw_data = possibly_locked_rw_data.lock();
self.outstanding_web_fonts.fetch_sub(1, Ordering::SeqCst);
@@ -603,37 +583,6 @@ impl LayoutThread {
}
}
- /// Repaint the scene, without performing style matching. This is typically
- /// used when an image arrives asynchronously and triggers a relayout and
- /// repaint.
- /// TODO: In the future we could detect if the image size hasn't changed
- /// since last time and avoid performing a complete layout pass.
- fn repaint<'a, 'b>(&mut self, possibly_locked_rw_data: &mut RwData<'a, 'b>) -> bool {
- let mut rw_data = possibly_locked_rw_data.lock();
-
- if let Some(mut root_flow) = self.root_flow.clone() {
- let flow = flow::mut_base(FlowRef::deref_mut(&mut root_flow));
- flow.restyle_damage.insert(REPAINT);
- }
-
- let reflow_info = Reflow {
- goal: ReflowGoal::ForDisplay,
- page_clip_rect: max_rect(),
- };
- let mut layout_context = self.build_layout_context(&*rw_data,
- false,
- reflow_info.goal);
-
- self.perform_post_style_recalc_layout_passes(&reflow_info,
- None,
- None,
- &mut *rw_data,
- &mut layout_context);
-
-
- true
- }
-
/// Receives and dispatches messages from other threads.
fn handle_request_helper<'a, 'b>(&mut self,
request: Msg,
@@ -1247,6 +1196,9 @@ impl LayoutThread {
query_type: &ReflowQueryType,
rw_data: &mut LayoutThreadData,
context: &mut LayoutContext) {
+ rw_data.pending_images =
+ std_mem::replace(&mut context.pending_images.lock().unwrap(), vec![]);
+
let mut root_flow = match self.root_flow.clone() {
Some(root_flow) => root_flow,
None => return,
@@ -1387,6 +1339,14 @@ impl LayoutThread {
None,
&mut *rw_data,
&mut layout_context);
+
+ let mut pending_images = layout_context.pending_images.lock().unwrap();
+ if pending_images.len() > 0 {
+ //XXXjdm we drop all the images on the floor, but there's no guarantee that
+ // the node references are valid since the script thread isn't paused.
+ // need to figure out what to do here!
+ pending_images.truncate(0);
+ }
}
fn reflow_with_newly_loaded_web_font<'a, 'b>(&mut self, possibly_locked_rw_data: &mut RwData<'a, 'b>) {
diff --git a/components/net/image_cache_thread.rs b/components/net/image_cache_thread.rs
index 51d8324bd48..17a852833c2 100644
--- a/components/net/image_cache_thread.rs
+++ b/components/net/image_cache_thread.rs
@@ -5,12 +5,11 @@
use immeta::load_from_buf;
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
use ipc_channel::router::ROUTER;
-use net_traits::{CoreResourceThread, NetworkError, fetch_async, FetchResponseMsg};
+use net_traits::{NetworkError, FetchResponseMsg};
use net_traits::image::base::{Image, ImageMetadata, PixelFormat, load_from_memory};
-use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheCommand, ImageCacheThread, ImageState};
-use net_traits::image_cache_thread::{ImageCacheResult, ImageOrMetadataAvailable, ImageResponse, UsePlaceholder};
-use net_traits::image_cache_thread::ImageResponder;
-use net_traits::request::{Destination, RequestInit, Type as RequestType};
+use net_traits::image_cache_thread::{ImageCacheCommand, ImageCacheThread, ImageState};
+use net_traits::image_cache_thread::{ImageOrMetadataAvailable, ImageResponse, UsePlaceholder};
+use net_traits::image_cache_thread::{ImageResponder, PendingImageId};
use servo_config::resource_files::resources_dir_path;
use servo_url::ServoUrl;
use std::borrow::ToOwned;
@@ -65,7 +64,7 @@ struct PendingLoad {
// Once loading is complete, the result of the operation.
result: Option<Result<(), NetworkError>>,
- listeners: Vec<ImageListener>,
+ listeners: Vec<ImageResponder>,
// The url being loaded. Do not forget that this may be several Mb
// if we are loading a data: url.
@@ -89,7 +88,7 @@ impl PendingLoad {
}
}
- fn add_listener(&mut self, listener: ImageListener) {
+ fn add_listener(&mut self, listener: ImageResponder) {
self.listeners.push(listener);
}
}
@@ -131,20 +130,11 @@ impl AllPendingLoads {
self.loads.is_empty()
}
- // get a PendingLoad from its LoadKey. Prefer this to `get_by_url`,
- // for performance reasons.
+ // get a PendingLoad from its LoadKey.
fn get_by_key_mut(&mut self, key: &LoadKey) -> Option<&mut PendingLoad> {
self.loads.get_mut(key)
}
- // get a PendingLoad from its url. When possible, prefer `get_by_key_mut`.
- fn get_by_url(&self, url: &ServoUrl) -> Option<&PendingLoad> {
- self.url_to_load_key.get(url).
- and_then(|load_key|
- self.loads.get(load_key)
- )
- }
-
fn remove(&mut self, key: &LoadKey) -> Option<PendingLoad> {
self.loads.remove(key).
and_then(|pending_load| {
@@ -182,27 +172,20 @@ impl AllPendingLoads {
/// fetched again.
struct CompletedLoad {
image_response: ImageResponse,
+ id: PendingImageId,
}
impl CompletedLoad {
- fn new(image_response: ImageResponse) -> CompletedLoad {
+ fn new(image_response: ImageResponse, id: PendingImageId) -> CompletedLoad {
CompletedLoad {
image_response: image_response,
+ id: id,
}
}
}
-/// Stores information to notify a client when the state
-/// of an image changes.
-struct ImageListener {
- sender: ImageCacheChan,
- responder: Option<ImageResponder>,
- send_metadata_msg: bool,
-}
-
// A key used to communicate during loading.
-#[derive(Eq, Hash, PartialEq, Clone, Copy)]
-struct LoadKey(u64);
+type LoadKey = PendingImageId;
struct LoadKeyGenerator {
counter: u64
@@ -214,34 +197,9 @@ impl LoadKeyGenerator {
counter: 0
}
}
- fn next(&mut self) -> LoadKey {
+ fn next(&mut self) -> PendingImageId {
self.counter += 1;
- LoadKey(self.counter)
- }
-}
-
-impl ImageListener {
- fn new(sender: ImageCacheChan, responder: Option<ImageResponder>, send_metadata_msg: bool) -> ImageListener {
- ImageListener {
- sender: sender,
- responder: responder,
- send_metadata_msg: send_metadata_msg,
- }
- }
-
- fn notify(&self, image_response: ImageResponse) {
- if !self.send_metadata_msg {
- if let ImageResponse::MetadataLoaded(_) = image_response {
- return;
- }
- }
-
- let ImageCacheChan(ref sender) = self.sender;
- let msg = ImageCacheResult {
- responder: self.responder.clone(),
- image_response: image_response,
- };
- sender.send(msg).ok();
+ PendingImageId(self.counter)
}
}
@@ -259,9 +217,6 @@ struct ImageCache {
// Worker threads for decoding images.
thread_pool: ThreadPool,
- // Resource thread handle
- core_resource_thread: CoreResourceThread,
-
// Images that are loading over network, or decoding.
pending_loads: AllPendingLoads,
@@ -347,8 +302,7 @@ fn get_placeholder_image(webrender_api: &webrender_traits::RenderApi) -> io::Res
}
impl ImageCache {
- fn run(core_resource_thread: CoreResourceThread,
- webrender_api: webrender_traits::RenderApi,
+ fn run(webrender_api: webrender_traits::RenderApi,
ipc_command_receiver: IpcReceiver<ImageCacheCommand>) {
// Preload the placeholder image, used when images fail to load.
let placeholder_image = get_placeholder_image(&webrender_api).ok();
@@ -364,7 +318,6 @@ impl ImageCache {
thread_pool: ThreadPool::new(4),
pending_loads: AllPendingLoads::new(),
completed_loads: HashMap::new(),
- core_resource_thread: core_resource_thread,
placeholder_image: placeholder_image,
webrender_api: webrender_api,
};
@@ -406,22 +359,15 @@ impl ImageCache {
ImageCacheCommand::Exit(sender) => {
return Some(sender);
}
- ImageCacheCommand::RequestImage(url, result_chan, responder) => {
- self.request_image(url, result_chan, responder, false);
- }
- ImageCacheCommand::RequestImageAndMetadata(url, result_chan, responder) => {
- self.request_image(url, result_chan, responder, true);
- }
- ImageCacheCommand::GetImageIfAvailable(url, use_placeholder, consumer) => {
- let result = self.get_image_if_available(url, use_placeholder);
- let _ = consumer.send(result);
+ ImageCacheCommand::AddListener(id, responder) => {
+ self.add_listener(id, responder);
}
ImageCacheCommand::GetImageOrMetadataIfAvailable(url, use_placeholder, consumer) => {
let result = self.get_image_or_meta_if_available(url, use_placeholder);
let _ = consumer.send(result);
}
- ImageCacheCommand::StoreDecodeImage(url, image_vector) => {
- self.store_decode_image(url, image_vector);
+ ImageCacheCommand::StoreDecodeImage(id, image_vector) => {
+ self.store_decode_image(id, image_vector);
}
};
@@ -445,7 +391,7 @@ impl ImageCache {
height: dimensions.height };
pending_load.metadata = Some(img_metadata.clone());
for listener in &pending_load.listeners {
- listener.notify(ImageResponse::MetadataLoaded(img_metadata.clone()).clone());
+ listener.respond(ImageResponse::MetadataLoaded(img_metadata.clone()));
}
}
}
@@ -518,85 +464,35 @@ impl ImageCache {
LoadResult::None => ImageResponse::None,
};
- let completed_load = CompletedLoad::new(image_response.clone());
+ let completed_load = CompletedLoad::new(image_response.clone(), key);
self.completed_loads.insert(pending_load.url.into(), completed_load);
for listener in pending_load.listeners {
- listener.notify(image_response.clone());
+ listener.respond(image_response.clone());
}
}
- // Request an image from the cache. If the image hasn't been
- // loaded/decoded yet, it will be loaded/decoded in the
- // background. If send_metadata_msg is set, the channel will be notified
- // that image metadata is available, possibly before the image has finished
- // loading.
- fn request_image(&mut self,
- url: ServoUrl,
- result_chan: ImageCacheChan,
- responder: Option<ImageResponder>,
- send_metadata_msg: bool) {
- let image_listener = ImageListener::new(result_chan, responder, send_metadata_msg);
-
- // Check if already completed
- match self.completed_loads.get(&url) {
- Some(completed_load) => {
- // It's already completed, return a notify straight away
- image_listener.notify(completed_load.image_response.clone());
- }
- None => {
- // Check if the load is already pending
- let (cache_result, load_key, mut pending_load) = self.pending_loads.get_cached(url.clone());
- pending_load.add_listener(image_listener);
- match cache_result {
- CacheResult::Miss => {
- // A new load request! Request the load from
- // the resource thread.
- // https://html.spec.whatwg.org/multipage/#update-the-image-data
- // step 12.
- //
- // TODO(emilio): ServoUrl in more places please!
- let request = RequestInit {
- url: url.clone(),
- type_: RequestType::Image,
- destination: Destination::Image,
- origin: url.clone(),
- .. RequestInit::default()
- };
-
- let progress_sender = self.progress_sender.clone();
- fetch_async(request, &self.core_resource_thread, move |action| {
- let action = match action {
- FetchResponseMsg::ProcessRequestBody |
- FetchResponseMsg::ProcessRequestEOF => return,
- a => a
- };
- progress_sender.send(ResourceLoadInfo {
- action: action,
- key: load_key,
- }).unwrap();
- });
- }
- CacheResult::Hit => {
- // Request is already on its way.
- }
- }
+ /// Add a listener for a given image if it is still pending, or notify the
+ /// listener if the image is complete.
+ fn add_listener(&mut self,
+ id: PendingImageId,
+ listener: ImageResponder) {
+ if let Some(load) = self.pending_loads.get_by_key_mut(&id) {
+ if let Some(ref metadata) = load.metadata {
+ listener.respond(ImageResponse::MetadataLoaded(metadata.clone()));
}
+ load.add_listener(listener);
+ return;
}
+ if let Some(load) = self.completed_loads.values().find(|l| l.id == id) {
+ listener.respond(load.image_response.clone());
+ return;
+ }
+ warn!("Couldn't find cached entry for listener {:?}", id);
}
- fn get_image_if_available(&mut self,
- url: ServoUrl,
- placeholder: UsePlaceholder, )
- -> Result<Arc<Image>, ImageState> {
- let img_or_metadata = self.get_image_or_meta_if_available(url, placeholder);
- match img_or_metadata {
- Ok(ImageOrMetadataAvailable::ImageAvailable(image)) => Ok(image),
- Ok(ImageOrMetadataAvailable::MetadataAvailable(_)) => Err(ImageState::Pending),
- Err(err) => Err(err),
- }
- }
-
+ /// Return a completed image if it exists, or None if there is no complete load
+ /// of the complete load is not fully decoded or is unavailable.
fn get_image_or_meta_if_available(&mut self,
url: ServoUrl,
placeholder: UsePlaceholder)
@@ -616,46 +512,42 @@ impl ImageCache {
}
}
None => {
- let pl = match self.pending_loads.get_by_url(&url) {
- Some(pl) => pl,
- None => return Err(ImageState::NotRequested),
- };
-
- let meta = match pl.metadata {
- Some(ref meta) => meta,
- None => return Err(ImageState::Pending),
- };
-
- Ok(ImageOrMetadataAvailable::MetadataAvailable(meta.clone()))
+ let (result, key, pl) = self.pending_loads.get_cached(url);
+ match result {
+ CacheResult::Hit => match pl.metadata {
+ Some(ref meta) =>
+ Ok(ImageOrMetadataAvailable::MetadataAvailable(meta.clone())),
+ None =>
+ Err(ImageState::Pending(key)),
+ },
+ CacheResult::Miss => Err(ImageState::NotRequested(key)),
+ }
}
}
}
fn store_decode_image(&mut self,
- ref_url: ServoUrl,
+ id: PendingImageId,
loaded_bytes: Vec<u8>) {
- let (cache_result, load_key, _) = self.pending_loads.get_cached(ref_url.clone());
- assert!(cache_result == CacheResult::Miss);
let action = FetchResponseMsg::ProcessResponseChunk(loaded_bytes);
let _ = self.progress_sender.send(ResourceLoadInfo {
action: action,
- key: load_key,
+ key: id,
});
let action = FetchResponseMsg::ProcessResponseEOF(Ok(()));
let _ = self.progress_sender.send(ResourceLoadInfo {
action: action,
- key: load_key,
+ key: id,
});
}
}
/// Create a new image cache.
-pub fn new_image_cache_thread(core_resource_thread: CoreResourceThread,
- webrender_api: webrender_traits::RenderApi) -> ImageCacheThread {
+pub fn new_image_cache_thread(webrender_api: webrender_traits::RenderApi) -> ImageCacheThread {
let (ipc_command_sender, ipc_command_receiver) = ipc::channel().unwrap();
thread::Builder::new().name("ImageCacheThread".to_owned()).spawn(move || {
- ImageCache::run(core_resource_thread, webrender_api, ipc_command_receiver)
+ ImageCache::run(webrender_api, ipc_command_receiver)
}).expect("Thread spawning failed");
ImageCacheThread::new(ipc_command_sender)
diff --git a/components/net_traits/image_cache_thread.rs b/components/net_traits/image_cache_thread.rs
index 4fb7aedee04..a000bd8b6bc 100644
--- a/components/net_traits/image_cache_thread.rs
+++ b/components/net_traits/image_cache_thread.rs
@@ -13,27 +13,45 @@ use std::sync::Arc;
/// and/or repaint.
#[derive(Clone, Deserialize, Serialize)]
pub struct ImageResponder {
- sender: IpcSender<ImageResponse>,
+ id: PendingImageId,
+ sender: IpcSender<PendingImageResponse>,
+}
+
+#[derive(Deserialize, Serialize)]
+pub struct PendingImageResponse {
+ pub response: ImageResponse,
+ pub id: PendingImageId,
}
impl ImageResponder {
- pub fn new(sender: IpcSender<ImageResponse>) -> ImageResponder {
+ pub fn new(sender: IpcSender<PendingImageResponse>, id: PendingImageId) -> ImageResponder {
ImageResponder {
sender: sender,
+ id: id,
}
}
pub fn respond(&self, response: ImageResponse) {
- self.sender.send(response).unwrap()
+ // This send can fail if thread waiting for this notification has panicked.
+ // That's not a case that's worth warning about.
+ // TODO(#15501): are there cases in which we should perform cleanup?
+ let _ = self.sender.send(PendingImageResponse {
+ response: response,
+ id: self.id,
+ });
}
}
+/// The unique id for an image that has previously been requested.
+#[derive(Copy, Clone, PartialEq, Eq, Deserialize, Serialize, HeapSizeOf, Hash, Debug)]
+pub struct PendingImageId(pub u64);
+
/// The current state of an image in the cache.
#[derive(PartialEq, Copy, Clone, Deserialize, Serialize)]
pub enum ImageState {
- Pending,
+ Pending(PendingImageId),
LoadError,
- NotRequested,
+ NotRequested(PendingImageId),
}
/// The returned image.
@@ -56,45 +74,19 @@ pub enum ImageOrMetadataAvailable {
MetadataAvailable(ImageMetadata),
}
-/// Channel used by the image cache to send results.
-#[derive(Clone, Deserialize, Serialize)]
-pub struct ImageCacheChan(pub IpcSender<ImageCacheResult>);
-
-/// The result of an image cache command that is returned to the
-/// caller.
-#[derive(Deserialize, Serialize)]
-pub struct ImageCacheResult {
- pub responder: Option<ImageResponder>,
- pub image_response: ImageResponse,
-}
-
/// Commands that the image cache understands.
#[derive(Deserialize, Serialize)]
pub enum ImageCacheCommand {
- /// Request an image asynchronously from the cache. Supply a channel
- /// to receive the result, and optionally an image responder
- /// that is passed to the result channel.
- RequestImage(ServoUrl, ImageCacheChan, Option<ImageResponder>),
-
- /// Requests an image and a "metadata-ready" notification message asynchronously from the
- /// cache. The cache will make an effort to send metadata before the image is completely
- /// loaded. Supply a channel to receive the results, and optionally an image responder
- /// that is passed to the result channel.
- RequestImageAndMetadata(ServoUrl, ImageCacheChan, Option<ImageResponder>),
-
- /// Synchronously check the state of an image in the cache.
- /// TODO(gw): Profile this on some real world sites and see
- /// if it's worth caching the results of this locally in each
- /// layout / paint thread.
- GetImageIfAvailable(ServoUrl, UsePlaceholder, IpcSender<Result<Arc<Image>, ImageState>>),
-
/// Synchronously check the state of an image in the cache. If the image is in a loading
/// state and but its metadata has been made available, it will be sent as a response.
GetImageOrMetadataIfAvailable(ServoUrl, UsePlaceholder, IpcSender<Result<ImageOrMetadataAvailable, ImageState>>),
+ /// Add a new listener for the given pending image.
+ AddListener(PendingImageId, ImageResponder),
+
/// Instruct the cache to store this data as a newly-complete network request and continue
/// decoding the result into pixel data
- StoreDecodeImage(ServoUrl, Vec<u8>),
+ StoreDecodeImage(PendingImageId, Vec<u8>),
/// Clients must wait for a response before shutting down the ResourceThread
Exit(IpcSender<()>),
@@ -122,30 +114,6 @@ impl ImageCacheThread {
}
}
- /// Asynchronously request an image. See ImageCacheCommand::RequestImage.
- pub fn request_image(&self, url: ServoUrl, result_chan: ImageCacheChan, responder: Option<ImageResponder>) {
- let msg = ImageCacheCommand::RequestImage(url, result_chan, responder);
- let _ = self.chan.send(msg);
- }
-
- /// Asynchronously request an image and metadata.
- /// See ImageCacheCommand::RequestImageAndMetadata
- pub fn request_image_and_metadata(&self,
- url: ServoUrl,
- result_chan: ImageCacheChan,
- responder: Option<ImageResponder>) {
- let msg = ImageCacheCommand::RequestImageAndMetadata(url, result_chan, responder);
- let _ = self.chan.send(msg);
- }
-
- /// Get the current state of an image. See ImageCacheCommand::GetImageIfAvailable.
- pub fn find_image(&self, url: ServoUrl, use_placeholder: UsePlaceholder) -> Result<Arc<Image>, ImageState> {
- let (sender, receiver) = ipc::channel().unwrap();
- let msg = ImageCacheCommand::GetImageIfAvailable(url, use_placeholder, sender);
- let _ = self.chan.send(msg);
- try!(receiver.recv().map_err(|_| ImageState::LoadError))
- }
-
/// Get the current state of an image, returning its metadata if available.
/// See ImageCacheCommand::GetImageOrMetadataIfAvailable.
///
@@ -160,14 +128,23 @@ impl ImageCacheThread {
try!(receiver.recv().map_err(|_| ImageState::LoadError))
}
- /// Decode the given image bytes and cache the result for the given URL.
- pub fn store_complete_image_bytes(&self, url: ServoUrl, image_data: Vec<u8>) {
- let msg = ImageCacheCommand::StoreDecodeImage(url, image_data);
- let _ = self.chan.send(msg);
+ /// Add a new listener for the given pending image id. If the image is already present,
+ /// the responder will still receive the expected response.
+ pub fn add_listener(&self, id: PendingImageId, responder: ImageResponder) {
+ let msg = ImageCacheCommand::AddListener(id, responder);
+ self.chan.send(msg).expect("Image cache thread is not available");
+ }
+
+ /// Decode the given image bytes and cache the result for the given pending ID.
+ pub fn store_complete_image_bytes(&self, id: PendingImageId, image_data: Vec<u8>) {
+ let msg = ImageCacheCommand::StoreDecodeImage(id, image_data);
+ self.chan.send(msg).expect("Image cache thread is not available");
}
/// Shutdown the image cache thread.
pub fn exit(&self) {
+ // If the image cache is not available when we're trying to shut it down,
+ // that is not worth warning about.
let (response_chan, response_port) = ipc::channel().unwrap();
let _ = self.chan.send(ImageCacheCommand::Exit(response_chan));
let _ = response_port.recv();
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs
index 0e605780a20..4d38b39d333 100644
--- a/components/script/dom/bindings/trace.rs
+++ b/components/script/dom/bindings/trace.rs
@@ -62,7 +62,7 @@ use msg::constellation_msg::{FrameId, FrameType, PipelineId};
use net_traits::{Metadata, NetworkError, ReferrerPolicy, ResourceThreads};
use net_traits::filemanager_thread::RelativePos;
use net_traits::image::base::{Image, ImageMetadata};
-use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread};
+use net_traits::image_cache_thread::{ImageCacheThread, PendingImageId};
use net_traits::request::{Request, RequestInit};
use net_traits::response::{Response, ResponseBody};
use net_traits::response::HttpsState;
@@ -320,7 +320,7 @@ unsafe_no_jsmanaged_fields!(bool, f32, f64, String, AtomicBool, AtomicUsize, Uui
unsafe_no_jsmanaged_fields!(usize, u8, u16, u32, u64);
unsafe_no_jsmanaged_fields!(isize, i8, i16, i32, i64);
unsafe_no_jsmanaged_fields!(ServoUrl, ImmutableOrigin, MutableOrigin);
-unsafe_no_jsmanaged_fields!(Image, ImageMetadata, ImageCacheChan, ImageCacheThread);
+unsafe_no_jsmanaged_fields!(Image, ImageMetadata, ImageCacheThread, PendingImageId);
unsafe_no_jsmanaged_fields!(Metadata);
unsafe_no_jsmanaged_fields!(NetworkError);
unsafe_no_jsmanaged_fields!(Atom, Prefix, LocalName, Namespace, QualName);
diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs
index fff339e1cc7..065b966d939 100644
--- a/components/script/dom/canvasrenderingcontext2d.rs
+++ b/components/script/dom/canvasrenderingcontext2d.rs
@@ -429,7 +429,9 @@ impl CanvasRenderingContext2D {
let img = match self.request_image_from_cache(url) {
ImageResponse::Loaded(img) => img,
- ImageResponse::PlaceholderLoaded(_) | ImageResponse::None | ImageResponse::MetadataLoaded(_) => {
+ ImageResponse::PlaceholderLoaded(_) |
+ ImageResponse::None |
+ ImageResponse::MetadataLoaded(_) => {
return None;
}
};
diff --git a/components/script/dom/htmlcanvaselement.rs b/components/script/dom/htmlcanvaselement.rs
index b174d2ec9a8..47dc1d46a3d 100644
--- a/components/script/dom/htmlcanvaselement.rs
+++ b/components/script/dom/htmlcanvaselement.rs
@@ -337,15 +337,18 @@ impl<'a> From<&'a WebGLContextAttributes> for GLContextAttributes {
pub mod utils {
use dom::window::Window;
- use ipc_channel::ipc;
- use net_traits::image_cache_thread::{ImageCacheChan, ImageResponse};
+ use net_traits::image_cache_thread::ImageResponse;
use servo_url::ServoUrl;
pub fn request_image_from_cache(window: &Window, url: ServoUrl) -> ImageResponse {
- let image_cache = window.image_cache_thread();
+ panic!()
+ /*let image_cache = window.image_cache_thread();
let (response_chan, response_port) = ipc::channel().unwrap();
image_cache.request_image(url.into(), ImageCacheChan(response_chan), None);
let result = response_port.recv().unwrap();
- result.image_response
+ match result {
+ ImageCacheResult::InitiateRequest(..) => panic!("unexpected image request initiator"),
+ ImageCacheResult::Response(result) => result.image_response,
+ }*/
}
}
diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs
index ec9f2e1ec80..e762675bc70 100644
--- a/components/script/dom/htmlimageelement.rs
+++ b/components/script/dom/htmlimageelement.rs
@@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use app_units::{Au, AU_PER_PX};
+use document_loader::{LoadType, LoadBlocker};
use dom::activation::Activatable;
use dom::attr::Attr;
use dom::bindings::cell::DOMRefCell;
@@ -16,6 +17,7 @@ use dom::bindings::error::Fallible;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{LayoutJS, Root};
use dom::bindings::refcounted::Trusted;
+use dom::bindings::reflector::DomObject;
use dom::bindings::str::DOMString;
use dom::document::Document;
use dom::element::{AttributeMutation, Element, RawLayoutElementHelpers};
@@ -34,13 +36,17 @@ use euclid::point::Point2D;
use html5ever_atoms::LocalName;
use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
+use net_traits::{FetchResponseListener, FetchMetadata, Metadata, NetworkError};
use net_traits::image::base::{Image, ImageMetadata};
-use net_traits::image_cache_thread::{ImageResponder, ImageResponse};
+use net_traits::image_cache_thread::{ImageResponder, ImageResponse, PendingImageId, ImageState};
+use net_traits::image_cache_thread::{UsePlaceholder, ImageOrMetadataAvailable};
+use net_traits::request::{RequestInit, Type as RequestType};
+use network_listener::{NetworkListener, PreInvoke};
use num_traits::ToPrimitive;
use script_thread::Runnable;
use servo_url::ServoUrl;
use std::i32;
-use std::sync::Arc;
+use std::sync::{Arc, Mutex};
use style::attr::{AttrValue, LengthOrPercentageOrAuto};
use task_source::TaskSource;
@@ -53,10 +59,12 @@ enum State {
Broken,
}
#[derive(JSTraceable, HeapSizeOf)]
+#[must_root]
struct ImageRequest {
state: State,
parsed_url: Option<ServoUrl>,
source_url: Option<DOMString>,
+ blocker: Option<LoadBlocker>,
#[ignore_heap_size_of = "Arc"]
image: Option<Arc<Image>>,
metadata: Option<ImageMetadata>,
@@ -74,6 +82,64 @@ impl HTMLImageElement {
}
}
+struct ImageRequestRunnable {
+ element: Trusted<HTMLImageElement>,
+ img_url: ServoUrl,
+ id: PendingImageId,
+}
+
+impl ImageRequestRunnable {
+ fn new(element: Trusted<HTMLImageElement>,
+ img_url: ServoUrl,
+ id: PendingImageId)
+ -> ImageRequestRunnable {
+ ImageRequestRunnable {
+ element: element,
+ img_url: img_url,
+ id: id,
+ }
+ }
+}
+
+impl Runnable for ImageRequestRunnable {
+ fn handler(self: Box<Self>) {
+ let this = *self;
+ let trusted_node = this.element.clone();
+ let element = this.element.root();
+
+ let document = document_from_node(&*element);
+ let window = window_from_node(&*element);
+
+ let context = Arc::new(Mutex::new(ImageContext {
+ elem: trusted_node,
+ data: vec!(),
+ metadata: None,
+ url: this.img_url.clone(),
+ status: Ok(()),
+ id: this.id,
+ }));
+
+ 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 = RequestInit {
+ url: this.img_url.clone(),
+ origin: document.url().clone(),
+ type_: RequestType::Image,
+ pipeline_id: Some(document.global().pipeline_id()),
+ .. RequestInit::default()
+ };
+
+ document.fetch_async(LoadType::Image(this.img_url), request, action_sender);
+ }
+}
struct ImageResponseHandlerRunnable {
element: Trusted<HTMLImageElement>,
@@ -122,23 +188,83 @@ impl Runnable for ImageResponseHandlerRunnable {
element.upcast::<EventTarget>().fire_event(atom!("error"));
}
+ LoadBlocker::terminate(&mut element.current_request.borrow_mut().blocker);
+
// Trigger reflow
let window = window_from_node(&*document);
window.add_pending_reflow();
}
}
+/// The context required for asynchronously loading an external image.
+struct ImageContext {
+ /// The element that initiated the request.
+ elem: Trusted<HTMLImageElement>,
+ /// The response body received to date.
+ data: Vec<u8>,
+ /// The response metadata received to date.
+ metadata: Option<Metadata>,
+ /// The initial URL requested.
+ url: ServoUrl,
+ /// Indicates whether the request failed, and why
+ status: Result<(), NetworkError>,
+ /// The cache ID for this request.
+ id: PendingImageId,
+}
+
+impl FetchResponseListener for ImageContext {
+ fn process_request_body(&mut self) {}
+ fn process_request_eof(&mut self) {}
+
+ fn process_response(&mut self, metadata: Result<FetchMetadata, NetworkError>) {
+ self.metadata = metadata.ok().map(|meta| match meta {
+ FetchMetadata::Unfiltered(m) => m,
+ FetchMetadata::Filtered { unsafe_, .. } => unsafe_
+ });
+
+ let status_code = self.metadata.as_ref().and_then(|m| {
+ match m.status {
+ Some((c, _)) => Some(c),
+ _ => None,
+ }
+ }).unwrap_or(0);
+
+ self.status = match status_code {
+ 0 => Err(NetworkError::Internal("No http status code received".to_owned())),
+ 200...299 => Ok(()), // HTTP ok status codes
+ _ => Err(NetworkError::Internal(format!("HTTP error code {}", status_code)))
+ };
+ }
+
+ fn process_response_chunk(&mut self, mut payload: Vec<u8>) {
+ if self.status.is_ok() {
+ self.data.append(&mut payload);
+ }
+ }
+
+ fn process_response_eof(&mut self, _response: Result<(), NetworkError>) {
+ let elem = self.elem.root();
+ let document = document_from_node(&*elem);
+ 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 ImageContext {}
+
impl HTMLImageElement {
/// Makes the local `image` member match the status of the `src` attribute and starts
/// prefetching the image. This method must be called after `src` is changed.
fn update_image(&self, value: Option<(DOMString, ServoUrl)>) {
let document = document_from_node(self);
let window = document.window();
- let image_cache = window.image_cache_thread();
match value {
None => {
self.current_request.borrow_mut().parsed_url = None;
self.current_request.borrow_mut().source_url = None;
+ LoadBlocker::terminate(&mut self.current_request.borrow_mut().blocker);
self.current_request.borrow_mut().image = None;
}
Some((src, base_url)) => {
@@ -147,22 +273,57 @@ impl HTMLImageElement {
self.current_request.borrow_mut().parsed_url = Some(img_url.clone());
self.current_request.borrow_mut().source_url = Some(src);
+ LoadBlocker::terminate(&mut self.current_request.borrow_mut().blocker);
+ self.current_request.borrow_mut().blocker =
+ Some(LoadBlocker::new(&*document, LoadType::Image(img_url.clone())));
+
let trusted_node = Trusted::new(self);
let (responder_sender, responder_receiver) = ipc::channel().unwrap();
let task_source = window.networking_task_source();
let wrapper = window.get_runnable_wrapper();
+ let img_url_cloned = img_url.clone();
+ let trusted_node_clone = trusted_node.clone();
ROUTER.add_route(responder_receiver.to_opaque(), box move |message| {
// Return the image via a message to the script thread, which marks the element
// as dirty and triggers a reflow.
- let image_response = message.to().unwrap();
- let runnable = box ImageResponseHandlerRunnable::new(
- trusted_node.clone(), image_response);
- let _ = task_source.queue_with_wrapper(runnable, &wrapper);
+ let runnable = ImageResponseHandlerRunnable::new(
+ trusted_node_clone.clone(), message.to().unwrap());
+ let _ = task_source.queue_with_wrapper(box runnable, &wrapper);
});
- image_cache.request_image_and_metadata(img_url.into(),
- window.image_cache_chan(),
- Some(ImageResponder::new(responder_sender)));
+ let image_cache = window.image_cache_thread();
+ let response =
+ image_cache.find_image_or_metadata(img_url_cloned.into(), UsePlaceholder::Yes);
+ match response {
+ Ok(ImageOrMetadataAvailable::ImageAvailable(image)) => {
+ let event = box ImageResponseHandlerRunnable::new(
+ trusted_node, ImageResponse::Loaded(image));
+ event.handler();
+ }
+
+ Ok(ImageOrMetadataAvailable::MetadataAvailable(m)) => {
+ let event = box ImageResponseHandlerRunnable::new(
+ trusted_node, ImageResponse::MetadataLoaded(m));
+ event.handler();
+ }
+
+ Err(ImageState::Pending(id)) => {
+ image_cache.add_listener(id, ImageResponder::new(responder_sender, id));
+ }
+
+ Err(ImageState::LoadError) => {
+ let event = box ImageResponseHandlerRunnable::new(
+ trusted_node, ImageResponse::None);
+ event.handler();
+ }
+
+ Err(ImageState::NotRequested(id)) => {
+ image_cache.add_listener(id, ImageResponder::new(responder_sender, id));
+ let runnable = box ImageRequestRunnable::new(
+ Trusted::new(self), img_url, id);
+ runnable.handler();
+ }
+ }
} else {
// https://html.spec.whatwg.org/multipage/#update-the-image-data
// Step 11 (error substeps)
@@ -202,6 +363,7 @@ impl HTMLImageElement {
}
}
}
+
fn new_inherited(local_name: LocalName, prefix: Option<DOMString>, document: &Document) -> HTMLImageElement {
HTMLImageElement {
htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
@@ -210,14 +372,16 @@ impl HTMLImageElement {
parsed_url: None,
source_url: None,
image: None,
- metadata: None
+ metadata: None,
+ blocker: None,
}),
pending_request: DOMRefCell::new(ImageRequest {
state: State::Unavailable,
parsed_url: None,
source_url: None,
image: None,
- metadata: None
+ metadata: None,
+ blocker: None,
}),
}
}
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);
+}
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs
index 2ab421c707b..192923294a6 100644
--- a/components/script/script_thread.rs
+++ b/components/script/script_thread.rs
@@ -73,7 +73,7 @@ use microtask::{MicrotaskQueue, Microtask};
use msg::constellation_msg::{FrameId, FrameType, PipelineId, PipelineNamespace};
use net_traits::{CoreResourceMsg, FetchMetadata, FetchResponseListener};
use net_traits::{IpcSend, Metadata, ReferrerPolicy, ResourceThreads};
-use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheResult, ImageCacheThread};
+use net_traits::image_cache_thread::{PendingImageResponse, ImageCacheThread};
use net_traits::request::{CredentialsMode, Destination, RequestInit};
use net_traits::storage_thread::StorageType;
use network_listener::NetworkListener;
@@ -120,6 +120,8 @@ use url::Position;
use webdriver_handlers;
use webvr_traits::WebVRMsg;
+pub type ImageCacheMsg = (PipelineId, PendingImageResponse);
+
thread_local!(pub static STACK_ROOTS: Cell<Option<RootCollectionPtr>> = Cell::new(None));
thread_local!(static SCRIPT_THREAD_ROOT: Cell<Option<*const ScriptThread>> = Cell::new(None));
@@ -230,7 +232,7 @@ enum MixedMessage {
FromConstellation(ConstellationControlMsg),
FromScript(MainThreadScriptMsg),
FromDevtools(DevtoolScriptControlMsg),
- FromImageCache(ImageCacheResult),
+ FromImageCache((PipelineId, PendingImageResponse)),
FromScheduler(TimerEvent)
}
@@ -444,10 +446,10 @@ pub struct ScriptThread {
layout_to_constellation_chan: IpcSender<LayoutMsg>,
/// The port on which we receive messages from the image cache
- image_cache_port: Receiver<ImageCacheResult>,
+ image_cache_port: Receiver<ImageCacheMsg>,
/// The channel on which the image cache can send messages to ourself.
- image_cache_channel: ImageCacheChan,
+ image_cache_channel: Sender<ImageCacheMsg>,
/// For providing contact with the time profiler.
time_profiler_chan: time::ProfilerChan,
@@ -646,11 +648,6 @@ impl ScriptThread {
let (ipc_devtools_sender, ipc_devtools_receiver) = ipc::channel().unwrap();
let devtools_port = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_devtools_receiver);
- // Ask the router to proxy IPC messages from the image cache thread to us.
- let (ipc_image_cache_channel, ipc_image_cache_port) = ipc::channel().unwrap();
- let image_cache_port =
- ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_image_cache_port);
-
let (timer_event_chan, timer_event_port) = channel();
// Ask the router to proxy IPC messages from the control port to us.
@@ -658,6 +655,8 @@ impl ScriptThread {
let boxed_script_sender = MainThreadScriptChan(chan.clone()).clone();
+ let (image_cache_channel, image_cache_port) = channel();
+
ScriptThread {
documents: DOMRefCell::new(Documents::new()),
browsing_contexts: DOMRefCell::new(HashMap::new()),
@@ -666,7 +665,7 @@ impl ScriptThread {
job_queue_map: Rc::new(JobQueue::new()),
image_cache_thread: state.image_cache_thread,
- image_cache_channel: ImageCacheChan(ipc_image_cache_channel),
+ image_cache_channel: image_cache_channel,
image_cache_port: image_cache_port,
resource_threads: state.resource_threads,
@@ -1111,8 +1110,11 @@ impl ScriptThread {
}
}
- fn handle_msg_from_image_cache(&self, msg: ImageCacheResult) {
- msg.responder.unwrap().respond(msg.image_response);
+ fn handle_msg_from_image_cache(&self, (id, response): (PipelineId, PendingImageResponse)) {
+ let window = self.documents.borrow().find_window(id);
+ if let Some(ref window) = window {
+ window.pending_image_notification(response);
+ }
}
fn handle_webdriver_msg(&self, pipeline_id: PipelineId, msg: WebDriverScriptCommand) {
diff --git a/components/script_layout_interface/lib.rs b/components/script_layout_interface/lib.rs
index 87b76763d7d..1793e06909d 100644
--- a/components/script_layout_interface/lib.rs
+++ b/components/script_layout_interface/lib.rs
@@ -43,6 +43,9 @@ use canvas_traits::CanvasMsg;
use core::nonzero::NonZero;
use ipc_channel::ipc::IpcSender;
use libc::c_void;
+use net_traits::image_cache_thread::PendingImageId;
+use script_traits::UntrustedNodeAddress;
+use servo_url::ServoUrl;
use std::sync::atomic::AtomicIsize;
use style::data::ElementData;
@@ -137,3 +140,18 @@ pub fn is_image_data(uri: &str) -> bool {
static TYPES: &'static [&'static str] = &["data:image/png", "data:image/gif", "data:image/jpeg"];
TYPES.iter().any(|&type_| uri.starts_with(type_))
}
+
+/// Whether the pending image needs to be fetched or is waiting on an existing fetch.
+pub enum PendingImageState {
+ Unrequested(ServoUrl),
+ PendingResponse,
+}
+
+/// The data associated with an image that is not yet present in the image cache.
+/// Used by the script thread to hold on to DOM elements that need to be repainted
+/// when an image fetch is complete.
+pub struct PendingImage {
+ pub state: PendingImageState,
+ pub node: UntrustedNodeAddress,
+ pub id: PendingImageId,
+}
diff --git a/components/script_layout_interface/rpc.rs b/components/script_layout_interface/rpc.rs
index 2fb75f6b959..78e99571ee7 100644
--- a/components/script_layout_interface/rpc.rs
+++ b/components/script_layout_interface/rpc.rs
@@ -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 PendingImage;
use app_units::Au;
use euclid::point::Point2D;
use euclid::rect::Rect;
@@ -37,7 +38,8 @@ pub trait LayoutRPC {
fn offset_parent(&self) -> OffsetParentResponse;
/// Query layout for the resolve values of the margin properties for an element.
fn margin_style(&self) -> MarginStyleResponse;
-
+ /// Requests the list of not-yet-loaded images that were encountered in the last reflow.
+ fn pending_images(&self) -> Vec<PendingImage>;
fn nodes_from_point(&self, page_point: Point2D<f32>, client_point: Point2D<f32>) -> Vec<UntrustedNodeAddress>;
fn text_index(&self) -> TextIndexResponse;
diff --git a/components/script_plugins/unrooted_must_root.rs b/components/script_plugins/unrooted_must_root.rs
index 660a0c58781..ad74f6c4b8f 100644
--- a/components/script_plugins/unrooted_must_root.rs
+++ b/components/script_plugins/unrooted_must_root.rs
@@ -52,6 +52,7 @@ fn is_unrooted_ty(cx: &LateContext, ty: &ty::TyS, in_new_function: bool) -> bool
} else if match_def_path(cx, did.did, &["core", "cell", "Ref"])
|| match_def_path(cx, did.did, &["core", "cell", "RefMut"])
|| match_def_path(cx, did.did, &["core", "slice", "Iter"])
+ || match_def_path(cx, did.did, &["std", "collections", "hash", "map", "Entry"])
|| match_def_path(cx, did.did, &["std", "collections", "hash", "map", "OccupiedEntry"])
|| match_def_path(cx, did.did, &["std", "collections", "hash", "map", "VacantEntry"]) {
// Structures which are semantically similar to an &ptr.
diff --git a/components/servo/lib.rs b/components/servo/lib.rs
index afda35f1582..69b9bbeceb8 100644
--- a/components/servo/lib.rs
+++ b/components/servo/lib.rs
@@ -281,8 +281,7 @@ fn create_constellation(user_agent: Cow<'static, str>,
devtools_chan.clone(),
time_profiler_chan.clone(),
config_dir);
- let image_cache_thread = new_image_cache_thread(public_resource_threads.sender(),
- webrender_api_sender.create_api());
+ let image_cache_thread = new_image_cache_thread(webrender_api_sender.create_api());
let font_cache_thread = FontCacheThread::new(public_resource_threads.sender(),
Some(webrender_api_sender.create_api()));