Auto merge of #23661 - julientregoat:i-21289, r=jdm

Refactor ImageCache::find_image_or_metadata -> ImageCache::{get_image, track_image}

<!-- Please describe your changes on the following line: -->
Updated the `ImageCache` trait to replace `find_image_or_metadata` with two new functions `track_image` and `get_image`, as well as a new enum (`ImageCacheResult`).

As a result, I was able to refactor the functions that previously called `find_image_or_metadata` pretty cleanly. For a list of these functions, please see the commit information.

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [x] These changes fix #21289  (GitHub issue number if applicable)

<!-- Either: -->
- [ ] There are tests for these changes OR
- [x] These changes do not require tests because tests already exist for these components. I ran `cargo test` in `net`, `net_traits`, `layout`, and `script` successfully.

<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/23661)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2020-04-17 15:56:30 -04:00 committed by GitHub
commit c9480c8e07
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 282 additions and 242 deletions

View file

@ -11,7 +11,7 @@ use gfx::font_cache_thread::FontCacheThread;
use gfx::font_context::FontContext; use gfx::font_context::FontContext;
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use msg::constellation_msg::PipelineId; use msg::constellation_msg::PipelineId;
use net_traits::image_cache::{CanRequestImages, ImageCache, ImageState}; use net_traits::image_cache::{CanRequestImages, ImageCache, ImageCacheResult};
use net_traits::image_cache::{ImageOrMetadataAvailable, UsePlaceholder}; use net_traits::image_cache::{ImageOrMetadataAvailable, UsePlaceholder};
use parking_lot::RwLock; use parking_lot::RwLock;
use script_layout_interface::{PendingImage, PendingImageState}; use script_layout_interface::{PendingImage, PendingImageState};
@ -122,24 +122,40 @@ impl<'a> LayoutContext<'a> {
CanRequestImages::No CanRequestImages::No
}; };
// See if the image is already available // Check for available image or start tracking.
let result = self.image_cache.find_image_or_metadata( let cache_result = self.image_cache.get_cached_image_status(
url.clone(), url.clone(),
self.origin.clone(), self.origin.clone(),
None, None,
use_placeholder, use_placeholder,
can_request, can_request,
); );
match result {
Ok(image_or_metadata) => Some(image_or_metadata), match cache_result {
// Image failed to load, so just return nothing ImageCacheResult::Available(img_or_meta) => Some(img_or_meta),
Err(ImageState::LoadError) => 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.
ImageCacheResult::Pending(id) => {
//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,
origin: self.origin.clone(),
};
pending_images.lock().unwrap().push(image);
}
None
},
// Not yet requested - request image or metadata from the cache // Not yet requested - request image or metadata from the cache
Err(ImageState::NotRequested(id)) => { ImageCacheResult::ReadyForRequest(id) => {
let image = PendingImage { let image = PendingImage {
state: PendingImageState::Unrequested(url), state: PendingImageState::Unrequested(url),
node: node.to_untrusted_node_address(), node: node.to_untrusted_node_address(),
id: id, id,
origin: self.origin.clone(), origin: self.origin.clone(),
}; };
self.pending_images self.pending_images
@ -150,23 +166,8 @@ impl<'a> LayoutContext<'a> {
.push(image); .push(image);
None None
}, },
// Image has been requested, is still pending. Return no image for this paint loop. // Image failed to load, so just return nothing
// When the image loads it will trigger a reflow and/or repaint. ImageCacheResult::LoadError => None,
Err(ImageState::Pending(id)) => {
//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,
origin: self.origin.clone(),
};
pending_images.lock().unwrap().push(image);
}
None
},
} }
} }

View file

