mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +01:00
Double key image cache by requesting origin, and store CORS status with cached images.
This commit is contained in:
parent
ea46008288
commit
81a67aed9e
11 changed files with 132 additions and 57 deletions
|
@ -22,6 +22,7 @@ use ipc_channel::ipc;
|
||||||
use libc::c_void;
|
use libc::c_void;
|
||||||
use msg::constellation_msg::{PipelineId, PipelineIndex, PipelineNamespaceId};
|
use msg::constellation_msg::{PipelineId, PipelineIndex, PipelineNamespaceId};
|
||||||
use net_traits::image::base::Image;
|
use net_traits::image::base::Image;
|
||||||
|
use net_traits::image_cache::CorsStatus;
|
||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
#[cfg(feature = "gl")]
|
#[cfg(feature = "gl")]
|
||||||
use pixels::PixelFormat;
|
use pixels::PixelFormat;
|
||||||
|
@ -1384,6 +1385,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
|
||||||
format: PixelFormat::RGB8,
|
format: PixelFormat::RGB8,
|
||||||
bytes: ipc::IpcSharedMemory::from_bytes(&*img),
|
bytes: ipc::IpcSharedMemory::from_bytes(&*img),
|
||||||
id: None,
|
id: None,
|
||||||
|
cors_status: CorsStatus::Safe,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
#[cfg(feature = "gl")]
|
#[cfg(feature = "gl")]
|
||||||
|
|
|
@ -18,7 +18,7 @@ use script_layout_interface::{PendingImage, PendingImageState};
|
||||||
use script_traits::Painter;
|
use script_traits::Painter;
|
||||||
use script_traits::UntrustedNodeAddress;
|
use script_traits::UntrustedNodeAddress;
|
||||||
use servo_atoms::Atom;
|
use servo_atoms::Atom;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::{ImmutableOrigin, ServoUrl};
|
||||||
use std::cell::{RefCell, RefMut};
|
use std::cell::{RefCell, RefMut};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::hash::BuildHasherDefault;
|
use std::hash::BuildHasherDefault;
|
||||||
|
@ -60,6 +60,9 @@ pub struct LayoutContext<'a> {
|
||||||
/// The pipeline id of this LayoutContext.
|
/// The pipeline id of this LayoutContext.
|
||||||
pub id: PipelineId,
|
pub id: PipelineId,
|
||||||
|
|
||||||
|
/// The origin of this layout context.
|
||||||
|
pub origin: ImmutableOrigin,
|
||||||
|
|
||||||
/// Bits shared by the layout and style system.
|
/// Bits shared by the layout and style system.
|
||||||
pub style_context: SharedStyleContext<'a>,
|
pub style_context: SharedStyleContext<'a>,
|
||||||
|
|
||||||
|
@ -120,9 +123,12 @@ impl<'a> LayoutContext<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// See if the image is already available
|
// See if the image is already available
|
||||||
let result =
|
let result = self.image_cache.find_image_or_metadata(
|
||||||
self.image_cache
|
url.clone(),
|
||||||
.find_image_or_metadata(url.clone(), use_placeholder, can_request);
|
self.origin.clone(),
|
||||||
|
use_placeholder,
|
||||||
|
can_request,
|
||||||
|
);
|
||||||
match result {
|
match result {
|
||||||
Ok(image_or_metadata) => Some(image_or_metadata),
|
Ok(image_or_metadata) => Some(image_or_metadata),
|
||||||
// Image failed to load, so just return nothing
|
// Image failed to load, so just return nothing
|
||||||
|
|
|
@ -659,6 +659,7 @@ impl LayoutThread {
|
||||||
|
|
||||||
LayoutContext {
|
LayoutContext {
|
||||||
id: self.id,
|
id: self.id,
|
||||||
|
origin: self.url.origin(),
|
||||||
style_context: SharedStyleContext {
|
style_context: SharedStyleContext {
|
||||||
stylist: &self.stylist,
|
stylist: &self.stylist,
|
||||||
options: GLOBAL_STYLE_DATA.options.clone(),
|
options: GLOBAL_STYLE_DATA.options.clone(),
|
||||||
|
|
|
@ -5,12 +5,12 @@
|
||||||
use embedder_traits::resources::{self, Resource};
|
use embedder_traits::resources::{self, Resource};
|
||||||
use immeta::load_from_buf;
|
use immeta::load_from_buf;
|
||||||
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, ImageCache, ImageResponder};
|
use net_traits::image_cache::{CanRequestImages, CorsStatus, ImageCache, ImageResponder};
|
||||||
use net_traits::image_cache::{ImageOrMetadataAvailable, ImageResponse, ImageState};
|
use net_traits::image_cache::{ImageOrMetadataAvailable, ImageResponse, ImageState};
|
||||||
use net_traits::image_cache::{PendingImageId, UsePlaceholder};
|
use net_traits::image_cache::{PendingImageId, UsePlaceholder};
|
||||||
use net_traits::{FetchMetadata, FetchResponseMsg, NetworkError};
|
use net_traits::{FetchMetadata, FetchResponseMsg, FilteredMetadata, NetworkError};
|
||||||
use pixels::PixelFormat;
|
use pixels::PixelFormat;
|
||||||
use servo_url::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::io;
|
||||||
|
@ -33,8 +33,8 @@ use webrender_api::units::DeviceIntSize;
|
||||||
// Helper functions.
|
// Helper functions.
|
||||||
// ======================================================================
|
// ======================================================================
|
||||||
|
|
||||||
fn decode_bytes_sync(key: LoadKey, bytes: &[u8]) -> DecoderMsg {
|
fn decode_bytes_sync(key: LoadKey, bytes: &[u8], cors: CorsStatus) -> DecoderMsg {
|
||||||
let image = load_from_memory(bytes);
|
let image = load_from_memory(bytes, cors);
|
||||||
DecoderMsg {
|
DecoderMsg {
|
||||||
key: key,
|
key: key,
|
||||||
image: image,
|
image: image,
|
||||||
|
@ -45,7 +45,7 @@ fn get_placeholder_image(
|
||||||
webrender_api: &webrender_api::RenderApi,
|
webrender_api: &webrender_api::RenderApi,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
) -> io::Result<Arc<Image>> {
|
) -> io::Result<Arc<Image>> {
|
||||||
let mut image = load_from_memory(&data).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))
|
Ok(Arc::new(image))
|
||||||
}
|
}
|
||||||
|
@ -99,9 +99,9 @@ struct AllPendingLoads {
|
||||||
// for performance reasons.
|
// for performance reasons.
|
||||||
loads: HashMap<LoadKey, PendingLoad>,
|
loads: HashMap<LoadKey, PendingLoad>,
|
||||||
|
|
||||||
// Get a load key from its url. Used ony when starting and
|
// Get a load key from its url and requesting origin. Used ony when starting and
|
||||||
// finishing a load or when adding a new listener.
|
// finishing a load or when adding a new listener.
|
||||||
url_to_load_key: HashMap<ServoUrl, LoadKey>,
|
url_to_load_key: HashMap<(ServoUrl, ImmutableOrigin), LoadKey>,
|
||||||
|
|
||||||
// A counter used to generate instances of LoadKey
|
// A counter used to generate instances of LoadKey
|
||||||
keygen: LoadKeyGenerator,
|
keygen: LoadKeyGenerator,
|
||||||
|
@ -123,7 +123,9 @@ impl AllPendingLoads {
|
||||||
|
|
||||||
fn remove(&mut self, key: &LoadKey) -> Option<PendingLoad> {
|
fn remove(&mut self, key: &LoadKey) -> Option<PendingLoad> {
|
||||||
self.loads.remove(key).and_then(|pending_load| {
|
self.loads.remove(key).and_then(|pending_load| {
|
||||||
self.url_to_load_key.remove(&pending_load.url).unwrap();
|
self.url_to_load_key
|
||||||
|
.remove(&(pending_load.url.clone(), pending_load.load_origin.clone()))
|
||||||
|
.unwrap();
|
||||||
Some(pending_load)
|
Some(pending_load)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -131,9 +133,10 @@ impl AllPendingLoads {
|
||||||
fn get_cached<'a>(
|
fn get_cached<'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
url: ServoUrl,
|
url: ServoUrl,
|
||||||
|
origin: ImmutableOrigin,
|
||||||
can_request: CanRequestImages,
|
can_request: CanRequestImages,
|
||||||
) -> CacheResult<'a> {
|
) -> CacheResult<'a> {
|
||||||
match self.url_to_load_key.entry(url.clone()) {
|
match self.url_to_load_key.entry((url.clone(), origin.clone())) {
|
||||||
Occupied(url_entry) => {
|
Occupied(url_entry) => {
|
||||||
let load_key = url_entry.get();
|
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())
|
||||||
|
@ -146,7 +149,7 @@ impl AllPendingLoads {
|
||||||
let load_key = self.keygen.next();
|
let load_key = self.keygen.next();
|
||||||
url_entry.insert(load_key);
|
url_entry.insert(load_key);
|
||||||
|
|
||||||
let pending_load = PendingLoad::new(url);
|
let pending_load = PendingLoad::new(url, origin);
|
||||||
match self.loads.entry(load_key) {
|
match self.loads.entry(load_key) {
|
||||||
Occupied(_) => unreachable!(),
|
Occupied(_) => unreachable!(),
|
||||||
Vacant(load_entry) => {
|
Vacant(load_entry) => {
|
||||||
|
@ -251,33 +254,44 @@ enum LoadResult {
|
||||||
/// Represents an image that is either being loaded
|
/// Represents an image that is either being loaded
|
||||||
/// by the resource thread, or decoded by a worker thread.
|
/// by the resource thread, or decoded by a worker thread.
|
||||||
struct PendingLoad {
|
struct PendingLoad {
|
||||||
// The bytes loaded so far. Reset to an empty vector once loading
|
/// The bytes loaded so far. Reset to an empty vector once loading
|
||||||
// is complete and the buffer has been transmitted to the decoder.
|
/// is complete and the buffer has been transmitted to the decoder.
|
||||||
bytes: ImageBytes,
|
bytes: ImageBytes,
|
||||||
|
|
||||||
// Image metadata, if available.
|
/// Image metadata, if available.
|
||||||
metadata: Option<ImageMetadata>,
|
metadata: Option<ImageMetadata>,
|
||||||
|
|
||||||
// Once loading is complete, the result of the operation.
|
/// Once loading is complete, the result of the operation.
|
||||||
result: Option<Result<(), NetworkError>>,
|
result: Option<Result<(), NetworkError>>,
|
||||||
|
|
||||||
|
/// The listeners that are waiting for this response to complete.
|
||||||
listeners: Vec<ImageResponder>,
|
listeners: Vec<ImageResponder>,
|
||||||
|
|
||||||
// The url being loaded. Do not forget that this may be several Mb
|
/// The url being loaded. Do not forget that this may be several Mb
|
||||||
// if we are loading a data: url.
|
/// if we are loading a data: url.
|
||||||
url: ServoUrl,
|
url: ServoUrl,
|
||||||
|
|
||||||
|
/// The origin that requested this load.
|
||||||
|
load_origin: ImmutableOrigin,
|
||||||
|
|
||||||
|
/// The CORS status of this image response.
|
||||||
|
cors_status: CorsStatus,
|
||||||
|
|
||||||
|
/// The URL of the final response that contains a body.
|
||||||
final_url: Option<ServoUrl>,
|
final_url: Option<ServoUrl>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PendingLoad {
|
impl PendingLoad {
|
||||||
fn new(url: ServoUrl) -> PendingLoad {
|
fn new(url: ServoUrl, load_origin: ImmutableOrigin) -> PendingLoad {
|
||||||
PendingLoad {
|
PendingLoad {
|
||||||
bytes: ImageBytes::InProgress(vec![]),
|
bytes: ImageBytes::InProgress(vec![]),
|
||||||
metadata: None,
|
metadata: None,
|
||||||
result: None,
|
result: None,
|
||||||
listeners: vec![],
|
listeners: vec![],
|
||||||
url: url,
|
url: url,
|
||||||
|
load_origin,
|
||||||
final_url: None,
|
final_url: None,
|
||||||
|
cors_status: CorsStatus::Unsafe,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,7 +308,7 @@ struct ImageCacheStore {
|
||||||
pending_loads: AllPendingLoads,
|
pending_loads: AllPendingLoads,
|
||||||
|
|
||||||
// Images that have finished loading (successful or not)
|
// Images that have finished loading (successful or not)
|
||||||
completed_loads: HashMap<ServoUrl, CompletedLoad>,
|
completed_loads: HashMap<(ServoUrl, ImmutableOrigin), 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: Option<Arc<Image>>,
|
||||||
|
@ -331,8 +345,10 @@ impl ImageCacheStore {
|
||||||
};
|
};
|
||||||
|
|
||||||
let completed_load = CompletedLoad::new(image_response.clone(), key);
|
let completed_load = CompletedLoad::new(image_response.clone(), key);
|
||||||
self.completed_loads
|
self.completed_loads.insert(
|
||||||
.insert(pending_load.url.into(), completed_load);
|
(pending_load.url.into(), pending_load.load_origin),
|
||||||
|
completed_load,
|
||||||
|
);
|
||||||
|
|
||||||
for listener in pending_load.listeners {
|
for listener in pending_load.listeners {
|
||||||
listener.respond(image_response.clone());
|
listener.respond(image_response.clone());
|
||||||
|
@ -343,20 +359,27 @@ impl ImageCacheStore {
|
||||||
/// or the complete load is not fully decoded or is unavailable.
|
/// or the complete load is not fully decoded or is unavailable.
|
||||||
fn get_completed_image_if_available(
|
fn get_completed_image_if_available(
|
||||||
&self,
|
&self,
|
||||||
url: &ServoUrl,
|
url: ServoUrl,
|
||||||
|
origin: ImmutableOrigin,
|
||||||
placeholder: UsePlaceholder,
|
placeholder: UsePlaceholder,
|
||||||
) -> Option<Result<ImageOrMetadataAvailable, ImageState>> {
|
) -> Option<Result<ImageOrMetadataAvailable, ImageState>> {
|
||||||
self.completed_loads.get(url).map(|completed_load| {
|
self.completed_loads
|
||||||
match (&completed_load.image_response, placeholder) {
|
.get(&(url, origin))
|
||||||
(&ImageResponse::Loaded(ref image, ref url), _) |
|
.map(
|
||||||
(&ImageResponse::PlaceholderLoaded(ref image, ref url), UsePlaceholder::Yes) => Ok(
|
|completed_load| match (&completed_load.image_response, placeholder) {
|
||||||
ImageOrMetadataAvailable::ImageAvailable(image.clone(), url.clone()),
|
(&ImageResponse::Loaded(ref image, ref url), _) |
|
||||||
),
|
(
|
||||||
(&ImageResponse::PlaceholderLoaded(_, _), UsePlaceholder::No) |
|
&ImageResponse::PlaceholderLoaded(ref image, ref url),
|
||||||
(&ImageResponse::None, _) |
|
UsePlaceholder::Yes,
|
||||||
(&ImageResponse::MetadataLoaded(_), _) => Err(ImageState::LoadError),
|
) => Ok(ImageOrMetadataAvailable::ImageAvailable(
|
||||||
}
|
image.clone(),
|
||||||
})
|
url.clone(),
|
||||||
|
)),
|
||||||
|
(&ImageResponse::PlaceholderLoaded(_, _), UsePlaceholder::No) |
|
||||||
|
(&ImageResponse::None, _) |
|
||||||
|
(&ImageResponse::MetadataLoaded(_), _) => Err(ImageState::LoadError),
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle a message from one of the decoder worker threads or from a sync
|
/// Handle a message from one of the decoder worker threads or from a sync
|
||||||
|
@ -397,23 +420,28 @@ impl ImageCache for ImageCacheImpl {
|
||||||
fn find_image_or_metadata(
|
fn find_image_or_metadata(
|
||||||
&self,
|
&self,
|
||||||
url: ServoUrl,
|
url: ServoUrl,
|
||||||
|
origin: ImmutableOrigin,
|
||||||
use_placeholder: UsePlaceholder,
|
use_placeholder: UsePlaceholder,
|
||||||
can_request: CanRequestImages,
|
can_request: CanRequestImages,
|
||||||
) -> Result<ImageOrMetadataAvailable, ImageState> {
|
) -> Result<ImageOrMetadataAvailable, ImageState> {
|
||||||
debug!("Find image or metadata for {}", url);
|
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(&url, use_placeholder) {
|
if let Some(result) =
|
||||||
|
store.get_completed_image_if_available(url.clone(), origin.clone(), use_placeholder)
|
||||||
|
{
|
||||||
debug!("{} is available", url);
|
debug!("{} is available", url);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
let decoded = {
|
let decoded = {
|
||||||
let result = store.pending_loads.get_cached(url.clone(), can_request);
|
let result = store
|
||||||
|
.pending_loads
|
||||||
|
.get_cached(url.clone(), origin.clone(), can_request);
|
||||||
match result {
|
match result {
|
||||||
CacheResult::Hit(key, pl) => match (&pl.result, &pl.metadata) {
|
CacheResult::Hit(key, pl) => match (&pl.result, &pl.metadata) {
|
||||||
(&Some(Ok(_)), _) => {
|
(&Some(Ok(_)), _) => {
|
||||||
debug!("Sync decoding {} ({:?})", url, key);
|
debug!("Sync decoding {} ({:?})", url, key);
|
||||||
decode_bytes_sync(key, &pl.bytes.as_slice())
|
decode_bytes_sync(key, &pl.bytes.as_slice(), pl.cors_status)
|
||||||
},
|
},
|
||||||
(&None, &Some(ref meta)) => {
|
(&None, &Some(ref meta)) => {
|
||||||
debug!("Metadata available for {} ({:?})", url, key);
|
debug!("Metadata available for {} ({:?})", url, key);
|
||||||
|
@ -440,7 +468,7 @@ impl ImageCache for ImageCacheImpl {
|
||||||
// and ignore the async decode when it finishes later.
|
// and ignore the async decode when it finishes later.
|
||||||
// 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, use_placeholder) {
|
match store.get_completed_image_if_available(url, origin, use_placeholder) {
|
||||||
Some(result) => result,
|
Some(result) => result,
|
||||||
None => Err(ImageState::LoadError),
|
None => Err(ImageState::LoadError),
|
||||||
}
|
}
|
||||||
|
@ -472,15 +500,26 @@ impl ImageCache for ImageCacheImpl {
|
||||||
(FetchResponseMsg::ProcessResponse(response), _) => {
|
(FetchResponseMsg::ProcessResponse(response), _) => {
|
||||||
let mut store = self.store.lock().unwrap();
|
let mut store = self.store.lock().unwrap();
|
||||||
let pending_load = store.pending_loads.get_by_key_mut(&id).unwrap();
|
let pending_load = store.pending_loads.get_by_key_mut(&id).unwrap();
|
||||||
let metadata = match response {
|
let (cors_status, metadata) = match response {
|
||||||
Ok(meta) => Some(match meta {
|
Ok(meta) => match meta {
|
||||||
FetchMetadata::Unfiltered(m) => m,
|
FetchMetadata::Unfiltered(m) => (CorsStatus::Safe, Some(m)),
|
||||||
FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
|
FetchMetadata::Filtered { unsafe_, filtered } => (
|
||||||
}),
|
match filtered {
|
||||||
Err(_) => None,
|
FilteredMetadata::Basic(_) | FilteredMetadata::Cors(_) => {
|
||||||
|
CorsStatus::Safe
|
||||||
|
},
|
||||||
|
FilteredMetadata::Opaque | FilteredMetadata::OpaqueRedirect => {
|
||||||
|
CorsStatus::Unsafe
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Some(unsafe_),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Err(_) => (CorsStatus::Unsafe, None),
|
||||||
};
|
};
|
||||||
let final_url = metadata.as_ref().map(|m| m.final_url.clone());
|
let final_url = metadata.as_ref().map(|m| m.final_url.clone());
|
||||||
pending_load.final_url = final_url;
|
pending_load.final_url = final_url;
|
||||||
|
pending_load.cors_status = cors_status;
|
||||||
},
|
},
|
||||||
(FetchResponseMsg::ProcessResponseChunk(data), _) => {
|
(FetchResponseMsg::ProcessResponseChunk(data), _) => {
|
||||||
debug!("Got some data for {:?}", id);
|
debug!("Got some data for {:?}", id);
|
||||||
|
@ -506,17 +545,17 @@ impl ImageCache for ImageCacheImpl {
|
||||||
debug!("Received EOF for {:?}", key);
|
debug!("Received EOF for {:?}", key);
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let bytes = {
|
let (bytes, cors_status) = {
|
||||||
let mut store = self.store.lock().unwrap();
|
let mut store = self.store.lock().unwrap();
|
||||||
let pending_load = store.pending_loads.get_by_key_mut(&id).unwrap();
|
let pending_load = store.pending_loads.get_by_key_mut(&id).unwrap();
|
||||||
pending_load.result = Some(Ok(()));
|
pending_load.result = Some(Ok(()));
|
||||||
debug!("Async decoding {} ({:?})", pending_load.url, key);
|
debug!("Async decoding {} ({:?})", pending_load.url, key);
|
||||||
pending_load.bytes.mark_complete()
|
(pending_load.bytes.mark_complete(), pending_load.cors_status)
|
||||||
};
|
};
|
||||||
|
|
||||||
let local_store = self.store.clone();
|
let local_store = self.store.clone();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let msg = decode_bytes_sync(key, &*bytes);
|
let msg = decode_bytes_sync(key, &*bytes, cors_status);
|
||||||
debug!("Image decoded");
|
debug!("Image decoded");
|
||||||
local_store.lock().unwrap().handle_decoder(msg);
|
local_store.lock().unwrap().handle_decoder(msg);
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* 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::image_cache::CorsStatus;
|
||||||
use ipc_channel::ipc::IpcSharedMemory;
|
use ipc_channel::ipc::IpcSharedMemory;
|
||||||
use piston_image::{DynamicImage, ImageFormat};
|
use piston_image::{DynamicImage, ImageFormat};
|
||||||
use pixels::PixelFormat;
|
use pixels::PixelFormat;
|
||||||
|
@ -16,6 +17,7 @@ pub struct Image {
|
||||||
pub bytes: IpcSharedMemory,
|
pub bytes: IpcSharedMemory,
|
||||||
#[ignore_malloc_size_of = "Defined in webrender_api"]
|
#[ignore_malloc_size_of = "Defined in webrender_api"]
|
||||||
pub id: Option<webrender_api::ImageKey>,
|
pub id: Option<webrender_api::ImageKey>,
|
||||||
|
pub cors_status: CorsStatus,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Image {
|
impl fmt::Debug for Image {
|
||||||
|
@ -37,7 +39,7 @@ pub struct ImageMetadata {
|
||||||
// FIXME: Images must not be copied every frame. Instead we should atomically
|
// FIXME: Images must not be copied every frame. Instead we should atomically
|
||||||
// reference count them.
|
// reference count them.
|
||||||
|
|
||||||
pub fn load_from_memory(buffer: &[u8]) -> Option<Image> {
|
pub fn load_from_memory(buffer: &[u8], cors_status: CorsStatus) -> Option<Image> {
|
||||||
if buffer.is_empty() {
|
if buffer.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -61,6 +63,7 @@ pub fn load_from_memory(buffer: &[u8]) -> Option<Image> {
|
||||||
format: PixelFormat::BGRA8,
|
format: PixelFormat::BGRA8,
|
||||||
bytes: IpcSharedMemory::from_bytes(&*rgba),
|
bytes: IpcSharedMemory::from_bytes(&*rgba),
|
||||||
id: None,
|
id: None,
|
||||||
|
cors_status,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
use crate::image::base::{Image, ImageMetadata};
|
use crate::image::base::{Image, ImageMetadata};
|
||||||
use crate::FetchResponseMsg;
|
use crate::FetchResponseMsg;
|
||||||
use ipc_channel::ipc::IpcSender;
|
use ipc_channel::ipc::IpcSender;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::{ImmutableOrigin, ServoUrl};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
// ======================================================================
|
// ======================================================================
|
||||||
|
@ -110,6 +110,7 @@ pub trait ImageCache: Sync + Send {
|
||||||
fn find_image_or_metadata(
|
fn find_image_or_metadata(
|
||||||
&self,
|
&self,
|
||||||
url: ServoUrl,
|
url: ServoUrl,
|
||||||
|
origin: ImmutableOrigin,
|
||||||
use_placeholder: UsePlaceholder,
|
use_placeholder: UsePlaceholder,
|
||||||
can_request: CanRequestImages,
|
can_request: CanRequestImages,
|
||||||
) -> Result<ImageOrMetadataAvailable, ImageState>;
|
) -> Result<ImageOrMetadataAvailable, ImageState>;
|
||||||
|
@ -121,3 +122,14 @@ pub trait ImageCache: Sync + Send {
|
||||||
/// Inform the image cache about a response for a pending request.
|
/// Inform the image cache about a response for a pending request.
|
||||||
fn notify_pending_response(&self, id: PendingImageId, action: FetchResponseMsg);
|
fn notify_pending_response(&self, id: PendingImageId, action: FetchResponseMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether this response passed any CORS checks, and is thus safe to read from
|
||||||
|
/// in cross-origin environments.
|
||||||
|
#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
|
||||||
|
pub enum CorsStatus {
|
||||||
|
/// The response is either same-origin or cross-origin but passed CORS checks.
|
||||||
|
Safe,
|
||||||
|
/// The response is cross-origin and did not pass CORS checks. It is unsafe
|
||||||
|
/// to expose pixel data to the requesting environment.
|
||||||
|
Unsafe,
|
||||||
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ use net_traits::image_cache::UsePlaceholder;
|
||||||
use pixels::PixelFormat;
|
use pixels::PixelFormat;
|
||||||
use profile_traits::ipc as profiled_ipc;
|
use profile_traits::ipc as profiled_ipc;
|
||||||
use script_traits::ScriptMsg;
|
use script_traits::ScriptMsg;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::{ImmutableOrigin, ServoUrl};
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -126,6 +126,7 @@ pub struct CanvasState {
|
||||||
/// The base URL for resolving CSS image URL values.
|
/// The base URL for resolving CSS image URL values.
|
||||||
/// Needed because of https://github.com/servo/servo/issues/17625
|
/// Needed because of https://github.com/servo/servo/issues/17625
|
||||||
base_url: ServoUrl,
|
base_url: ServoUrl,
|
||||||
|
origin: ImmutableOrigin,
|
||||||
/// Any missing image URLs.
|
/// Any missing image URLs.
|
||||||
missing_image_urls: DomRefCell<Vec<ServoUrl>>,
|
missing_image_urls: DomRefCell<Vec<ServoUrl>>,
|
||||||
saved_states: DomRefCell<Vec<CanvasContextState>>,
|
saved_states: DomRefCell<Vec<CanvasContextState>>,
|
||||||
|
@ -152,6 +153,7 @@ impl CanvasState {
|
||||||
base_url: global.api_base_url(),
|
base_url: global.api_base_url(),
|
||||||
missing_image_urls: DomRefCell::new(Vec::new()),
|
missing_image_urls: DomRefCell::new(Vec::new()),
|
||||||
saved_states: DomRefCell::new(Vec::new()),
|
saved_states: DomRefCell::new(Vec::new()),
|
||||||
|
origin: global.origin().immutable().clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,6 +224,7 @@ impl CanvasState {
|
||||||
fn request_image_from_cache(&self, url: ServoUrl) -> ImageResponse {
|
fn request_image_from_cache(&self, url: ServoUrl) -> ImageResponse {
|
||||||
let response = self.image_cache.find_image_or_metadata(
|
let response = self.image_cache.find_image_or_metadata(
|
||||||
url.clone(),
|
url.clone(),
|
||||||
|
self.origin.clone(),
|
||||||
UsePlaceholder::No,
|
UsePlaceholder::No,
|
||||||
CanRequestImages::No,
|
CanRequestImages::No,
|
||||||
);
|
);
|
||||||
|
|
|
@ -448,6 +448,7 @@ pub mod utils {
|
||||||
let image_cache = window.image_cache();
|
let image_cache = window.image_cache();
|
||||||
let response = image_cache.find_image_or_metadata(
|
let response = image_cache.find_image_or_metadata(
|
||||||
url.into(),
|
url.into(),
|
||||||
|
window.origin().immutable().clone(),
|
||||||
UsePlaceholder::No,
|
UsePlaceholder::No,
|
||||||
CanRequestImages::No,
|
CanRequestImages::No,
|
||||||
);
|
);
|
||||||
|
|
|
@ -56,7 +56,7 @@ 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::UsePlaceholder;
|
||||||
use net_traits::image_cache::{CanRequestImages, ImageCache, ImageOrMetadataAvailable};
|
use net_traits::image_cache::{CanRequestImages, CorsStatus, ImageCache, ImageOrMetadataAvailable};
|
||||||
use net_traits::image_cache::{ImageResponder, ImageResponse, ImageState, PendingImageId};
|
use net_traits::image_cache::{ImageResponder, ImageResponse, ImageState, PendingImageId};
|
||||||
use net_traits::request::RequestBuilder;
|
use net_traits::request::RequestBuilder;
|
||||||
use net_traits::{FetchMetadata, FetchResponseListener, FetchResponseMsg, NetworkError};
|
use net_traits::{FetchMetadata, FetchResponseListener, FetchResponseMsg, NetworkError};
|
||||||
|
@ -281,6 +281,7 @@ impl HTMLImageElement {
|
||||||
let image_cache = window.image_cache();
|
let image_cache = window.image_cache();
|
||||||
let response = image_cache.find_image_or_metadata(
|
let response = image_cache.find_image_or_metadata(
|
||||||
img_url.clone().into(),
|
img_url.clone().into(),
|
||||||
|
window.origin().immutable().clone(),
|
||||||
UsePlaceholder::Yes,
|
UsePlaceholder::Yes,
|
||||||
CanRequestImages::Yes,
|
CanRequestImages::Yes,
|
||||||
);
|
);
|
||||||
|
@ -907,6 +908,7 @@ impl HTMLImageElement {
|
||||||
let image_cache = window.image_cache();
|
let image_cache = window.image_cache();
|
||||||
let response = image_cache.find_image_or_metadata(
|
let response = image_cache.find_image_or_metadata(
|
||||||
img_url.clone().into(),
|
img_url.clone().into(),
|
||||||
|
window.origin().immutable().clone(),
|
||||||
UsePlaceholder::No,
|
UsePlaceholder::No,
|
||||||
CanRequestImages::No,
|
CanRequestImages::No,
|
||||||
);
|
);
|
||||||
|
@ -1062,6 +1064,7 @@ impl HTMLImageElement {
|
||||||
// Step 14
|
// Step 14
|
||||||
let response = image_cache.find_image_or_metadata(
|
let response = image_cache.find_image_or_metadata(
|
||||||
img_url.clone().into(),
|
img_url.clone().into(),
|
||||||
|
window.origin().immutable().clone(),
|
||||||
UsePlaceholder::No,
|
UsePlaceholder::No,
|
||||||
CanRequestImages::Yes,
|
CanRequestImages::Yes,
|
||||||
);
|
);
|
||||||
|
@ -1268,6 +1271,10 @@ impl HTMLImageElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn same_origin(&self, origin: &MutableOrigin) -> bool {
|
pub fn same_origin(&self, origin: &MutableOrigin) -> bool {
|
||||||
|
if let Some(ref image) = self.current_request.borrow().image {
|
||||||
|
return image.cors_status == CorsStatus::Safe;
|
||||||
|
}
|
||||||
|
|
||||||
self.current_request
|
self.current_request
|
||||||
.borrow()
|
.borrow()
|
||||||
.final_url
|
.final_url
|
||||||
|
|
|
@ -132,6 +132,7 @@ impl HTMLVideoElement {
|
||||||
let image_cache = window.image_cache();
|
let image_cache = window.image_cache();
|
||||||
let response = image_cache.find_image_or_metadata(
|
let response = image_cache.find_image_or_metadata(
|
||||||
poster_url.clone().into(),
|
poster_url.clone().into(),
|
||||||
|
window.origin().immutable().clone(),
|
||||||
UsePlaceholder::No,
|
UsePlaceholder::No,
|
||||||
CanRequestImages::Yes,
|
CanRequestImages::Yes,
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,7 +8,7 @@ use url::{Host, Origin};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
/// The origin of an URL
|
/// The origin of an URL
|
||||||
#[derive(Clone, Debug, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
|
||||||
pub enum ImmutableOrigin {
|
pub enum ImmutableOrigin {
|
||||||
/// A globally unique identifier
|
/// A globally unique identifier
|
||||||
Opaque(OpaqueOrigin),
|
Opaque(OpaqueOrigin),
|
||||||
|
@ -82,7 +82,7 @@ impl ImmutableOrigin {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Opaque identifier for URLs that have file or other schemes
|
/// Opaque identifier for URLs that have file or other schemes
|
||||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||||
pub struct OpaqueOrigin(Uuid);
|
pub struct OpaqueOrigin(Uuid);
|
||||||
|
|
||||||
malloc_size_of_is_0!(OpaqueOrigin);
|
malloc_size_of_is_0!(OpaqueOrigin);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue