Auto merge of #24340 - jdm:image-cache-cors, r=Manishearth

Allow using CORS filtered image responses as WebGL textures

More specifically, this makes the "is this image same origin?" check consider the CORS status of the original response, rather than relying on an overly-strict "is this image's response's URL same-origin with a particular global?" check. To do this, we make the image cache double keyed based on the requested URL as well as the requesting origin, and store the CORS status of the eventual response with the final image that eventually gets sent to the HTMLImageElement consumer.

---
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [x] These changes fix #24330 and fix #24368
- [x] There are tests for these changes

<!-- 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/24340)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2019-10-07 18:39:36 -04:00 committed by GitHub
commit 75548f40c6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
109 changed files with 531 additions and 419 deletions

View file

@ -22,6 +22,7 @@ use ipc_channel::ipc;
use libc::c_void;
use msg::constellation_msg::{PipelineId, PipelineIndex, PipelineNamespaceId};
use net_traits::image::base::Image;
use net_traits::image_cache::CorsStatus;
use num_traits::FromPrimitive;
#[cfg(feature = "gl")]
use pixels::PixelFormat;
@ -1384,6 +1385,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
format: PixelFormat::RGB8,
bytes: ipc::IpcSharedMemory::from_bytes(&*img),
id: None,
cors_status: CorsStatus::Safe,
})
},
#[cfg(feature = "gl")]

View file