@ -3,11 +3,12 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::display_list::WebRenderImageInfo; use crate::display_list::WebRenderImageInfo;
use crate::opaque_node::OpaqueNodeMethods;
use fnv::FnvHashMap; use fnv::FnvHashMap;
use gfx::font_cache_thread::FontCacheThread; use gfx::font_cache_thread::FontCacheThread;
use gfx::font_context::FontContext; use gfx::font_context::FontContext;
use msg::constellation_msg::PipelineId; use msg::constellation_msg::PipelineId;
use net_traits::image_cache::{CanRequestImages, ImageCache, ImageState}; use net_traits::image_cache::{CanRequestImages, ImageCache, ImageCacheResult};
use net_traits::image_cache::{ImageOrMetadataAvailable, UsePlaceholder}; use net_traits::image_cache::{ImageOrMetadataAvailable, UsePlaceholder};
use parking_lot::RwLock; use parking_lot::RwLock;
use script_layout_interface::{PendingImage, PendingImageState}; use script_layout_interface::{PendingImage, PendingImageState};
@ -70,24 +71,40 @@ impl<'a> LayoutContext<'a> {
CanRequestImages::No CanRequestImages::No
}; };
// See if the image is already available // Check for available image or start tracking.
let result = self.image_cache.find_image_or_metadata( let cache_result = self.image_cache.get_cached_image_status(
url.clone(), url.clone(),
self.origin.clone(), self.origin.clone(),
None, None,
use_placeholder, use_placeholder,
can_request, can_request,
); );
match result {
Ok(image_or_metadata) => Some(image_or_metadata), match cache_result {
// Image failed to load, so just return nothing ImageCacheResult::Available(img_or_meta) => Some(img_or_meta),
Err(ImageState::LoadError) => 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.
ImageCacheResult::Pending(id) => {
//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,
origin: self.origin.clone(),
};
pending_images.lock().unwrap().push(image);
}
None
},
// Not yet requested - request image or metadata from the cache // Not yet requested - request image or metadata from the cache
Err(ImageState::NotRequested(id)) => { ImageCacheResult::ReadyForRequest(id) => {
let image = PendingImage { let image = PendingImage {
state: PendingImageState::Unrequested(url), state: PendingImageState::Unrequested(url),
node: node.into(), node: node.to_untrusted_node_address(),
id: id, id,
origin: self.origin.clone(), origin: self.origin.clone(),
}; };
self.pending_images self.pending_images
@ -98,23 +115,8 @@ impl<'a> LayoutContext<'a> {
.push(image); .push(image);
None None
}, },
// Image has been requested, is still pending. Return no image for this paint loop. // Image failed to load, so just return nothing
// When the image loads it will trigger a reflow and/or repaint. ImageCacheResult::LoadError => None,
Err(ImageState::Pending(id)) => {
//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.into(),
id: id,
origin: self.origin.clone(),
};
pending_images.lock().unwrap().push(image);
}
None
},
} }
} }

View file

