Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DO NOT MERGE] Initiate image loads in the script thread #14781

Closed
wants to merge 8 commits into from

Avoid dropping image requests on the ground from non-script-initiated…

… reflow.
  • Loading branch information
jdm committed Jan 11, 2017
commit c68ceb34cfc08cfd9b2140f0094d106c1d7b05be
@@ -9,7 +9,7 @@ use gfx::display_list::{WebRenderImageInfo, OpaqueNode};
use gfx::font_cache_thread::FontCacheThread;
use gfx::font_context::FontContext;
use heapsize::HeapSizeOf;
use net_traits::image_cache_thread::{ImageCacheThread, ImageState};
use net_traits::image_cache_thread::{ImageCacheThread, ImageState, CanRequestImages};
use net_traits::image_cache_thread::{ImageOrMetadataAvailable, UsePlaceholder};
use opaque_node::OpaqueNodeMethods;
use parking_lot::RwLock;
@@ -112,12 +112,14 @@ pub struct SharedLayoutContext {

/// 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>>
pub pending_images: Option<Mutex<Vec<PendingImage>>>
}

impl Drop for SharedLayoutContext {
fn drop(&mut self) {
assert!(self.pending_images.lock().unwrap().is_empty());
if let Some(ref pending_images) = self.pending_images {
assert!(pending_images.lock().unwrap().is_empty());
}
}
}

@@ -161,11 +163,21 @@ impl SharedLayoutContext {
node: OpaqueNode,
url: ServoUrl,
use_placeholder: UsePlaceholder)
-> Option<ImageOrMetadataAvailable> {
-> Option<ImageOrMetadataAvailable> {
//XXXjdm For cases where we do not request an image, we still need to
// ensure the node gets another script-initiated reflow or it
// won't be requested at all.
let can_request = if self.pending_images.is_some() {
CanRequestImages::Yes
} else {
CanRequestImages::No
};

// See if the image is already available
let result = self.image_cache_thread.lock().unwrap()
.find_image_or_metadata(url.clone(),
use_placeholder);
use_placeholder,
can_request);
match result {
Ok(image_or_metadata) => Some(image_or_metadata),
// Image failed to load, so just return nothing
@@ -177,18 +189,23 @@ impl SharedLayoutContext {
node: node.to_untrusted_node_address(),
id: id,
};
self.pending_images.lock().unwrap().push(image);
self.pending_images.as_ref().unwrap().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(id)) => {
let image = PendingImage {
state: PendingImageState::PendingResponse,
node: node.to_untrusted_node_address(),
id: id,
};
self.pending_images.lock().unwrap().push(image);
//XXXjdm if self.pending_images is not available, we should make sure that
// this node gets marked dirty again so it gets a script-initiated
// reflow that deals with this properly.
if let Some(ref pending_images) = self.pending_images {
let image = PendingImage {
state: PendingImageState::PendingResponse,
node: node.to_untrusted_node_address(),
id: id,
};
pending_images.lock().unwrap().push(image);
}
None
}
}
@@ -499,7 +499,8 @@ impl LayoutThread {
fn build_shared_layout_context(&self,
rw_data: &LayoutThreadData,
screen_size_changed: bool,
goal: ReflowGoal)
goal: ReflowGoal,
request_images: bool)
-> SharedLayoutContext {
let thread_local_style_context_creation_data =
ThreadLocalStyleContextCreationInfo::new(self.new_animations_sender.clone());
@@ -525,7 +526,7 @@ impl LayoutThread {
image_cache_thread: Mutex::new(self.image_cache_thread.clone()),
font_cache_thread: Mutex::new(self.font_cache_thread.clone()),
webrender_image_cache: self.webrender_image_cache.clone(),
pending_images: Mutex::new(vec![]),
pending_images: if request_images { Some(Mutex::new(vec![])) } else { None },
}
}

@@ -1103,7 +1104,8 @@ impl LayoutThread {
// Create a layout context for use throughout the following passes.
let mut shared_layout_context = self.build_shared_layout_context(&*rw_data,
viewport_size_changed,
data.reflow_info.goal);
data.reflow_info.goal,
true);

