Skip to content

Commit

Permalink
Support CORS attributes for image elements.
Browse files Browse the repository at this point in the history
  • Loading branch information
jdm committed Oct 4, 2019
1 parent 583536c commit 1df8d57
Show file tree
Hide file tree
Showing 102 changed files with 278 additions and 364 deletions.
1 change: 1 addition & 0 deletions components/layout/context.rs
Expand Up @@ -126,6 +126,7 @@ impl<'a> LayoutContext<'a> {
let result = self.image_cache.find_image_or_metadata(
url.clone(),
self.origin.clone(),
None,
use_placeholder,
can_request,
);
Expand Down
62 changes: 47 additions & 15 deletions components/net/image_cache.rs
Expand Up @@ -8,6 +8,7 @@ use net_traits::image::base::{load_from_memory, Image, ImageMetadata};
use net_traits::image_cache::{CanRequestImages, CorsStatus, ImageCache, ImageResponder};
use net_traits::image_cache::{ImageOrMetadataAvailable, ImageResponse, ImageState};
use net_traits::image_cache::{PendingImageId, UsePlaceholder};
use net_traits::request::CorsSettings;
use net_traits::{FetchMetadata, FetchResponseMsg, FilteredMetadata, NetworkError};
use pixels::PixelFormat;
use servo_url::{ImmutableOrigin, ServoUrl};
Expand Down Expand Up @@ -92,6 +93,9 @@ fn set_webrender_image_key(webrender_api: &webrender_api::RenderApi, image: &mut
// Aux structs and enums.
// ======================================================================

/// https://html.spec.whatwg.org/multipage/#list-of-available-images
type ImageKey = (ServoUrl, ImmutableOrigin, Option<CorsSettings>);

// Represents all the currently pending loads/decodings. For
// performance reasons, loads are indexed by a dedicated load key.
struct AllPendingLoads {
Expand All @@ -101,7 +105,7 @@ struct AllPendingLoads {

// Get a load key from its url and requesting origin. Used ony when starting and
// finishing a load or when adding a new listener.
url_to_load_key: HashMap<(ServoUrl, ImmutableOrigin), LoadKey>,
url_to_load_key: HashMap<ImageKey, LoadKey>,

// A counter used to generate instances of LoadKey
keygen: LoadKeyGenerator,
Expand All @@ -124,7 +128,11 @@ impl AllPendingLoads {
fn remove(&mut self, key: &LoadKey) -> Option<PendingLoad> {
self.loads.remove(key).and_then(|pending_load| {
self.url_to_load_key
.remove(&(pending_load.url.clone(), pending_load.load_origin.clone()))
.remove(&(
pending_load.url.clone(),
pending_load.load_origin.clone(),
pending_load.cors_setting,
))
.unwrap();
Some(pending_load)
})
Expand All @@ -134,9 +142,13 @@ impl AllPendingLoads {
&'a mut self,
url: ServoUrl,
origin: ImmutableOrigin,
cors_status: Option<CorsSettings>,
can_request: CanRequestImages,
) -> CacheResult<'a> {
match self.url_to_load_key.entry((url.clone(), origin.clone())) {
match self
.url_to_load_key
.entry((url.clone(), origin.clone(), cors_status))
{
Occupied(url_entry) => {
let load_key = url_entry.get();
CacheResult::Hit(*load_key, self.loads.get_mut(load_key).unwrap())
Expand All @@ -149,7 +161,7 @@ impl AllPendingLoads {
let load_key = self.keygen.next();
url_entry.insert(load_key);

let pending_load = PendingLoad::new(url, origin);
let pending_load = PendingLoad::new(url, origin, cors_status);
match self.loads.entry(load_key) {
Occupied(_) => unreachable!(),
Vacant(load_entry) => {
Expand Down Expand Up @@ -274,6 +286,9 @@ struct PendingLoad {
/// The origin that requested this load.
load_origin: ImmutableOrigin,

/// The CORS attribute setting for the requesting
cors_setting: Option<CorsSettings>,

/// The CORS status of this image response.
cors_status: CorsStatus,

Expand All @@ -282,7 +297,11 @@ struct PendingLoad {
}

impl PendingLoad {
fn new(url: ServoUrl, load_origin: ImmutableOrigin) -> PendingLoad {
fn new(
url: ServoUrl,
load_origin: ImmutableOrigin,
cors_setting: Option<CorsSettings>,
) -> PendingLoad {
PendingLoad {
bytes: ImageBytes::InProgress(vec![]),
metadata: None,
Expand All @@ -291,6 +310,7 @@ impl PendingLoad {
url: url,
load_origin,
final_url: None,
cors_setting,
cors_status: CorsStatus::Unsafe,
}
}
Expand All @@ -308,7 +328,7 @@ struct ImageCacheStore {
pending_loads: AllPendingLoads,

// Images that have finished loading (successful or not)
completed_loads: HashMap<(ServoUrl, ImmutableOrigin), CompletedLoad>,
completed_loads: HashMap<ImageKey, CompletedLoad>,

// The placeholder image used when an image fails to load
placeholder_image: Option<Arc<Image>>,
Expand Down Expand Up @@ -346,7 +366,11 @@ impl ImageCacheStore {

let completed_load = CompletedLoad::new(image_response.clone(), key);
self.completed_loads.insert(
(pending_load.url.into(), pending_load.load_origin),
(
pending_load.url.into(),
pending_load.load_origin,
pending_load.cors_setting,
),
completed_load,
);

Expand All @@ -361,10 +385,11 @@ impl ImageCacheStore {
&self,
url: ServoUrl,
origin: ImmutableOrigin,
cors_setting: Option<CorsSettings>,
placeholder: UsePlaceholder,
) -> Option<Result<ImageOrMetadataAvailable, ImageState>> {
self.completed_loads
.get(&(url, origin))
.get(&(url, origin, cors_setting))
.map(
|completed_load| match (&completed_load.image_response, placeholder) {
(&ImageResponse::Loaded(ref image, ref url), _) |
Expand Down Expand Up @@ -421,22 +446,29 @@ impl ImageCache for ImageCacheImpl {
&self,
url: ServoUrl,
origin: ImmutableOrigin,
cors_setting: Option<CorsSettings>,
use_placeholder: UsePlaceholder,
can_request: CanRequestImages,
) -> Result<ImageOrMetadataAvailable, ImageState> {
debug!("Find image or metadata for {} ({:?})", url, origin);
let mut store = self.store.lock().unwrap();
if let Some(result) =
store.get_completed_image_if_available(url.clone(), origin.clone(), use_placeholder)
{
if let Some(result) = store.get_completed_image_if_available(
url.clone(),
origin.clone(),
cors_setting,
use_placeholder,
) {
debug!("{} is available", url);
return result;
}

let decoded = {
let result = store
.pending_loads
.get_cached(url.clone(), origin.clone(), can_request);
let result = store.pending_loads.get_cached(
url.clone(),
origin.clone(),
cors_setting,
can_request,
);
match result {
CacheResult::Hit(key, pl) => match (&pl.result, &pl.metadata) {
(&Some(Ok(_)), _) => {
Expand Down Expand Up @@ -468,7 +500,7 @@ impl ImageCache for ImageCacheImpl {
// and ignore the async decode when it finishes later.
// TODO: make this behaviour configurable according to the caller's needs.
store.handle_decoder(decoded);
match store.get_completed_image_if_available(url, origin, use_placeholder) {
match store.get_completed_image_if_available(url, origin, cors_setting, use_placeholder) {
Some(result) => result,
None => Err(ImageState::LoadError),
}
Expand Down
2 changes: 2 additions & 0 deletions components/net_traits/image_cache.rs
Expand Up @@ -3,6 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::image::base::{Image, ImageMetadata};
use crate::request::CorsSettings;
use crate::FetchResponseMsg;
use ipc_channel::ipc::IpcSender;
use servo_url::{ImmutableOrigin, ServoUrl};
Expand Down Expand Up @@ -111,6 +112,7 @@ pub trait ImageCache: Sync + Send {
&self,
url: ServoUrl,
origin: ImmutableOrigin,
cors_setting: Option<CorsSettings>,
use_placeholder: UsePlaceholder,
can_request: CanRequestImages,
) -> Result<ImageOrMetadataAvailable, ImageState>;
Expand Down
12 changes: 10 additions & 2 deletions components/net_traits/request.rs
Expand Up @@ -10,7 +10,7 @@ use msg::constellation_msg::PipelineId;
use servo_url::{ImmutableOrigin, ServoUrl};

/// An [initiator](https://fetch.spec.whatwg.org/#concept-request-initiator)
#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
pub enum Initiator {
None,
Download,
Expand Down Expand Up @@ -128,7 +128,7 @@ pub enum Window {
}

/// [CORS settings attribute](https://html.spec.whatwg.org/multipage/#attr-crossorigin-anonymous)
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum CorsSettings {
Anonymous,
UseCredentials,
Expand Down Expand Up @@ -178,6 +178,7 @@ pub struct RequestBuilder {
// to keep track of redirects
pub url_list: Vec<ServoUrl>,
pub parser_metadata: ParserMetadata,
pub initiator: Initiator,
}

impl RequestBuilder {
Expand All @@ -204,9 +205,15 @@ impl RequestBuilder {
integrity_metadata: "".to_owned(),
url_list: vec![],
parser_metadata: ParserMetadata::Default,
initiator: Initiator::None,
}
}

pub fn initiator(mut self, initiator: Initiator) -> RequestBuilder {
self.initiator = initiator;
self
}

pub fn method(mut self, method: Method) -> RequestBuilder {
self.method = method;
self
Expand Down Expand Up @@ -298,6 +305,7 @@ impl RequestBuilder {
Some(Origin::Origin(self.origin)),
self.pipeline_id,
);
request.initiator = self.initiator;
request.method = self.method;
request.headers = self.headers;
request.unsafe_request = self.unsafe_request;
Expand Down
48 changes: 39 additions & 9 deletions components/script/dom/canvasrenderingcontext2d.rs
Expand Up @@ -19,6 +19,7 @@ use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom};
use crate::dom::bindings::str::DOMString;
use crate::dom::canvasgradient::{CanvasGradient, CanvasGradientStyle, ToFillOrStrokeStyle};
use crate::dom::canvaspattern::CanvasPattern;
use crate::dom::element::cors_setting_for_element;
use crate::dom::element::Element;
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlcanvaselement::{CanvasContext, HTMLCanvasElement};
Expand All @@ -45,6 +46,7 @@ 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 pixels::PixelFormat;
use profile_traits::ipc as profiled_ipc;
use script_traits::ScriptMsg;
Expand Down Expand Up @@ -210,8 +212,12 @@ impl CanvasState {
}
}

fn fetch_image_data(&self, url: ServoUrl) -> Option<(Vec<u8>, Size2D<u32>)> {
let img = match self.request_image_from_cache(url) {
fn fetch_image_data(
&self,
url: ServoUrl,
cors_setting: Option<CorsSettings>,
) -> Option<(Vec<u8>, Size2D<u32>)> {
let img = match self.request_image_from_cache(url, cors_setting) {
ImageResponse::Loaded(img, _) => img,
ImageResponse::PlaceholderLoaded(_, _) |
ImageResponse::None |
Expand All @@ -229,11 +235,15 @@ impl CanvasState {
Some((image_data, image_size))
}

#[inline]
fn request_image_from_cache(&self, url: ServoUrl) -> ImageResponse {
fn request_image_from_cache(
&self,
url: ServoUrl,
cors_setting: Option<CorsSettings>,
) -> ImageResponse {
let response = self.image_cache.find_image_or_metadata(
url.clone(),
self.origin.clone(),
cors_setting,
UsePlaceholder::No,
CanRequestImages::No,
);
Expand Down Expand Up @@ -353,13 +363,28 @@ impl CanvasState {
// If the image argument is an HTMLImageElement object that is in the broken state,
// then throw an InvalidStateError exception
let url = image.get_url().ok_or(Error::InvalidState)?;
self.fetch_and_draw_image_data(htmlcanvas, url, sx, sy, sw, sh, dx, dy, dw, dh)
let cors_setting = cors_setting_for_element(image.upcast());
self.fetch_and_draw_image_data(
htmlcanvas,
url,
cors_setting,
sx,
sy,
sw,
sh,
dx,
dy,
dw,
dh,
)
},
CanvasImageSource::CSSStyleValue(ref value) => {
let url = value
.get_url(self.base_url.clone())
.ok_or(Error::InvalidState)?;
self.fetch_and_draw_image_data(htmlcanvas, url, sx, sy, sw, sh, dx, dy, dw, dh)
self.fetch_and_draw_image_data(
htmlcanvas, url, None, sx, sy, sw, sh, dx, dy, dw, dh,
)
},
};

Expand Down Expand Up @@ -435,6 +460,7 @@ impl CanvasState {
&self,
canvas: Option<&HTMLCanvasElement>,
url: ServoUrl,
cors_setting: Option<CorsSettings>,
sx: f64,
sy: f64,
sw: Option<f64>,
Expand All @@ -445,7 +471,9 @@ impl CanvasState {
dh: Option<f64>,
) -> ErrorResult {
debug!("Fetching image {}.", url);
let (mut image_data, image_size) = self.fetch_image_data(url).ok_or(Error::InvalidState)?;
let (mut image_data, image_size) = self
.fetch_image_data(url, cors_setting)
.ok_or(Error::InvalidState)?;
pixels::rgba8_premultiply_inplace(&mut image_data);
let image_size = image_size.to_f64();

Expand Down Expand Up @@ -788,7 +816,9 @@ impl CanvasState {
// then throw an InvalidStateError exception
image
.get_url()
.and_then(|url| self.fetch_image_data(url))
.and_then(|url| {
self.fetch_image_data(url, cors_setting_for_element(image.upcast()))
})
.ok_or(Error::InvalidState)?
},
CanvasImageSource::HTMLCanvasElement(ref canvas) => {
Expand All @@ -800,7 +830,7 @@ impl CanvasState {
},
CanvasImageSource::CSSStyleValue(ref value) => value
.get_url(self.base_url.clone())
.and_then(|url| self.fetch_image_data(url))
.and_then(|url| self.fetch_image_data(url, None))
.ok_or(Error::InvalidState)?,
};

Expand Down

0 comments on commit 1df8d57

Please sign in to comment.