@ -18,7 +18,7 @@ use script_layout_interface::{PendingImage, PendingImageState};
use script_traits::Painter;
use script_traits::UntrustedNodeAddress;
use servo_atoms::Atom;
use servo_url::ServoUrl;
use servo_url::{ImmutableOrigin, ServoUrl};
use std::cell::{RefCell, RefMut};
use std::collections::HashMap;
use std::hash::BuildHasherDefault;
@ -60,6 +60,9 @@ pub struct LayoutContext<'a> {
/// The pipeline id of this LayoutContext.
pub id: PipelineId,
/// The origin of this layout context.
pub origin: ImmutableOrigin,
/// Bits shared by the layout and style system.
pub style_context: SharedStyleContext<'a>,
@ -120,9 +123,13 @@ impl<'a> LayoutContext<'a> {
};
// See if the image is already available
let result =
self.image_cache
.find_image_or_metadata(url.clone(), use_placeholder, can_request);
let result = self.image_cache.find_image_or_metadata(
url.clone(),
self.origin.clone(),
None,
use_placeholder,
can_request,
);
match result {
Ok(image_or_metadata) => Some(image_or_metadata),
// Image failed to load, so just return nothing

View file

@ -659,6 +659,7 @@ impl LayoutThread {
LayoutContext {
id: self.id,
origin: self.url.origin(),
style_context: SharedStyleContext {
stylist: &self.stylist,
options: GLOBAL_STYLE_DATA.options.clone(),

View file

@ -1333,6 +1333,7 @@ fn http_network_fetch(
res.status(),
res.status().canonical_reason().unwrap_or("").into(),
));
debug!("got {:?} response for {:?}", res.status(), request.url());
response.raw_status = Some((
res.status().as_u16(),
res.status().canonical_reason().unwrap_or("").into(),
@ -1387,6 +1388,8 @@ fn http_network_fetch(
let done_sender3 = done_sender.clone();
let timing_ptr2 = context.timing.clone();
let timing_ptr3 = context.timing.clone();
let url1 = request.url();
let url2 = url1.clone();
HANDLE.lock().unwrap().spawn(
res.into_body()
.map_err(|_| ())
@ -1404,6 +1407,7 @@ fn http_network_fetch(
future::ok(res_body)
})
.and_then(move |res_body| {
debug!("successfully finished response for {:?}", url1);
let mut body = res_body.lock().unwrap();
let completed_body = match *body {
ResponseBody::Receiving(ref mut body) => mem::replace(body, vec![]),
@ -1418,6 +1422,7 @@ fn http_network_fetch(
future::ok(())
})
.map_err(move |_| {
debug!("finished response for {:?} with error", url2);
let mut body = res_body2.lock().unwrap();
let completed_body = match *body {
ResponseBody::Receiving(ref mut body) => mem::replace(body, vec![]),

View file

@ -5,12 +5,13 @@
use embedder_traits::resources::{self, Resource};
use immeta::load_from_buf;
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::{PendingImageId, UsePlaceholder};
use net_traits::{FetchMetadata, FetchResponseMsg, NetworkError};
use net_traits::request::CorsSettings;
use net_traits::{FetchMetadata, FetchResponseMsg, FilteredMetadata, NetworkError};
use pixels::PixelFormat;
use servo_url::ServoUrl;
use servo_url::{ImmutableOrigin, ServoUrl};
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::HashMap;
use std::io;
@ -33,8 +34,8 @@ use webrender_api::units::DeviceIntSize;
// Helper functions.
// ======================================================================
fn decode_bytes_sync(key: LoadKey, bytes: &[u8]) -> DecoderMsg {
let image = load_from_memory(bytes);
fn decode_bytes_sync(key: LoadKey, bytes: &[u8], cors: CorsStatus) -> DecoderMsg {
let image = load_from_memory(bytes, cors);
DecoderMsg {
key: key,
image: image,
@ -45,7 +46,7 @@ fn get_placeholder_image(
webrender_api: &webrender_api::RenderApi,
data: &[u8],
) -> 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);
Ok(Arc::new(image))
}
@ -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 {
@ -99,9 +103,9 @@ struct AllPendingLoads {
// for performance reasons.
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.
url_to_load_key: HashMap<ServoUrl, LoadKey>,
url_to_load_key: HashMap<ImageKey, LoadKey>,
// A counter used to generate instances of LoadKey
keygen: LoadKeyGenerator,
@ -123,7 +127,13 @@ 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).unwrap();
self.url_to_load_key
.remove(&(
pending_load.url.clone(),
pending_load.load_origin.clone(),
pending_load.cors_setting,
))
.unwrap();
Some(pending_load)
})
}
@ -131,9 +141,14 @@ impl AllPendingLoads {
fn get_cached<'a>(
&'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()) {
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())
@ -146,7 +161,7 @@ impl AllPendingLoads {
let load_key = self.keygen.next();
url_entry.insert(load_key);
let pending_load = PendingLoad::new(url);
let pending_load = PendingLoad::new(url, origin, cors_status);
match self.loads.entry(load_key) {
Occupied(_) => unreachable!(),
Vacant(load_entry) => {
@ -251,33 +266,52 @@ enum LoadResult {
/// Represents an image that is either being loaded
/// by the resource thread, or decoded by a worker thread.
struct PendingLoad {
// The bytes loaded so far. Reset to an empty vector once loading
// is complete and the buffer has been transmitted to the decoder.
/// The bytes loaded so far. Reset to an empty vector once loading
/// is complete and the buffer has been transmitted to the decoder.
bytes: ImageBytes,
// Image metadata, if available.
/// Image metadata, if available.
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>>,
/// The listeners that are waiting for this response to complete.
listeners: Vec<ImageResponder>,
// The url being loaded. Do not forget that this may be several Mb
// if we are loading a data: url.
/// The url being loaded. Do not forget that this may be several Mb
/// if we are loading a data: url.
url: ServoUrl,
/// 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,
/// The URL of the final response that contains a body.
final_url: Option<ServoUrl>,
}
impl PendingLoad {
fn new(url: ServoUrl) -> PendingLoad {
fn new(
url: ServoUrl,
load_origin: ImmutableOrigin,
cors_setting: Option<CorsSettings>,
) -> PendingLoad {
PendingLoad {
bytes: ImageBytes::InProgress(vec![]),
metadata: None,
result: None,
listeners: vec![],
url: url,
load_origin,
final_url: None,
cors_setting,
cors_status: CorsStatus::Unsafe,
}
}
@ -294,7 +328,7 @@ struct ImageCacheStore {
pending_loads: AllPendingLoads,
// Images that have finished loading (successful or not)
completed_loads: HashMap<ServoUrl, CompletedLoad>,
completed_loads: HashMap<ImageKey, CompletedLoad>,
// The placeholder image used when an image fails to load
placeholder_image: Option<Arc<Image>>,
@ -331,8 +365,14 @@ impl ImageCacheStore {
};
let completed_load = CompletedLoad::new(image_response.clone(), key);
self.completed_loads
.insert(pending_load.url.into(), completed_load);
self.completed_loads.insert(
(
pending_load.url.into(),
pending_load.load_origin,
pending_load.cors_setting,
),
completed_load,
);
for listener in pending_load.listeners {
listener.respond(image_response.clone());
@ -343,20 +383,28 @@ impl ImageCacheStore {
/// or the complete load is not fully decoded or is unavailable.
fn get_completed_image_if_available(
&self,
url: &ServoUrl,
url: ServoUrl,
origin: ImmutableOrigin,
cors_setting: Option<CorsSettings>,
placeholder: UsePlaceholder,
) -> Option<Result<ImageOrMetadataAvailable, ImageState>> {
self.completed_loads.get(url).map(|completed_load| {
match (&completed_load.image_response, placeholder) {
(&ImageResponse::Loaded(ref image, ref url), _) |
(&ImageResponse::PlaceholderLoaded(ref image, ref url), UsePlaceholder::Yes) => Ok(
ImageOrMetadataAvailable::ImageAvailable(image.clone(), url.clone()),
),
(&ImageResponse::PlaceholderLoaded(_, _), UsePlaceholder::No) |
(&ImageResponse::None, _) |
(&ImageResponse::MetadataLoaded(_), _) => Err(ImageState::LoadError),
}
})
self.completed_loads
.get(&(url, origin, cors_setting))
.map(
|completed_load| match (&completed_load.image_response, placeholder) {
(&ImageResponse::Loaded(ref image, ref url), _) |
(
&ImageResponse::PlaceholderLoaded(ref image, ref url),
UsePlaceholder::Yes,
) => 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
@ -397,23 +445,35 @@ impl ImageCache for ImageCacheImpl {
fn find_image_or_metadata(
&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);
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, 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(), 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(_)), _) => {
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)) => {
debug!("Metadata available for {} ({:?})", url, key);
@ -440,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, use_placeholder) {
match store.get_completed_image_if_available(url, origin, cors_setting, use_placeholder) {
Some(result) => result,
None => Err(ImageState::LoadError),
}
@ -470,17 +530,29 @@ impl ImageCache for ImageCacheImpl {
(FetchResponseMsg::ProcessRequestBody, _) |
(FetchResponseMsg::ProcessRequestEOF, _) => return,
(FetchResponseMsg::ProcessResponse(response), _) => {
debug!("Received {:?} for {:?}", response.as_ref().map(|_| ()), id);
let mut store = self.store.lock().unwrap();
let pending_load = store.pending_loads.get_by_key_mut(&id).unwrap();
let metadata = match response {
Ok(meta) => Some(match meta {
FetchMetadata::Unfiltered(m) => m,
FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
}),
Err(_) => None,
let (cors_status, metadata) = match response {
Ok(meta) => match meta {
FetchMetadata::Unfiltered(m) => (CorsStatus::Safe, Some(m)),
FetchMetadata::Filtered { unsafe_, filtered } => (
match filtered {
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());
pending_load.final_url = final_url;
pending_load.cors_status = cors_status;
},
(FetchResponseMsg::ProcessResponseChunk(data), _) => {
debug!("Got some data for {:?}", id);
@ -506,17 +578,17 @@ impl ImageCache for ImageCacheImpl {
debug!("Received EOF for {:?}", key);
match result {
Ok(_) => {
let bytes = {
let (bytes, cors_status) = {
let mut store = self.store.lock().unwrap();
let pending_load = store.pending_loads.get_by_key_mut(&id).unwrap();
pending_load.result = Some(Ok(()));
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();
thread::spawn(move || {
let msg = decode_bytes_sync(key, &*bytes);
let msg = decode_bytes_sync(key, &*bytes, cors_status);
debug!("Image decoded");
local_store.lock().unwrap().handle_decoder(msg);
});

View file

@ -2,6 +2,7 @@
* 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/. */
use crate::image_cache::CorsStatus;
use ipc_channel::ipc::IpcSharedMemory;
use piston_image::{DynamicImage, ImageFormat};
use pixels::PixelFormat;
@ -16,6 +17,7 @@ pub struct Image {
pub bytes: IpcSharedMemory,
#[ignore_malloc_size_of = "Defined in webrender_api"]
pub id: Option<webrender_api::ImageKey>,
pub cors_status: CorsStatus,
}
impl fmt::Debug for Image {
@ -37,7 +39,7 @@ pub struct ImageMetadata {
// FIXME: Images must not be copied every frame. Instead we should atomically
// 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() {
return None;
}
@ -61,6 +63,7 @@ pub fn load_from_memory(buffer: &[u8]) -> Option<Image> {
format: PixelFormat::BGRA8,
bytes: IpcSharedMemory::from_bytes(&*rgba),
id: None,
cors_status,
})
},
Err(e) => {

View file

@ -3,9 +3,10 @@
* 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::ServoUrl;
use servo_url::{ImmutableOrigin, ServoUrl};
use std::sync::Arc;
// ======================================================================
@ -110,6 +111,8 @@ pub trait ImageCache: Sync + Send {
fn find_image_or_metadata(
&self,
url: ServoUrl,
origin: ImmutableOrigin,
cors_setting: Option<CorsSettings>,
use_placeholder: UsePlaceholder,
can_request: CanRequestImages,
) -> Result<ImageOrMetadataAvailable, ImageState>;
@ -121,3 +124,14 @@ pub trait ImageCache: Sync + Send {
/// Inform the image cache about a response for a pending request.
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,
}

View file

@ -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;

View file

@ -19,11 +19,13 @@ 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};
use crate::dom::imagedata::ImageData;
use crate::dom::node::{Node, NodeDamage};
use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope;
use crate::dom::textmetrics::TextMetrics;
use crate::unpremultiplytable::UNPREMULTIPLY_TABLE;
use canvas_traits::canvas::{Canvas2dMsg, CanvasId, CanvasMsg};
@ -44,10 +46,11 @@ 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;
use servo_url::ServoUrl;
use servo_url::{ImmutableOrigin, ServoUrl};
use std::cell::Cell;
use std::str::FromStr;
use std::sync::Arc;
@ -126,6 +129,7 @@ pub struct CanvasState {
/// The base URL for resolving CSS image URL values.
/// Needed because of https://github.com/servo/servo/issues/17625
base_url: ServoUrl,
origin: ImmutableOrigin,
/// Any missing image URLs.
missing_image_urls: DomRefCell<Vec<ServoUrl>>,
saved_states: DomRefCell<Vec<CanvasContextState>>,
@ -143,6 +147,14 @@ impl CanvasState {
.unwrap();
let (ipc_renderer, canvas_id) = receiver.recv().unwrap();
debug!("Done.");
// Worklets always receive a unique origin. This messes with fetching
// cached images in the case of paint worklets, since the image cache
// is keyed on the origin requesting the image data.
let origin = if global.is::<PaintWorkletGlobalScope>() {
global.api_base_url().origin()
} else {
global.origin().immutable().clone()
};
CanvasState {
ipc_renderer: ipc_renderer,
canvas_id: canvas_id,
@ -152,6 +164,7 @@ impl CanvasState {
base_url: global.api_base_url(),
missing_image_urls: DomRefCell::new(Vec::new()),
saved_states: DomRefCell::new(Vec::new()),
origin,
}
}
@ -199,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 |
@ -218,10 +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,
);
@ -341,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,
)
},
};
@ -423,6 +460,7 @@ impl CanvasState {
&self,
canvas: Option<&HTMLCanvasElement>,
url: ServoUrl,
cors_setting: Option<CorsSettings>,
sx: f64,
sy: f64,
sw: Option<f64>,
@ -433,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();
@ -776,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) => {
@ -788,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)?,
};

View file

@ -35,7 +35,7 @@ use crate::dom::create::create_element;
use crate::dom::customelementregistry::{
CallbackReaction, CustomElementDefinition, CustomElementReaction, CustomElementState,
};
use crate::dom::document::{Document, LayoutDocumentHelpers};
use crate::dom::document::{determine_policy_for_token, Document, LayoutDocumentHelpers};
use crate::dom::documentfragment::DocumentFragment;
use crate::dom::domrect::DOMRect;
use crate::dom::domtokenlist::DOMTokenList;
@ -97,6 +97,7 @@ use js::jsapi::Heap;
use js::jsval::JSVal;
use msg::constellation_msg::InputMethodType;
use net_traits::request::CorsSettings;
use net_traits::ReferrerPolicy;
use ref_filter_map::ref_filter_map;
use script_layout_interface::message::ReflowGoal;
use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
@ -3606,7 +3607,14 @@ pub fn set_cross_origin_attribute(element: &Element, value: Option<DOMString>) {
}
}
pub fn cors_setting_for_element(element: &Element) -> Option<CorsSettings> {
pub(crate) fn referrer_policy_for_element(element: &Element) -> Option<ReferrerPolicy> {
element
.get_attribute_by_name(DOMString::from_string(String::from("referrerpolicy")))
.and_then(|attribute: DomRoot<Attr>| determine_policy_for_token(&attribute.Value()))
.or_else(|| document_from_node(element).get_referrer_policy())
}
pub(crate) fn cors_setting_for_element(element: &Element) -> Option<CorsSettings> {
reflect_cross_origin_attribute(element).map_or(None, |attr| match &*attr {
"anonymous" => Some(CorsSettings::Anonymous),
"use-credentials" => Some(CorsSettings::UseCredentials),

View file

@ -18,7 +18,7 @@ use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
use crate::dom::messageevent::MessageEvent;
use crate::dom::performanceresourcetiming::InitiatorType;
use crate::fetch::FetchCanceller;
use crate::fetch::{create_a_potential_CORS_request, FetchCanceller};
use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener};
use crate::task_source::{TaskSource, TaskSourceName};
use crate::timers::OneshotTimerCallback;
@ -31,8 +31,7 @@ use ipc_channel::router::ROUTER;
use js::conversions::ToJSValConvertible;
use js::jsval::UndefinedValue;
use mime::{self, Mime};
use net_traits::request::{CacheMode, CorsSettings, CredentialsMode};
use net_traits::request::{RequestBuilder, RequestMode};
use net_traits::request::{CacheMode, CorsSettings, Destination, RequestBuilder};
use net_traits::{CoreResourceMsg, FetchChannels, FetchMetadata};
use net_traits::{FetchResponseListener, FetchResponseMsg, NetworkError};
use net_traits::{ResourceFetchTiming, ResourceTimingType};
@ -516,17 +515,14 @@ impl EventSource {
};
// Step 8
// TODO: Step 9 set request's client settings
let mut request = RequestBuilder::new(url_record)
.origin(global.origin().immutable().clone())
.pipeline_id(Some(global.pipeline_id()))
// https://html.spec.whatwg.org/multipage/#create-a-potential-cors-request
.use_url_credentials(true)
.mode(RequestMode::CorsMode)
.credentials_mode(if cors_attribute_state == CorsSettings::Anonymous {
CredentialsMode::CredentialsSameOrigin
} else {
CredentialsMode::Include
});
let mut request = create_a_potential_CORS_request(
url_record,
Destination::None,
Some(cors_attribute_state),
Some(true),
)
.origin(global.origin().immutable().clone())
.pipeline_id(Some(global.pipeline_id()));
// Step 10
// TODO(eijebong): Replace once typed headers allow it

View file

@ -3,7 +3,6 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::dom::activation::Activatable;
use crate::dom::attr::Attr;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use crate::dom::bindings::codegen::Bindings::HTMLAnchorElementBinding;
@ -14,10 +13,9 @@ use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::bindings::str::{DOMString, USVString};
use crate::dom::document::determine_policy_for_token;
use crate::dom::document::Document;
use crate::dom::domtokenlist::DOMTokenList;
use crate::dom::element::Element;
use crate::dom::element::{referrer_policy_for_element, Element};
use crate::dom::event::Event;
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
@ -669,9 +667,7 @@ pub fn follow_hyperlink(subject: &Element, hyperlink_suffix: Option<String>) {
};
// Step 12.
let referrer_policy = subject
.get_attribute_by_name(DOMString::from_string(String::from("referrerpolicy")))
.and_then(|attribute: DomRoot<Attr>| (determine_policy_for_token(&attribute.Value())));
let referrer_policy = referrer_policy_for_element(subject);
// Step 13
let referrer = match subject.get_attribute(&ns!(), &local_name!("rel")) {

View file

@ -442,12 +442,19 @@ pub mod utils {
use crate::dom::window::Window;
use net_traits::image_cache::CanRequestImages;
use net_traits::image_cache::{ImageOrMetadataAvailable, ImageResponse, UsePlaceholder};
use net_traits::request::CorsSettings;
use servo_url::ServoUrl;
pub fn request_image_from_cache(window: &Window, url: ServoUrl) -> ImageResponse {
pub fn request_image_from_cache(
window: &Window,
url: ServoUrl,
cors_setting: Option<CorsSettings>,
) -> ImageResponse {
let image_cache = window.image_cache();
let response = image_cache.find_image_or_metadata(
url.into(),
window.origin().immutable().clone(),
cors_setting,
UsePlaceholder::No,
CanRequestImages::No,
);

View file

@ -20,6 +20,7 @@ use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
use crate::dom::bindings::str::{DOMString, USVString};
use crate::dom::document::Document;
use crate::dom::element::{cors_setting_for_element, referrer_policy_for_element};
use crate::dom::element::{reflect_cross_origin_attribute, set_cross_origin_attribute};
use crate::dom::element::{AttributeMutation, Element, RawLayoutElementHelpers};
use crate::dom::event::Event;
@ -40,6 +41,7 @@ use crate::dom::performanceresourcetiming::InitiatorType;
use crate::dom::values::UNSIGNED_LONG_MAX;
use crate::dom::virtualmethods::VirtualMethods;
use crate::dom::window::Window;
use crate::fetch::create_a_potential_CORS_request;
use crate::image_listener::{add_cache_listener_for_element, ImageCacheListener};
use crate::microtask::{Microtask, MicrotaskRunnable};
use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener};
@ -56,11 +58,11 @@ use mime::{self, Mime};
use msg::constellation_msg::PipelineId;
use net_traits::image::base::{Image, ImageMetadata};
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::request::RequestBuilder;
use net_traits::request::{CorsSettings, Destination, Initiator, RequestBuilder};
use net_traits::{FetchMetadata, FetchResponseListener, FetchResponseMsg, NetworkError};
use net_traits::{ResourceFetchTiming, ResourceTimingType};
use net_traits::{ReferrerPolicy, ResourceFetchTiming, ResourceTimingType};
use num_traits::ToPrimitive;
use servo_url::origin::ImmutableOrigin;
use servo_url::origin::MutableOrigin;
@ -184,6 +186,7 @@ impl FetchResponseListener for ImageContext {
fn process_request_eof(&mut self) {}
fn process_response(&mut self, metadata: Result<FetchMetadata, NetworkError>) {
debug!("got {:?} for {:?}", metadata.as_ref().map(|_| ()), self.url);
self.image_cache
.notify_pending_response(self.id, FetchResponseMsg::ProcessResponse(metadata.clone()));
@ -263,15 +266,31 @@ impl PreInvoke for ImageContext {
}
}
#[derive(PartialEq)]
pub(crate) enum FromPictureOrSrcSet {
Yes,
No,
}
// https://html.spec.whatwg.org/multipage/#update-the-image-data steps 17-20
// This function is also used to prefetch an image in `script::dom::servoparser::prefetch`.
pub(crate) fn image_fetch_request(
img_url: ServoUrl,
origin: ImmutableOrigin,
pipeline_id: PipelineId,
cors_setting: Option<CorsSettings>,
referrer_policy: Option<ReferrerPolicy>,
from_picture_or_srcset: FromPictureOrSrcSet,
) -> RequestBuilder {
RequestBuilder::new(img_url)
.origin(origin)
.pipeline_id(Some(pipeline_id))
let mut request =
create_a_potential_CORS_request(img_url, Destination::Image, cors_setting, None)
.origin(origin)
.pipeline_id(Some(pipeline_id))
.referrer_policy(referrer_policy);
if from_picture_or_srcset == FromPictureOrSrcSet::Yes {
request = request.initiator(Initiator::ImageSet);
}
request
}
impl HTMLImageElement {
@ -281,6 +300,8 @@ impl HTMLImageElement {
let image_cache = window.image_cache();
let response = image_cache.find_image_or_metadata(
img_url.clone().into(),
window.origin().immutable().clone(),
cors_setting_for_element(self.upcast()),
UsePlaceholder::Yes,
CanRequestImages::Yes,
);
@ -343,6 +364,13 @@ impl HTMLImageElement {
img_url.clone(),
document.origin().immutable().clone(),
document.global().pipeline_id(),
cors_setting_for_element(self.upcast()),
referrer_policy_for_element(self.upcast()),
if Self::uses_srcset_or_picture(self.upcast()) {
FromPictureOrSrcSet::Yes
} else {
FromPictureOrSrcSet::No
},
);
// This is a background load because the load blocker already fulfills the
@ -907,6 +935,8 @@ impl HTMLImageElement {
let image_cache = window.image_cache();
let response = image_cache.find_image_or_metadata(
img_url.clone().into(),
window.origin().immutable().clone(),
cors_setting_for_element(self.upcast()),
UsePlaceholder::No,
CanRequestImages::No,
);
@ -1062,6 +1092,8 @@ impl HTMLImageElement {
// Step 14
let response = image_cache.find_image_or_metadata(
img_url.clone().into(),
window.origin().immutable().clone(),
cors_setting_for_element(self.upcast()),
UsePlaceholder::No,
CanRequestImages::Yes,
);
@ -1268,6 +1300,10 @@ impl HTMLImageElement {
}
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
.borrow()
.final_url

View file

@ -58,7 +58,7 @@ use crate::dom::url::URL;
use crate::dom::videotrack::VideoTrack;
use crate::dom::videotracklist::VideoTrackList;
use crate::dom::virtualmethods::VirtualMethods;
use crate::fetch::FetchCanceller;
use crate::fetch::{create_a_potential_CORS_request, FetchCanceller};
use crate::microtask::{Microtask, MicrotaskRunnable};
use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener};
use crate::script_thread::ScriptThread;
@ -74,7 +74,7 @@ use ipc_channel::router::ROUTER;
use media::{glplayer_channel, GLPlayerMsg, GLPlayerMsgForward};
use net_traits::image::base::Image;
use net_traits::image_cache::ImageResponse;
use net_traits::request::{CredentialsMode, Destination, Referrer, RequestBuilder, RequestMode};
use net_traits::request::{Destination, Referrer};
use net_traits::{CoreResourceMsg, FetchChannels, FetchMetadata, FetchResponseListener, Metadata};
use net_traits::{NetworkError, ResourceFetchTiming, ResourceTimingType};
use script_layout_interface::HTMLMediaData;
@ -830,16 +830,9 @@ impl HTMLMediaElement {
None => self.blob_url.borrow().as_ref().unwrap().clone(),
};
let request = RequestBuilder::new(url.clone())
let cors_setting = cors_setting_for_element(self.upcast());
let request = create_a_potential_CORS_request(url.clone(), destination, cors_setting, None)
.headers(headers)
.destination(destination)
.credentials_mode(CredentialsMode::Include)
// https://html.spec.whatwg.org/multipage/#create-a-potential-cors-request
.mode(match cors_setting_for_element(self.upcast::<Element>()) {
Some(_) => RequestMode::CorsMode,
None => RequestMode::NoCors,
})
.use_url_credentials(true)
.origin(document.origin().immutable().clone())
.pipeline_id(Some(self.global().pipeline_id()))
.referrer(Some(Referrer::ReferrerUrl(document.url())))

View file

@ -25,6 +25,7 @@ use crate::dom::node::{document_from_node, window_from_node};
use crate::dom::node::{BindContext, ChildrenMutation, CloneChildrenFlag, Node};
use crate::dom::performanceresourcetiming::InitiatorType;
use crate::dom::virtualmethods::VirtualMethods;
use crate::fetch::create_a_potential_CORS_request;
use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener};
use dom_struct::dom_struct;
use encoding_rs::Encoding;
@ -33,9 +34,7 @@ use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
use js::jsval::UndefinedValue;
use msg::constellation_msg::PipelineId;
use net_traits::request::{
CorsSettings, CredentialsMode, Destination, Referrer, RequestBuilder, RequestMode,
};
use net_traits::request::{CorsSettings, Destination, Referrer, RequestBuilder};
use net_traits::ReferrerPolicy;
use net_traits::{FetchMetadata, FetchResponseListener, Metadata, NetworkError};
use net_traits::{ResourceFetchTiming, ResourceTimingType};
@ -306,20 +305,7 @@ pub(crate) fn script_fetch_request(
referrer_policy: Option<ReferrerPolicy>,
integrity_metadata: String,
) -> RequestBuilder {
RequestBuilder::new(url)
.destination(Destination::Script)
// https://html.spec.whatwg.org/multipage/#create-a-potential-cors-request
// Step 1
.mode(match cors_setting {
Some(_) => RequestMode::CorsMode,
None => RequestMode::NoCors,
})
// https://html.spec.whatwg.org/multipage/#create-a-potential-cors-request
// Step 3-4
.credentials_mode(match cors_setting {
Some(CorsSettings::Anonymous) => CredentialsMode::CredentialsSameOrigin,
_ => CredentialsMode::Include,
})
create_a_potential_CORS_request(url, Destination::Script, cors_setting, None)
.origin(origin)
.pipeline_id(Some(pipeline_id))
.referrer(Some(referrer))

View file

@ -159,6 +159,8 @@ impl HTMLVideoElement {
let image_cache = window.image_cache();
let response = image_cache.find_image_or_metadata(
poster_url.clone().into(),
window.origin().immutable().clone(),
None,
UsePlaceholder::No,
CanRequestImages::Yes,
);

View file

@ -4,8 +4,8 @@
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::trace::JSTraceable;
use crate::dom::document::Document;
use crate::dom::htmlimageelement::image_fetch_request;
use crate::dom::document::{determine_policy_for_token, Document};
use crate::dom::htmlimageelement::{image_fetch_request, FromPictureOrSrcSet};
use crate::dom::htmlscriptelement::script_fetch_request;
use crate::stylesheet_loader::stylesheet_fetch_request;
use html5ever::buffer_queue::BufferQueue;
@ -123,7 +123,14 @@ impl TokenSink for PrefetchSink {
(TagKind::StartTag, local_name!("img")) if self.prefetching => {
if let Some(url) = self.get_url(tag, local_name!("src")) {
debug!("Prefetch {} {}", tag.name, url);
let request = image_fetch_request(url, self.origin.clone(), self.pipeline_id);
let request = image_fetch_request(
url,
self.origin.clone(),
self.pipeline_id,
self.get_cors_settings(tag, local_name!("crossorigin")),
self.get_referrer_policy(tag, LocalName::from("referrerpolicy")),
FromPictureOrSrcSet::No,
);
let _ = self
.resource_threads
.send(CoreResourceMsg::Fetch(request, FetchChannels::Prefetch));
@ -137,6 +144,8 @@ impl TokenSink for PrefetchSink {
debug!("Prefetch {} {}", tag.name, url);
let cors_setting =
self.get_cors_settings(tag, local_name!("crossorigin"));
let referrer_policy =
self.get_referrer_policy(tag, LocalName::from("referrerpolicy"));
let integrity_metadata = self
.get_attr(tag, local_name!("integrity"))
.map(|attr| String::from(&attr.value))
@ -147,7 +156,7 @@ impl TokenSink for PrefetchSink {
self.origin.clone(),
self.pipeline_id,
self.referrer.clone(),
self.referrer_policy,
referrer_policy,
integrity_metadata,
);
let _ = self
@ -191,6 +200,12 @@ impl PrefetchSink {
ServoUrl::parse_with_base(Some(base), &attr.value).ok()
}
fn get_referrer_policy(&self, tag: &Tag, name: LocalName) -> Option<ReferrerPolicy> {
self.get_attr(tag, name)
.and_then(|attr| determine_policy_for_token(&*attr.value))
.or(self.referrer_policy)
}
fn get_cors_settings(&self, tag: &Tag, name: LocalName) -> Option<CorsSettings> {
let crossorigin = self.get_attr(tag, name)?;
if crossorigin.value.eq_ignore_ascii_case("anonymous") {

View file

@ -19,6 +19,7 @@ use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
use crate::dom::bindings::root::{Dom, DomOnceCell, DomRoot, LayoutDom, MutNullableDom};
use crate::dom::bindings::str::DOMString;
use crate::dom::element::cors_setting_for_element;
use crate::dom::event::{Event, EventBubbles, EventCancelable};
use crate::dom::htmlcanvaselement::utils as canvas_utils;
use crate::dom::htmlcanvaselement::HTMLCanvasElement;
@ -576,13 +577,15 @@ impl WebGLRenderingContext {
};
let window = window_from_node(&*self.canvas);
let cors_setting = cors_setting_for_element(image.upcast());
let img = match canvas_utils::request_image_from_cache(&window, img_url) {
ImageResponse::Loaded(img, _) => img,
ImageResponse::PlaceholderLoaded(_, _) |
ImageResponse::None |
ImageResponse::MetadataLoaded(_) => return Ok(None),
};
let img =
match canvas_utils::request_image_from_cache(&window, img_url, cors_setting) {
ImageResponse::Loaded(img, _) => img,
ImageResponse::PlaceholderLoaded(_, _) |
ImageResponse::None |
ImageResponse::MetadataLoaded(_) => return Ok(None),
};
let size = Size2D::new(img.width, img.height);

View file

@ -26,7 +26,9 @@ use crate::network_listener::{
use crate::task_source::TaskSourceName;
use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
use net_traits::request::RequestBuilder;
use net_traits::request::{
CorsSettings, CredentialsMode, Destination, RequestBuilder, RequestMode,
};
use net_traits::request::{Request as NetTraitsRequest, ServiceWorkersMode};
use net_traits::CoreResourceMsg::Fetch as NetTraitsFetch;
use net_traits::{CoreResourceMsg, CoreResourceThread, FetchResponseMsg};
@ -124,6 +126,7 @@ fn request_init_from_request(request: NetTraitsRequest) -> RequestBuilder {
integrity_metadata: "".to_owned(),
url_list: vec![],
parser_metadata: request.parser_metadata,
initiator: request.initiator,
}
}
@ -339,3 +342,29 @@ pub fn load_whole_resource(
}
}
}
/// https://html.spec.whatwg.org/multipage/#create-a-potential-cors-request
pub(crate) fn create_a_potential_CORS_request(
url: ServoUrl,
destination: Destination,
cors_setting: Option<CorsSettings>,
same_origin_fallback: Option<bool>,
) -> RequestBuilder {
RequestBuilder::new(url)
// https://html.spec.whatwg.org/multipage/#create-a-potential-cors-request
// Step 1
.mode(match cors_setting {
Some(_) => RequestMode::CorsMode,
None if same_origin_fallback == Some(true) => RequestMode::SameOrigin,
None => RequestMode::NoCors,
})
// https://html.spec.whatwg.org/multipage/#create-a-potential-cors-request
// Step 3-4
.credentials_mode(match cors_setting {
Some(CorsSettings::Anonymous) => CredentialsMode::CredentialsSameOrigin,
_ => CredentialsMode::Include,
})
// Step 5
.destination(destination)
.use_url_credentials(true)
}

View file

@ -16,6 +16,7 @@ use crate::dom::htmllinkelement::{HTMLLinkElement, RequestGenerationId};
use crate::dom::node::{containing_shadow_root, document_from_node, window_from_node};
use crate::dom::performanceresourcetiming::InitiatorType;
use crate::dom::shadowroot::ShadowRoot;
use crate::fetch::create_a_potential_CORS_request;
use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener};
use cssparser::SourceLocation;
use encoding_rs::UTF_8;
@ -23,9 +24,7 @@ use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
use mime::{self, Mime};
use msg::constellation_msg::PipelineId;
use net_traits::request::{
CorsSettings, CredentialsMode, Destination, Referrer, RequestBuilder, RequestMode,
};
use net_traits::request::{CorsSettings, Destination, Referrer, RequestBuilder};
use net_traits::{
FetchMetadata, FetchResponseListener, FilteredMetadata, Metadata, NetworkError, ReferrerPolicy,
};
@ -335,6 +334,7 @@ impl<'a> StylesheetLoader<'a> {
}
// This function is also used to prefetch a stylesheet in `script::dom::servoparser::prefetch`.
// https://html.spec.whatwg.org/multipage/#default-fetch-and-process-the-linked-resource
pub(crate) fn stylesheet_fetch_request(
url: ServoUrl,
cors_setting: Option<CorsSettings>,
@ -344,20 +344,7 @@ pub(crate) fn stylesheet_fetch_request(
referrer_policy: Option<ReferrerPolicy>,
integrity_metadata: String,
) -> RequestBuilder {
RequestBuilder::new(url)
.destination(Destination::Style)
// https://html.spec.whatwg.org/multipage/#create-a-potential-cors-request
// Step 1
.mode(match cors_setting {
Some(_) => RequestMode::CorsMode,
None => RequestMode::NoCors,
})
// https://html.spec.whatwg.org/multipage/#create-a-potential-cors-request
// Step 3-4
.credentials_mode(match cors_setting {
Some(CorsSettings::Anonymous) => CredentialsMode::CredentialsSameOrigin,
_ => CredentialsMode::Include,
})
create_a_potential_CORS_request(url, Destination::Style, cors_setting, None)
.origin(origin)
.pipeline_id(Some(pipeline_id))
.referrer(Some(referrer))

View file

@ -180,7 +180,8 @@ impl fmt::Display for ServoUrl {
impl fmt::Debug for ServoUrl {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
if self.0.as_str().len() > 40 {
let hasher = DefaultHasher::new();
let mut hasher = DefaultHasher::new();
hasher.write(self.0.as_str().as_bytes());
let truncated: String = self.0.as_str().chars().take(40).collect();
let result = format!("{}... ({:x})", truncated, hasher.finish());
return result.fmt(formatter);

View file

@ -8,7 +8,7 @@ use url::{Host, Origin};
use uuid::Uuid;
/// 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 {
/// A globally unique identifier
Opaque(OpaqueOrigin),
@ -82,7 +82,7 @@ impl ImmutableOrigin {
}
/// 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);
malloc_size_of_is_0!(OpaqueOrigin);

View file

@ -1,6 +1,62 @@
127.0.0.1 web-platform.test
127.0.0.1 www.web-platform.test
127.0.0.1 www1.web-platform.test
127.0.0.1 www2.web-platform.test
127.0.0.1 xn--n8j6ds53lwwkrqhv28a.web-platform.test
127.0.0.1 xn--lve-6lad.web-platform.test
127.0.0.1 www1.xn--n8j6ds53lwwkrqhv28a.web-platform.test
127.0.0.1 xn--lve-6lad.www1.web-platform.test
127.0.0.1 www.www2.not-web-platform.test
127.0.0.1 www2.www2.not-web-platform.test
127.0.0.1 xn--lve-6lad.www.not-web-platform.test
127.0.0.1 xn--n8j6ds53lwwkrqhv28a.not-web-platform.test
127.0.0.1 www.not-web-platform.test
127.0.0.1 xn--n8j6ds53lwwkrqhv28a.www.not-web-platform.test
127.0.0.1 not-web-platform.test
127.0.0.1 www1.www.not-web-platform.test
127.0.0.1 xn--n8j6ds53lwwkrqhv28a.www1.web-platform.test
127.0.0.1 www2.not-web-platform.test
127.0.0.1 www1.not-web-platform.test
127.0.0.1 www1.www1.web-platform.test
127.0.0.1 www2.xn--lve-6lad.web-platform.test
127.0.0.1 www1.xn--n8j6ds53lwwkrqhv28a.not-web-platform.test
127.0.0.1 xn--n8j6ds53lwwkrqhv28a.xn--n8j6ds53lwwkrqhv28a.web-platform.test
127.0.0.1 www1.www2.web-platform.test
127.0.0.1 xn--lve-6lad.xn--n8j6ds53lwwkrqhv28a.not-web-platform.test
127.0.0.1 xn--lve-6lad.xn--lve-6lad.not-web-platform.test
127.0.0.1 www2.www1.not-web-platform.test
127.0.0.1 www2.xn--n8j6ds53lwwkrqhv28a.web-platform.test
127.0.0.1 www.xn--n8j6ds53lwwkrqhv28a.not-web-platform.test
127.0.0.1 web-platform.test
127.0.0.1 xn--n8j6ds53lwwkrqhv28a.xn--lve-6lad.not-web-platform.test
127.0.0.1 xn--lve-6lad.www.web-platform.test
127.0.0.1 www.www.web-platform.test
127.0.0.1 www1.xn--lve-6lad.web-platform.test
127.0.0.1 xn--lve-6lad.www2.web-platform.test
127.0.0.1 www.www2.web-platform.test
127.0.0.1 www2.www.web-platform.test
127.0.0.1 xn--n8j6ds53lwwkrqhv28a.www2.not-web-platform.test
127.0.0.1 www1.www.web-platform.test
127.0.0.1 www.xn--lve-6lad.web-platform.test
127.0.0.1 www.www.not-web-platform.test
127.0.0.1 xn--lve-6lad.xn--lve-6lad.web-platform.test
127.0.0.1 xn--lve-6lad.www1.not-web-platform.test
127.0.0.1 www1.www1.not-web-platform.test
127.0.0.1 xn--n8j6ds53lwwkrqhv28a.xn--n8j6ds53lwwkrqhv28a.not-web-platform.test
127.0.0.1 xn--n8j6ds53lwwkrqhv28a.www2.web-platform.test
127.0.0.1 xn--n8j6ds53lwwkrqhv28a.www1.not-web-platform.test
127.0.0.1 www2.www2.web-platform.test
127.0.0.1 xn--lve-6lad.web-platform.test
127.0.0.1 xn--n8j6ds53lwwkrqhv28a.web-platform.test
127.0.0.1 xn--n8j6ds53lwwkrqhv28a.www.web-platform.test
127.0.0.1 xn--lve-6lad.not-web-platform.test
127.0.0.1 www2.xn--n8j6ds53lwwkrqhv28a.not-web-platform.test
127.0.0.1 www2.xn--lve-6lad.not-web-platform.test
127.0.0.1 xn--n8j6ds53lwwkrqhv28a.xn--lve-6lad.web-platform.test
127.0.0.1 www1.xn--lve-6lad.not-web-platform.test
127.0.0.1 www1.web-platform.test
127.0.0.1 www.www1.web-platform.test
127.0.0.1 www2.web-platform.test
127.0.0.1 www.xn--lve-6lad.not-web-platform.test
127.0.0.1 www.web-platform.test
127.0.0.1 xn--lve-6lad.xn--n8j6ds53lwwkrqhv28a.web-platform.test
127.0.0.1 www1.www2.not-web-platform.test
127.0.0.1 www2.www1.web-platform.test
127.0.0.1 www.www1.not-web-platform.test
127.0.0.1 www.xn--n8j6ds53lwwkrqhv28a.web-platform.test
127.0.0.1 xn--lve-6lad.www2.not-web-platform.test
127.0.0.1 www2.www.not-web-platform.test

View file

@ -290999,6 +290999,12 @@
{}
]
],
"2dcontext/drawing-images-to-the-canvas/drawimage_crossorigin.sub.html": [
[
"2dcontext/drawing-images-to-the-canvas/drawimage_crossorigin.sub.html",
{}
]
],
"2dcontext/drawing-images-to-the-canvas/drawimage_html_image.html": [
[
"2dcontext/drawing-images-to-the-canvas/drawimage_html_image.html",
@ -447926,6 +447932,10 @@
"9f297cacdcd81bef7093f79ebed6992110dab4d7",
"support"
],
"2dcontext/drawing-images-to-the-canvas/drawimage_crossorigin.sub.html": [
"3d57d9f064bec7755a4f735e3fd12850109fcc15",
"testharness"
],
"2dcontext/drawing-images-to-the-canvas/drawimage_html_image.html": [
"a94cfdcd2d66fb667d458a4dff91532fbf3608de",
"testharness"

View file

@ -1,2 +0,0 @@
[floats-in-table-caption-001.html]
expected: FAIL

View file

@ -1,4 +0,0 @@
[inline-negative-margin-001.html]
[#container 1]
expected: FAIL

View file

@ -1,2 +1,2 @@
[abspos-float-with-inline-container.html]
expected: FAIL
expected: TIMEOUT

View file

@ -0,0 +1,2 @@
[white-space-002.xht]
expected: FAIL

View file

@ -0,0 +1,2 @@
[white-space-003.xht]
expected: FAIL

View file

@ -0,0 +1,2 @@
[line-height-204.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[mix-blend-mode-paragraph.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[background-repeat-round-roundup.xht]
expected: FAIL

View file

@ -254,9 +254,6 @@
[Matching font-stretch: '90%' should prefer '90% 100%' over '50% 80%']
expected: FAIL
[Matching font-weight: '400' should prefer '450 460' over '500']
expected: FAIL
[Matching font-style: 'normal' should prefer 'normal' over 'oblique 0deg']
expected: FAIL

View file

@ -1,2 +0,0 @@
[line-break-normal-018.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[line-break-strict-018.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[text-transform-full-size-kana-001.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[text-transform-full-size-kana-002.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[text-transform-full-size-kana-003.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[text-transform-full-size-kana-004.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[trailing-ideographic-space-004.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[word-break-break-all-007.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[word-break-keep-all-006.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[css-transforms-3d-on-anonymous-block-001.html]
expected: FAIL

View file

@ -1,14 +0,0 @@
[HTMLMediaElement.html]
expected: CRASH
[controls on HTMLMediaElement in video must enqueue an attributeChanged reaction when replacing an existing attribute]
expected: FAIL
[controls on HTMLMediaElement in video must enqueue an attributeChanged reaction when adding a new attribute]
expected: FAIL
[controls on HTMLMediaElement in audio must enqueue an attributeChanged reaction when replacing an existing attribute]
expected: FAIL
[controls on HTMLMediaElement in audio must enqueue an attributeChanged reaction when adding a new attribute]
expected: FAIL

View file

@ -1,2 +0,0 @@
[contenttype_html.html]
expected: CRASH

View file

@ -1,2 +0,0 @@
[contenttype_xml.html]
expected: CRASH

View file

@ -6,7 +6,5 @@
[redirect-to-url-with-credentials]
expected: FAIL
[CORS Image loading after a redirect with a cross origin URL containing credentials]
expected: FAIL
[Frame loading after a redirect with an URL containing credentials]
expected: TIMEOUT

View file

@ -1,4 +0,0 @@
[traverse_the_history_2.html]
[Multiple history traversals, last would be aborted]
expected: FAIL

View file

@ -1,11 +1,7 @@
[form-submission-algorithm.html]
expected: TIMEOUT
[If form's firing submission events is true, then return; 'submit' event]
expected: FAIL
[If form's firing submission events is true, then return; 'invalid' event]
expected: FAIL
[Cannot navigate (after constructing the entry list)]
expected: TIMEOUT

View file

@ -1,9 +1,6 @@
[htmlanchorelement_noopener.html]
type: testharness
expected: ERROR
[Check that rel=noopener with target=_self does a normal load]
expected: FAIL
[Check that targeting of rel=noopener with a given name ignores an existing window with that name]
expected: NOTRUN

View file

@ -0,0 +1,5 @@
[promise-rejection-events-iframe.html]
expected: TIMEOUT
[delayed handling: promise is created in iframe and being rejected elsewhere]
expected: TIMEOUT

View file

@ -0,0 +1,5 @@
[promise-rejection-events-onerror.html]
expected: TIMEOUT
[Throwing inside an unhandledrejection handler invokes the error handler.]
expected: TIMEOUT

View file

@ -1,4 +0,0 @@
[cross-http.keep-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-http origin and keep-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-http.no-redirect.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-http origin and no-redirect redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-https.keep-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-https origin and keep-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-https.no-redirect.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-https origin and no-redirect redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-https.swap-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-https origin and swap-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[same-http.swap-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to same-http origin and swap-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[same-https.keep-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to same-https origin and keep-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[same-https.no-redirect.http.html]
[Referrer Policy: Expects omitted for img-tag to same-https origin and no-redirect redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[same-https.swap-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to same-https origin and swap-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-http.keep-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-http origin and keep-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-http.no-redirect.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-http origin and no-redirect redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-https.keep-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-https origin and keep-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-https.no-redirect.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-https origin and no-redirect redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-https.swap-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-https origin and swap-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[same-http.swap-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to same-http origin and swap-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[same-https.swap-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to same-https origin and swap-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-http.keep-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-http origin and keep-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-http.no-redirect.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-http origin and no-redirect redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-https.keep-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-https origin and keep-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-https.no-redirect.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-https origin and no-redirect redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-https.swap-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-https origin and swap-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[same-http.swap-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to same-http origin and swap-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[same-https.keep-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to same-https origin and keep-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[same-https.no-redirect.http.html]
[Referrer Policy: Expects omitted for img-tag to same-https origin and no-redirect redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[same-https.swap-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to same-https origin and swap-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-http.keep-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-http origin and keep-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-http.no-redirect.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-http origin and no-redirect redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-https.keep-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-https origin and keep-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-https.no-redirect.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-https origin and no-redirect redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-https.swap-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-https origin and swap-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[same-http.swap-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to same-http origin and swap-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[same-https.swap-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to same-https origin and swap-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-http.keep-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-http origin and keep-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-http.no-redirect.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-http origin and no-redirect redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-https.keep-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-https origin and keep-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-https.no-redirect.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-https origin and no-redirect redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-https.swap-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-https origin and swap-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[same-http.swap-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to same-http origin and swap-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[same-https.keep-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to same-https origin and keep-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[same-https.no-redirect.http.html]
[Referrer Policy: Expects omitted for img-tag to same-https origin and no-redirect redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[same-https.swap-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to same-https origin and swap-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-http.keep-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-http origin and keep-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-http.no-redirect.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-http origin and no-redirect redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-https.keep-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-https origin and keep-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-https.no-redirect.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-https origin and no-redirect redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[cross-https.swap-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to cross-https origin and swap-origin redirection from http context.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[same-http.swap-origin.http.html]
[Referrer Policy: Expects omitted for img-tag to same-http origin and swap-origin redirection from http context.]
expected: FAIL

Some files were not shown because too many files have changed in this diff Show more