@ -4,9 +4,13 @@
use embedder_traits::resources::{self, Resource}; use embedder_traits::resources::{self, Resource};
use immeta::load_from_buf; use immeta::load_from_buf;
use ipc_channel::ipc::IpcSender;
use net_traits::image::base::{load_from_memory, Image, ImageMetadata}; use net_traits::image::base::{load_from_memory, Image, ImageMetadata};
use net_traits::image_cache::{CanRequestImages, CorsStatus, ImageCache, ImageResponder}; use net_traits::image_cache::{
use net_traits::image_cache::{ImageOrMetadataAvailable, ImageResponse, ImageState}; CanRequestImages, CorsStatus, ImageCache, ImageCacheResult, ImageResponder,
PendingImageResponse,
};
use net_traits::image_cache::{ImageOrMetadataAvailable, ImageResponse};
use net_traits::image_cache::{PendingImageId, UsePlaceholder}; use net_traits::image_cache::{PendingImageId, UsePlaceholder};
use net_traits::request::CorsSettings; use net_traits::request::CorsSettings;
use net_traits::{ use net_traits::{
@ -16,7 +20,6 @@ use pixels::PixelFormat;
use servo_url::{ImmutableOrigin, ServoUrl}; use servo_url::{ImmutableOrigin, ServoUrl};
use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::HashMap; use std::collections::HashMap;
use std::io;
use std::mem; use std::mem;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::thread; use std::thread;
@ -45,13 +48,10 @@ fn decode_bytes_sync(key: LoadKey, bytes: &[u8], cors: CorsStatus) -> DecoderMsg
} }
} }
fn get_placeholder_image( fn get_placeholder_image(webrender_api: &WebrenderIpcSender, data: &[u8]) -> Arc<Image> {
webrender_api: &WebrenderIpcSender,
data: &[u8],
) -> io::Result<Arc<Image>> {
let mut image = load_from_memory(&data, CorsStatus::Unsafe).unwrap(); let mut image = load_from_memory(&data, CorsStatus::Unsafe).unwrap();
set_webrender_image_key(webrender_api, &mut image); set_webrender_image_key(webrender_api, &mut image);
Ok(Arc::new(image)) Arc::new(image)
} }
fn set_webrender_image_key(webrender_api: &WebrenderIpcSender, image: &mut Image) { fn set_webrender_image_key(webrender_api: &WebrenderIpcSender, image: &mut Image) {
@ -335,7 +335,7 @@ struct ImageCacheStore {
completed_loads: HashMap<ImageKey, CompletedLoad>, completed_loads: HashMap<ImageKey, CompletedLoad>,
// The placeholder image used when an image fails to load // The placeholder image used when an image fails to load
placeholder_image: Option<Arc<Image>>, placeholder_image: Arc<Image>,
// The URL used for the placeholder image // The URL used for the placeholder image
placeholder_url: ServoUrl, placeholder_url: ServoUrl,
@ -391,7 +391,7 @@ impl ImageCacheStore {
origin: ImmutableOrigin, origin: ImmutableOrigin,
cors_setting: Option<CorsSettings>, cors_setting: Option<CorsSettings>,
placeholder: UsePlaceholder, placeholder: UsePlaceholder,
) -> Option<Result<ImageOrMetadataAvailable, ImageState>> { ) -> Option<Result<(Arc<Image>, ServoUrl), ()>> {
self.completed_loads self.completed_loads
.get(&(url, origin, cors_setting)) .get(&(url, origin, cors_setting))
.map( .map(
@ -400,13 +400,10 @@ impl ImageCacheStore {
( (
&ImageResponse::PlaceholderLoaded(ref image, ref url), &ImageResponse::PlaceholderLoaded(ref image, ref url),
UsePlaceholder::Yes, UsePlaceholder::Yes,
) => Ok(ImageOrMetadataAvailable::ImageAvailable( ) => Ok((image.clone(), url.clone())),
image.clone(),
url.clone(),
)),
(&ImageResponse::PlaceholderLoaded(_, _), UsePlaceholder::No) | (&ImageResponse::PlaceholderLoaded(_, _), UsePlaceholder::No) |
(&ImageResponse::None, _) | (&ImageResponse::None, _) |
(&ImageResponse::MetadataLoaded(_), _) => Err(ImageState::LoadError), (&ImageResponse::MetadataLoaded(_), _) => Err(()),
}, },
) )
} }
@ -436,25 +433,36 @@ impl ImageCache for ImageCacheImpl {
store: Arc::new(Mutex::new(ImageCacheStore { store: Arc::new(Mutex::new(ImageCacheStore {
pending_loads: AllPendingLoads::new(), pending_loads: AllPendingLoads::new(),
completed_loads: HashMap::new(), completed_loads: HashMap::new(),
placeholder_image: get_placeholder_image(&webrender_api, &rippy_data).ok(), placeholder_image: get_placeholder_image(&webrender_api, &rippy_data),
placeholder_url: ServoUrl::parse("chrome://resources/rippy.png").unwrap(), placeholder_url: ServoUrl::parse("chrome://resources/rippy.png").unwrap(),
webrender_api: webrender_api, webrender_api: webrender_api,
})), })),
} }
} }
/// Return any available metadata or image for the given URL, fn get_image(
/// or an indication that the image is not yet available if it is in progress, &self,
/// or else reserve a slot in the cache for the URL if the consumer can request images. url: ServoUrl,
fn find_image_or_metadata( origin: ImmutableOrigin,
cors_setting: Option<CorsSettings>,
) -> Option<Arc<Image>> {
let store = self.store.lock().unwrap();
let result =
store.get_completed_image_if_available(url, origin, cors_setting, UsePlaceholder::No);
match result {
Some(Ok((img, _))) => Some(img),
_ => None,
}
}
fn get_cached_image_status(
&self, &self,
url: ServoUrl, url: ServoUrl,
origin: ImmutableOrigin, origin: ImmutableOrigin,
cors_setting: Option<CorsSettings>, cors_setting: Option<CorsSettings>,
use_placeholder: UsePlaceholder, use_placeholder: UsePlaceholder,
can_request: CanRequestImages, can_request: CanRequestImages,
) -> Result<ImageOrMetadataAvailable, ImageState> { ) -> ImageCacheResult {
debug!("Find image or metadata for {} ({:?})", url, origin);
let mut store = self.store.lock().unwrap(); let mut store = self.store.lock().unwrap();
if let Some(result) = store.get_completed_image_if_available( if let Some(result) = store.get_completed_image_if_available(
url.clone(), url.clone(),
@ -462,8 +470,18 @@ impl ImageCache for ImageCacheImpl {
cors_setting, cors_setting,
use_placeholder, use_placeholder,
) { ) {
debug!("{} is available", url); match result {
return result; Ok((image, image_url)) => {
debug!("{} is available", url);
return ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable(
image, image_url,
));
},
Err(()) => {
debug!("{} is not available", url);
return ImageCacheResult::LoadError;
},
}
} }
let decoded = { let decoded = {
@ -481,20 +499,22 @@ impl ImageCache for ImageCacheImpl {
}, },
(&None, &Some(ref meta)) => { (&None, &Some(ref meta)) => {
debug!("Metadata available for {} ({:?})", url, key); debug!("Metadata available for {} ({:?})", url, key);
return Ok(ImageOrMetadataAvailable::MetadataAvailable(meta.clone())); return ImageCacheResult::Available(
ImageOrMetadataAvailable::MetadataAvailable(meta.clone()),
);
}, },
(&Some(Err(_)), _) | (&None, &None) => { (&Some(Err(_)), _) | (&None, &None) => {
debug!("{} ({:?}) is still pending", url, key); debug!("{} ({:?}) is still pending", url, key);
return Err(ImageState::Pending(key)); return ImageCacheResult::Pending(key);
}, },
}, },
CacheResult::Miss(Some((key, _pl))) => { CacheResult::Miss(Some((key, _pl))) => {
debug!("Should be requesting {} ({:?})", url, key); debug!("Should be requesting {} ({:?})", url, key);
return Err(ImageState::NotRequested(key)); return ImageCacheResult::ReadyForRequest(key);
}, },
CacheResult::Miss(None) => { CacheResult::Miss(None) => {
debug!("Couldn't find an entry for {}", url); debug!("Couldn't find an entry for {}", url);
return Err(ImageState::LoadError); return ImageCacheResult::LoadError;
}, },
} }
}; };
@ -505,11 +525,50 @@ impl ImageCache for ImageCacheImpl {
// TODO: make this behaviour configurable according to the caller's needs. // TODO: make this behaviour configurable according to the caller's needs.
store.handle_decoder(decoded); store.handle_decoder(decoded);
match store.get_completed_image_if_available(url, origin, cors_setting, use_placeholder) { match store.get_completed_image_if_available(url, origin, cors_setting, use_placeholder) {
Some(result) => result, Some(Ok((image, image_url))) => ImageCacheResult::Available(
None => Err(ImageState::LoadError), ImageOrMetadataAvailable::ImageAvailable(image, image_url),
),
_ => ImageCacheResult::LoadError,
} }
} }
fn track_image(
&self,
url: ServoUrl,
origin: ImmutableOrigin,
cors_setting: Option<CorsSettings>,
sender: IpcSender<PendingImageResponse>,
use_placeholder: UsePlaceholder,
can_request: CanRequestImages,
) -> ImageCacheResult {
debug!("Track image for {} ({:?})", url, origin);
let cache_result = self.get_cached_image_status(
url.clone(),
origin.clone(),
cors_setting,
use_placeholder,
can_request,
);
match cache_result {
ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(_)) => {
let store = self.store.lock().unwrap();
let id = store
.pending_loads
.url_to_load_key
.get(&(url, origin, cors_setting))
.unwrap();
self.add_listener(*id, ImageResponder::new(sender, *id));
},
ImageCacheResult::Pending(id) | ImageCacheResult::ReadyForRequest(id) => {
self.add_listener(id, ImageResponder::new(sender, id));
},
_ => {},
}
cache_result
}
/// Add a new listener for the given pending image id. If the image is already present, /// Add a new listener for the given pending image id. If the image is already present,
/// the responder will still receive the expected response. /// the responder will still receive the expected response.
fn add_listener(&self, id: PendingImageId, listener: ImageResponder) { fn add_listener(&self, id: PendingImageId, listener: ImageResponder) {
@ -600,13 +659,8 @@ impl ImageCache for ImageCacheImpl {
Err(_) => { Err(_) => {
debug!("Processing error for {:?}", key); debug!("Processing error for {:?}", key);
let mut store = self.store.lock().unwrap(); let mut store = self.store.lock().unwrap();
match store.placeholder_image.clone() { let placeholder_image = store.placeholder_image.clone();
Some(placeholder_image) => store.complete_load( store.complete_load(id, LoadResult::PlaceholderLoaded(placeholder_image))
id,
LoadResult::PlaceholderLoaded(placeholder_image),
),
None => store.complete_load(id, LoadResult::None),
}
}, },
} }
}, },