// NB: Type inference falls apart here for some reason, so we need to be very verbose. :-(
let traversal = RecalcStyleAndConstructFlows::new(shared_layout_context);
@@ -1177,8 +1179,11 @@ impl LayoutThread {
query_type: &ReflowQueryType,
rw_data: &mut LayoutThreadData,
shared: &mut SharedLayoutContext) {
rw_data.pending_images =
std_mem::replace(&mut shared.pending_images.lock().unwrap(), vec![]);
let pending_images = match shared.pending_images {
Some(ref pending) => std_mem::replace(&mut *pending.lock().unwrap(), vec![]),
None => vec![],
};
rw_data.pending_images = pending_images;

let mut root_flow = match self.root_flow.clone() {
Some(root_flow) => root_flow,
@@ -1287,7 +1292,8 @@ impl LayoutThread {

let mut layout_context = self.build_shared_layout_context(&*rw_data,
false,
reflow_info.goal);
reflow_info.goal,
false);

if let Some(mut root_flow) = self.root_flow.clone() {
// Perform an abbreviated style recalc that operates without access to the DOM.
@@ -1308,13 +1314,7 @@ impl LayoutThread {
&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);
}
assert!(layout_context.pending_images.is_none());
}

fn reflow_with_newly_loaded_web_font<'a, 'b>(&mut self, possibly_locked_rw_data: &mut RwData<'a, 'b>) {
@@ -1328,7 +1328,8 @@ impl LayoutThread {

let mut layout_context = self.build_shared_layout_context(&*rw_data,
false,
reflow_info.goal);
reflow_info.goal,
false);

// No need to do a style recalc here.
if self.root_flow.is_none() {
@@ -9,7 +9,7 @@ use net_traits::{NetworkError, FetchResponseMsg};
use net_traits::image::base::{Image, ImageMetadata, PixelFormat, load_from_memory};
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 net_traits::image_cache_thread::{ImageResponder, PendingImageId, CanRequestImages};
use servo_config::resource_files::resources_dir_path;
use servo_url::ServoUrl;
use std::borrow::ToOwned;
@@ -88,11 +88,12 @@ struct AllPendingLoads {
keygen: LoadKeyGenerator,
}

// Result of accessing a cache.
#[derive(Eq, PartialEq)]
enum CacheResult {
Hit, // The value was in the cache.
Miss, // The value was not in the cache and needed to be regenerated.
/// Result of accessing a cache.
enum CacheResult<'a> {
/// The value was in the cache.
Hit(LoadKey, &'a mut PendingLoad),
/// The value was not in the cache and needed to be regenerated.
Miss(Option<(LoadKey, &'a mut PendingLoad)>),
}

impl AllPendingLoads {
@@ -123,13 +124,18 @@ impl AllPendingLoads {
})
}

fn get_cached(&mut self, url: ServoUrl) -> (CacheResult, LoadKey, &mut PendingLoad) {
fn get_cached<'a>(&'a mut self, url: ServoUrl, can_request: CanRequestImages)
-> CacheResult<'a> {
match self.url_to_load_key.entry(url.clone()) {
Occupied(url_entry) => {
let load_key = url_entry.get();
(CacheResult::Hit, *load_key, self.loads.get_mut(load_key).unwrap())
CacheResult::Hit(*load_key, self.loads.get_mut(load_key).unwrap())
}
Vacant(url_entry) => {
if can_request == CanRequestImages::No {
return CacheResult::Miss(None);
}

let load_key = self.keygen.next();
url_entry.insert(load_key);

@@ -138,7 +144,7 @@ impl AllPendingLoads {
Occupied(_) => unreachable!(),
Vacant(load_entry) => {
let mut_load = load_entry.insert(pending_load);
(CacheResult::Miss, load_key, mut_load)
CacheResult::Miss(Some((load_key, mut_load)))
}
}
}
@@ -357,8 +363,11 @@ impl ImageCache {
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);
ImageCacheCommand::GetImageOrMetadataIfAvailable(url,
use_placeholder,
can_request,
consumer) => {
let result = self.get_image_or_meta_if_available(url, use_placeholder, can_request);
let _ = consumer.send(result);
}
ImageCacheCommand::StoreDecodeImage(id, image_vector) => {
@@ -488,7 +497,8 @@ impl ImageCache {
/// of the complete load is not fully decoded or is unavailable.
fn get_image_or_meta_if_available(&mut self,
url: ServoUrl,
placeholder: UsePlaceholder)
placeholder: UsePlaceholder,
can_request: CanRequestImages)
-> Result<ImageOrMetadataAvailable, ImageState> {
match self.completed_loads.get(&url) {
Some(completed_load) => {
@@ -505,15 +515,16 @@ impl ImageCache {
}
}
None => {
let (result, key, pl) = self.pending_loads.get_cached(url);
let result = self.pending_loads.get_cached(url, can_request);
match result {
CacheResult::Hit => match pl.metadata {
CacheResult::Hit(key, pl) => match pl.metadata {
Some(ref meta) =>
Ok(ImageOrMetadataAvailable::MetadataAvailable(meta.clone())),
None =>
Err(ImageState::Pending(key)),
},
CacheResult::Miss => Err(ImageState::NotRequested(key)),
CacheResult::Miss(Some((key, _pl))) => Err(ImageState::NotRequested(key)),
CacheResult::Miss(None) => Err(ImageState::LoadError),
}
}
}
@@ -76,7 +76,10 @@ pub enum ImageOrMetadataAvailable {
pub enum ImageCacheCommand {
/// 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>>),
GetImageOrMetadataIfAvailable(ServoUrl,
UsePlaceholder,
CanRequestImages,
IpcSender<Result<ImageOrMetadataAvailable, ImageState>>),

/// Add a new listener for the given pending image.
AddListener(PendingImageId, ImageResponder),
@@ -95,6 +98,12 @@ pub enum UsePlaceholder {
Yes,
}

#[derive(Copy, Clone, PartialEq, Deserialize, Serialize)]
pub enum CanRequestImages {
No,
Yes,
}

/// The client side of the image cache thread. This can be safely cloned
/// and passed to different threads.
#[derive(Clone, Deserialize, Serialize)]
@@ -117,10 +126,14 @@ impl ImageCacheThread {
/// FIXME: We shouldn't do IPC for data uris!
pub fn find_image_or_metadata(&self,
url: ServoUrl,
use_placeholder: UsePlaceholder)
use_placeholder: UsePlaceholder,
can_request: CanRequestImages)
-> Result<ImageOrMetadataAvailable, ImageState> {
let (sender, receiver) = ipc::channel().unwrap();
let msg = ImageCacheCommand::GetImageOrMetadataIfAvailable(url, use_placeholder, sender);
let msg = ImageCacheCommand::GetImageOrMetadataIfAvailable(url,
use_placeholder,
can_request,
sender);
let _ = self.chan.send(msg);
try!(receiver.recv().map_err(|_| ImageState::LoadError))
}
@@ -343,9 +343,10 @@ pub mod utils {

pub fn request_image_from_cache(window: &Window, url: ServoUrl) -> ImageResponse {
let image_cache = window.image_cache_thread();
//XXXjdm add a image cache mode that doesn't store anything for NotRequested?
let response =
image_cache.find_image_or_metadata(url.into(), UsePlaceholder::No);
image_cache.find_image_or_metadata(url.into(),
UsePlaceholder::No,
CanRequestImages::No);
match response {
Ok(ImageOrMetadataAvailable::ImageAvailable(image)) =>
ImageResponse::Loaded(image),
@@ -29,7 +29,7 @@ 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, PendingImageId, ImageState};
use net_traits::image_cache_thread::{UsePlaceholder, ImageOrMetadataAvailable};
use net_traits::image_cache_thread::{UsePlaceholder, ImageOrMetadataAvailable, CanRequestImages};
use net_traits::request::{RequestInit, Type as RequestType};
use network_listener::{NetworkListener, PreInvoke};
use script_thread::Runnable;
@@ -282,7 +282,9 @@ impl HTMLImageElement {

let image_cache = window.image_cache_thread();
let response =
image_cache.find_image_or_metadata(img_url_cloned.into(), UsePlaceholder::Yes);
image_cache.find_image_or_metadata(img_url_cloned.into(),
UsePlaceholder::Yes,
CanRequestImages::Yes);
match response {
Ok(ImageOrMetadataAvailable::ImageAvailable(image)) => {
let event = box ImageResponseHandlerRunnable::new(
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.