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

Allow using CORS filtered image responses as WebGL textures #24340

Merged
merged 6 commits into from Oct 8, 2019
Merged

Support CORS attributes for image elements.

  • Loading branch information
jdm committed Oct 4, 2019
commit 1df8d57dc6adcf56c22b45053b3d2eca904d17d3
@@ -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,
);
@@ -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};
@@ -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 {
@@ -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,
@@ -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)
})
@@ -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())
@@ -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) => {
@@ -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,

@@ -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,
@@ -291,6 +310,7 @@ impl PendingLoad {
url: url,
load_origin,
final_url: None,
cors_setting,
cors_status: CorsStatus::Unsafe,
}
}
@@ -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>>,
@@ -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,
);

@@ -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), _) |
@@ -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(_)), _) => {
@@ -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),
}
@@ -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};
@@ -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>;
@@ -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,
@@ -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,
@@ -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 {
@@ -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
@@ -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;
@@ -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};
@@ -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;
@@ -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 |
@@ -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,
);
@@ -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,
)
},
};

@@ -435,6 +460,7 @@ impl CanvasState {
&self,
canvas: Option<&HTMLCanvasElement>,
url: ServoUrl,
cors_setting: Option<CorsSettings>,
sx: f64,
sy: f64,
sw: Option<f64>,
@@ -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();

@@ -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) => {
@@ -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)?,
};

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.