View file

@ -73,14 +73,6 @@ pub enum ImageResponse {
None, None,
} }
/// The current state of an image in the cache.
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub enum ImageState {
Pending(PendingImageId),
LoadError,
NotRequested(PendingImageId),
}
/// The unique id for an image that has previously been requested. /// The unique id for an image that has previously been requested.
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)] #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
pub struct PendingImageId(pub u64); pub struct PendingImageId(pub u64);
@ -101,22 +93,50 @@ pub enum UsePlaceholder {
// ImageCache public API. // ImageCache public API.
// ====================================================================== // ======================================================================
pub enum ImageCacheResult {
Available(ImageOrMetadataAvailable),
LoadError,
Pending(PendingImageId),
ReadyForRequest(PendingImageId),
}
pub trait ImageCache: Sync + Send { pub trait ImageCache: Sync + Send {
fn new(webrender_api: WebrenderIpcSender) -> Self fn new(webrender_api: WebrenderIpcSender) -> Self
where where
Self: Sized; Self: Sized;
/// Return any available metadata or image for the given URL, /// Definitively check whether there is a cached, fully loaded image available.
/// or an indication that the image is not yet available if it is in progress, fn get_image(
/// or else reserve a slot in the cache for the URL if the consumer can request images. &self,
fn find_image_or_metadata( url: ServoUrl,
origin: ImmutableOrigin,
cors_setting: Option<CorsSettings>,
) -> Option<Arc<Image>>;
fn get_cached_image_status(
&self, &self,
url: ServoUrl, url: ServoUrl,
origin: ImmutableOrigin, origin: ImmutableOrigin,
cors_setting: Option<CorsSettings>, cors_setting: Option<CorsSettings>,
use_placeholder: UsePlaceholder, use_placeholder: UsePlaceholder,
can_request: CanRequestImages, can_request: CanRequestImages,
) -> Result<ImageOrMetadataAvailable, ImageState>; ) -> ImageCacheResult;
/// Add a listener for the provided pending image id, eventually called by
/// ImageCacheStore::complete_load.
/// If only metadata is available, Available(ImageOrMetadataAvailable) will
/// be returned.
/// If Available(ImageOrMetadataAvailable::Image) or LoadError is the final value,
/// the provided listener will be dropped (consumed & not added to PendingLoad).
fn track_image(
&self,
url: ServoUrl,
origin: ImmutableOrigin,
cors_setting: Option<CorsSettings>,
sender: IpcSender<PendingImageResponse>,
use_placeholder: UsePlaceholder,
can_request: CanRequestImages,
) -> ImageCacheResult;
/// Add a new listener for the given pending image id. If the image is already present, /// Add a new listener for the given pending image id. If the image is already present,
/// the responder will still receive the expected response. /// the responder will still receive the expected response.

View file

@ -38,12 +38,7 @@ use euclid::{
vec2, vec2,
}; };
use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::ipc::{self, IpcSender};
use net_traits::image_cache::CanRequestImages; use net_traits::image_cache::{ImageCache, ImageResponse};
use net_traits::image_cache::ImageCache;
use net_traits::image_cache::ImageOrMetadataAvailable;
use net_traits::image_cache::ImageResponse;
use net_traits::image_cache::ImageState;
use net_traits::image_cache::UsePlaceholder;
use net_traits::request::CorsSettings; use net_traits::request::CorsSettings;
use pixels::PixelFormat; use pixels::PixelFormat;
use profile_traits::ipc as profiled_ipc; use profile_traits::ipc as profiled_ipc;
@ -261,19 +256,12 @@ impl CanvasState {
url: ServoUrl, url: ServoUrl,
cors_setting: Option<CorsSettings>, cors_setting: Option<CorsSettings>,
) -> ImageResponse { ) -> ImageResponse {
let response = self.image_cache.find_image_or_metadata( match self
url.clone(), .image_cache
self.origin.clone(), .get_image(url.clone(), self.origin.clone(), cors_setting)
cors_setting, {
UsePlaceholder::No, Some(image) => ImageResponse::Loaded(image, url),
CanRequestImages::No, None => {
);
match response {
Ok(ImageOrMetadataAvailable::ImageAvailable(image, url)) => {
ImageResponse::Loaded(image, url)
},
Err(ImageState::Pending(_)) => ImageResponse::None,
_ => {
// Rather annoyingly, we get the same response back from // Rather annoyingly, we get the same response back from
// A load which really failed and from a load which hasn't started yet. // A load which really failed and from a load which hasn't started yet.
self.missing_image_urls.borrow_mut().push(url); self.missing_image_urls.borrow_mut().push(url);

View file

@ -432,8 +432,7 @@ impl<'a> From<&'a WebGLContextAttributes> for GLContextAttributes {
pub mod utils { pub mod utils {
use crate::dom::window::Window; use crate::dom::window::Window;
use net_traits::image_cache::CanRequestImages; use net_traits::image_cache::ImageResponse;
use net_traits::image_cache::{ImageOrMetadataAvailable, ImageResponse, UsePlaceholder};
use net_traits::request::CorsSettings; use net_traits::request::CorsSettings;
use servo_url::ServoUrl; use servo_url::ServoUrl;
@ -443,18 +442,15 @@ pub mod utils {
cors_setting: Option<CorsSettings>, cors_setting: Option<CorsSettings>,
) -> ImageResponse { ) -> ImageResponse {
let image_cache = window.image_cache(); let image_cache = window.image_cache();
let response = image_cache.find_image_or_metadata( let result = image_cache.get_image(
url.into(), url.clone(),
window.origin().immutable().clone(), window.origin().immutable().clone(),
cors_setting, cors_setting,
UsePlaceholder::No,
CanRequestImages::No,
); );
match response {
Ok(ImageOrMetadataAvailable::ImageAvailable(image, url)) => { match result {
ImageResponse::Loaded(image, url) Some(image) => ImageResponse::Loaded(image, url),
}, None => ImageResponse::None,
_ => ImageResponse::None,
} }
} }
} }

View file

@ -43,7 +43,7 @@ use crate::dom::values::UNSIGNED_LONG_MAX;
use crate::dom::virtualmethods::VirtualMethods; use crate::dom::virtualmethods::VirtualMethods;
use crate::dom::window::Window; use crate::dom::window::Window;
use crate::fetch::create_a_potential_cors_request; use crate::fetch::create_a_potential_cors_request;
use crate::image_listener::{add_cache_listener_for_element, ImageCacheListener}; use crate::image_listener::{generate_cache_listener_for_element, ImageCacheListener};
use crate::microtask::{Microtask, MicrotaskRunnable}; use crate::microtask::{Microtask, MicrotaskRunnable};
use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener};
use crate::script_thread::ScriptThread; use crate::script_thread::ScriptThread;
@ -54,13 +54,15 @@ use dom_struct::dom_struct;
use euclid::Point2D; use euclid::Point2D;
use html5ever::{LocalName, Prefix, QualName}; use html5ever::{LocalName, Prefix, QualName};
use ipc_channel::ipc; use ipc_channel::ipc;
use ipc_channel::ipc::IpcSender;
use ipc_channel::router::ROUTER; use ipc_channel::router::ROUTER;
use mime::{self, Mime}; use mime::{self, Mime};
use msg::constellation_msg::PipelineId; use msg::constellation_msg::PipelineId;
use net_traits::image::base::{Image, ImageMetadata}; use net_traits::image::base::{Image, ImageMetadata};
use net_traits::image_cache::UsePlaceholder; use net_traits::image_cache::{
use net_traits::image_cache::{CanRequestImages, CorsStatus, ImageCache, ImageOrMetadataAvailable}; CanRequestImages, CorsStatus, ImageCache, ImageCacheResult, ImageOrMetadataAvailable,
use net_traits::image_cache::{ImageResponder, ImageResponse, ImageState, PendingImageId}; ImageResponse, PendingImageId, PendingImageResponse, UsePlaceholder,
};
use net_traits::request::{CorsSettings, Destination, Initiator, RequestBuilder}; use net_traits::request::{CorsSettings, Destination, Initiator, RequestBuilder};
use net_traits::{FetchMetadata, FetchResponseListener, FetchResponseMsg, NetworkError}; use net_traits::{FetchMetadata, FetchResponseListener, FetchResponseMsg, NetworkError};
use net_traits::{ReferrerPolicy, ResourceFetchTiming, ResourceTimingType}; use net_traits::{ReferrerPolicy, ResourceFetchTiming, ResourceTimingType};
@ -317,35 +319,27 @@ impl HTMLImageElement {
fn fetch_image(&self, img_url: &ServoUrl) { fn fetch_image(&self, img_url: &ServoUrl) {
let window = window_from_node(self); let window = window_from_node(self);
let image_cache = window.image_cache(); let image_cache = window.image_cache();
let response = image_cache.find_image_or_metadata( let sender = generate_cache_listener_for_element(self);
img_url.clone().into(), let cache_result = image_cache.track_image(
img_url.clone(),
window.origin().immutable().clone(), window.origin().immutable().clone(),
cors_setting_for_element(self.upcast()), cors_setting_for_element(self.upcast()),
sender,
UsePlaceholder::Yes, UsePlaceholder::Yes,
CanRequestImages::Yes, CanRequestImages::Yes,
); );
match response {
Ok(ImageOrMetadataAvailable::ImageAvailable(image, url)) => {
self.process_image_response(ImageResponse::Loaded(image, url));
},
Ok(ImageOrMetadataAvailable::MetadataAvailable(m)) => { match cache_result {
self.process_image_response(ImageResponse::MetadataLoaded(m)); ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable(image, url)) => {
self.process_image_response(ImageResponse::Loaded(image, url))
}, },
ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(m)) => {
Err(ImageState::Pending(id)) => { self.process_image_response(ImageResponse::MetadataLoaded(m))
add_cache_listener_for_element(image_cache, id, self);
}, },
ImageCacheResult::Pending(_) => (),
Err(ImageState::LoadError) => { ImageCacheResult::ReadyForRequest(id) => self.fetch_request(img_url, id),
self.process_image_response(ImageResponse::None); ImageCacheResult::LoadError => self.process_image_response(ImageResponse::None),
}, };
Err(ImageState::NotRequested(id)) => {
add_cache_listener_for_element(image_cache, id, self);
self.fetch_request(img_url, id);
},
}
} }
fn fetch_request(&self, img_url: &ServoUrl, id: PendingImageId) { fn fetch_request(&self, img_url: &ServoUrl, id: PendingImageId) {
@ -943,14 +937,13 @@ impl HTMLImageElement {
if let Some(src) = selected_source { if let Some(src) = selected_source {
if let Ok(img_url) = base_url.join(&src) { if let Ok(img_url) = base_url.join(&src) {
let image_cache = window.image_cache(); let image_cache = window.image_cache();
let response = image_cache.find_image_or_metadata( let response = image_cache.get_image(
img_url.clone().into(), img_url.clone(),
window.origin().immutable().clone(), window.origin().immutable().clone(),
cors_setting_for_element(self.upcast()), cors_setting_for_element(self.upcast()),
UsePlaceholder::No,
CanRequestImages::No,
); );
if let Ok(ImageOrMetadataAvailable::ImageAvailable(image, url)) = response {
if let Some(image) = response {
// Cancel any outstanding tasks that were queued before the src was // Cancel any outstanding tasks that were queued before the src was
// set on this element. // set on this element.
self.generation.set(self.generation.get() + 1); self.generation.set(self.generation.get() + 1);
@ -963,7 +956,7 @@ impl HTMLImageElement {
self.abort_request(State::CompletelyAvailable, ImageRequestPhase::Current); self.abort_request(State::CompletelyAvailable, ImageRequestPhase::Current);
self.abort_request(State::Unavailable, ImageRequestPhase::Pending); self.abort_request(State::Unavailable, ImageRequestPhase::Pending);
let mut current_request = self.current_request.borrow_mut(); let mut current_request = self.current_request.borrow_mut();
current_request.final_url = Some(url); current_request.final_url = Some(img_url.clone());
current_request.image = Some(image.clone()); current_request.image = Some(image.clone());
current_request.metadata = Some(metadata); current_request.metadata = Some(metadata);
// Step 6.3.6 // Step 6.3.6
@ -1010,13 +1003,12 @@ impl HTMLImageElement {
/// Step 2-12 of https://html.spec.whatwg.org/multipage/#img-environment-changes /// Step 2-12 of https://html.spec.whatwg.org/multipage/#img-environment-changes
fn react_to_environment_changes_sync_steps(&self, generation: u32) { fn react_to_environment_changes_sync_steps(&self, generation: u32) {
// TODO reduce duplicacy of this code // TODO reduce duplicacy of this code
fn add_cache_listener_for_element(
image_cache: Arc<dyn ImageCache>, fn generate_cache_listener_for_element(
id: PendingImageId,
elem: &HTMLImageElement, elem: &HTMLImageElement,
selected_source: String, selected_source: String,
selected_pixel_density: f64, selected_pixel_density: f64,
) { ) -> IpcSender<PendingImageResponse> {
let trusted_node = Trusted::new(elem); let trusted_node = Trusted::new(elem);
let (responder_sender, responder_receiver) = ipc::channel().unwrap(); let (responder_sender, responder_receiver) = ipc::channel().unwrap();
@ -1025,27 +1017,30 @@ impl HTMLImageElement {
.task_manager() .task_manager()
.networking_task_source_with_canceller(); .networking_task_source_with_canceller();
let generation = elem.generation.get(); let generation = elem.generation.get();
ROUTER.add_route(responder_receiver.to_opaque(), Box::new(move |message| { ROUTER.add_route(
debug!("Got image {:?}", message); responder_receiver.to_opaque(),
// Return the image via a message to the script thread, which marks Box::new(move |message| {
// the element as dirty and triggers a reflow. debug!("Got image {:?}", message);
let element = trusted_node.clone(); // Return the image via a message to the script thread, which marks
let image = message.to().unwrap(); // the element as dirty and triggers a reflow.
let selected_source_clone = selected_source.clone(); let element = trusted_node.clone();
let _ = task_source.queue_with_canceller( let image = message.to().unwrap();
task!(process_image_response_for_environment_change: move || { let selected_source_clone = selected_source.clone();
let element = element.root(); let _ = task_source.queue_with_canceller(
// Ignore any image response for a previous request that has been discarded. task!(process_image_response_for_environment_change: move || {
if generation == element.generation.get() { let element = element.root();
element.process_image_response_for_environment_change(image, // Ignore any image response for a previous request that has been discarded.
USVString::from(selected_source_clone), generation, selected_pixel_density); if generation == element.generation.get() {
} element.process_image_response_for_environment_change(image,
}), USVString::from(selected_source_clone), generation, selected_pixel_density);
&canceller, }
); }),
})); &canceller,
);
}),
);
image_cache.add_listener(id, ImageResponder::new(responder_sender, id)); responder_sender
} }
let elem = self.upcast::<Element>(); let elem = self.upcast::<Element>();
@ -1099,25 +1094,32 @@ impl HTMLImageElement {
let window = window_from_node(self); let window = window_from_node(self);
let image_cache = window.image_cache(); let image_cache = window.image_cache();
// Step 14 // Step 14
let response = image_cache.find_image_or_metadata( let sender = generate_cache_listener_for_element(
img_url.clone().into(), self,
selected_source.0.clone(),
selected_pixel_density,
);
let cache_result = image_cache.track_image(
img_url.clone(),
window.origin().immutable().clone(), window.origin().immutable().clone(),
cors_setting_for_element(self.upcast()), cors_setting_for_element(self.upcast()),
sender,
UsePlaceholder::No, UsePlaceholder::No,
CanRequestImages::Yes, CanRequestImages::Yes,
); );
match response {
Ok(ImageOrMetadataAvailable::ImageAvailable(_image, _url)) => { match cache_result {
ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable(_image, _url)) => {
// Step 15 // Step 15
self.finish_reacting_to_environment_change( self.finish_reacting_to_environment_change(
selected_source, selected_source,
generation, generation,
selected_pixel_density, selected_pixel_density,
); )
}, },
ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(m)) => {
Ok(ImageOrMetadataAvailable::MetadataAvailable(m)) => {
self.process_image_response_for_environment_change( self.process_image_response_for_environment_change(
ImageResponse::MetadataLoaded(m), ImageResponse::MetadataLoaded(m),
selected_source, selected_source,
@ -1125,18 +1127,7 @@ impl HTMLImageElement {
selected_pixel_density, selected_pixel_density,
); );
}, },
ImageCacheResult::LoadError => {
Err(ImageState::Pending(id)) => {
add_cache_listener_for_element(
image_cache.clone(),
id,
self,
selected_source.0,
selected_pixel_density,
);
},
Err(ImageState::LoadError) => {
self.process_image_response_for_environment_change( self.process_image_response_for_environment_change(
ImageResponse::None, ImageResponse::None,
selected_source, selected_source,
@ -1144,17 +1135,8 @@ impl HTMLImageElement {
selected_pixel_density, selected_pixel_density,
); );
}, },
ImageCacheResult::ReadyForRequest(id) => self.fetch_request(&img_url, id),
Err(ImageState::NotRequested(id)) => { ImageCacheResult::Pending(_) => (),
add_cache_listener_for_element(
image_cache,
id,
self,
selected_source.0,
selected_pixel_density,
);
self.fetch_request(&img_url, id);
},
} }
} }

View file

@ -19,16 +19,17 @@ use crate::dom::node::{document_from_node, window_from_node, Node};
use crate::dom::performanceresourcetiming::InitiatorType; use crate::dom::performanceresourcetiming::InitiatorType;
use crate::dom::virtualmethods::VirtualMethods; use crate::dom::virtualmethods::VirtualMethods;
use crate::fetch::FetchCanceller; use crate::fetch::FetchCanceller;
use crate::image_listener::{add_cache_listener_for_element, ImageCacheListener}; use crate::image_listener::{generate_cache_listener_for_element, ImageCacheListener};
use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener};
use dom_struct::dom_struct; use dom_struct::dom_struct;
use euclid::default::Size2D; use euclid::default::Size2D;
use html5ever::{LocalName, Prefix}; use html5ever::{LocalName, Prefix};
use ipc_channel::ipc; use ipc_channel::ipc;
use ipc_channel::router::ROUTER; use ipc_channel::router::ROUTER;
use net_traits::image_cache::UsePlaceholder; use net_traits::image_cache::{
use net_traits::image_cache::{CanRequestImages, ImageCache, ImageOrMetadataAvailable}; CanRequestImages, ImageCache, ImageCacheResult, ImageOrMetadataAvailable, ImageResponse,
use net_traits::image_cache::{ImageResponse, ImageState, PendingImageId}; PendingImageId, UsePlaceholder,
};
use net_traits::request::{CredentialsMode, Destination, RequestBuilder}; use net_traits::request::{CredentialsMode, Destination, RequestBuilder};
use net_traits::{ use net_traits::{
CoreResourceMsg, FetchChannels, FetchMetadata, FetchResponseListener, FetchResponseMsg, CoreResourceMsg, FetchChannels, FetchMetadata, FetchResponseListener, FetchResponseMsg,
@ -155,27 +156,23 @@ impl HTMLVideoElement {
// network activity as possible. // network activity as possible.
let window = window_from_node(self); let window = window_from_node(self);
let image_cache = window.image_cache(); let image_cache = window.image_cache();
let response = image_cache.find_image_or_metadata( let sender = generate_cache_listener_for_element(self);
poster_url.clone().into(), let cache_result = image_cache.track_image(
poster_url.clone(),
window.origin().immutable().clone(), window.origin().immutable().clone(),
None, None,
sender,
UsePlaceholder::No, UsePlaceholder::No,
CanRequestImages::Yes, CanRequestImages::Yes,
); );
match response {
Ok(ImageOrMetadataAvailable::ImageAvailable(image, url)) => {
self.process_image_response(ImageResponse::Loaded(image, url));
},
Err(ImageState::Pending(id)) => { match cache_result {
add_cache_listener_for_element(image_cache, id, self); ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable(img, url)) => {
self.process_image_response(ImageResponse::Loaded(img, url));
}, },
ImageCacheResult::ReadyForRequest(id) => {
Err(ImageState::NotRequested(id)) => { self.do_fetch_poster_frame(poster_url, id, cancel_receiver)
add_cache_listener_for_element(image_cache, id, self);
self.do_fetch_poster_frame(poster_url, id, cancel_receiver);
}, },
_ => (), _ => (),
} }
} }

View file

@ -8,20 +8,20 @@ use crate::dom::bindings::reflector::DomObject;
use crate::dom::node::{window_from_node, Node}; use crate::dom::node::{window_from_node, Node};
use crate::task_source::TaskSource; use crate::task_source::TaskSource;
use ipc_channel::ipc; use ipc_channel::ipc;
use ipc_channel::ipc::IpcSender;
use ipc_channel::router::ROUTER; use ipc_channel::router::ROUTER;
use net_traits::image_cache::{ImageCache, ImageResponder, ImageResponse, PendingImageId}; use net_traits::image_cache::{ImageResponse, PendingImageResponse};
use std::sync::Arc;
pub trait ImageCacheListener { pub trait ImageCacheListener {
fn generation_id(&self) -> u32; fn generation_id(&self) -> u32;
fn process_image_response(&self, response: ImageResponse); fn process_image_response(&self, response: ImageResponse);
} }
pub fn add_cache_listener_for_element<T: ImageCacheListener + DerivedFrom<Node> + DomObject>( pub fn generate_cache_listener_for_element<
image_cache: Arc<dyn ImageCache>, T: ImageCacheListener + DerivedFrom<Node> + DomObject,
id: PendingImageId, >(
elem: &T, elem: &T,
) { ) -> IpcSender<PendingImageResponse> {
let trusted_node = Trusted::new(elem); let trusted_node = Trusted::new(elem);
let (responder_sender, responder_receiver) = ipc::channel().unwrap(); let (responder_sender, responder_receiver) = ipc::channel().unwrap();
@ -49,5 +49,5 @@ pub fn add_cache_listener_for_element<T: ImageCacheListener + DerivedFrom<Node>
}), }),
); );
image_cache.add_listener(id, ImageResponder::new(responder_sender, id)); responder_sender
} }