diff options
author | Josh Matthews <josh@joshmatthews.net> | 2019-10-03 17:36:02 -0400 |
---|---|---|
committer | Josh Matthews <josh@joshmatthews.net> | 2019-10-04 15:08:40 -0400 |
commit | 1df8d57dc6adcf56c22b45053b3d2eca904d17d3 (patch) | |
tree | ce2ba8d6672925a95d551a0ece8ee9e279332e3e /components | |
parent | 583536c9406e685405d5b28da99dd8cab94c7d66 (diff) | |
download | servo-1df8d57dc6adcf56c22b45053b3d2eca904d17d3.tar.gz servo-1df8d57dc6adcf56c22b45053b3d2eca904d17d3.zip |
Support CORS attributes for image elements.
Diffstat (limited to 'components')
-rw-r--r-- | components/layout/context.rs | 1 | ||||
-rw-r--r-- | components/net/image_cache.rs | 62 | ||||
-rw-r--r-- | components/net_traits/image_cache.rs | 2 | ||||
-rw-r--r-- | components/net_traits/request.rs | 12 | ||||
-rw-r--r-- | components/script/dom/canvasrenderingcontext2d.rs | 48 | ||||
-rw-r--r-- | components/script/dom/element.rs | 12 | ||||
-rw-r--r-- | components/script/dom/eventsource.rs | 24 | ||||
-rw-r--r-- | components/script/dom/htmlanchorelement.rs | 8 | ||||
-rw-r--r-- | components/script/dom/htmlcanvaselement.rs | 8 | ||||
-rw-r--r-- | components/script/dom/htmlimageelement.rs | 38 | ||||
-rw-r--r-- | components/script/dom/htmlmediaelement.rs | 15 | ||||
-rw-r--r-- | components/script/dom/htmlscriptelement.rs | 20 | ||||
-rw-r--r-- | components/script/dom/htmlvideoelement.rs | 1 | ||||
-rw-r--r-- | components/script/dom/servoparser/prefetch.rs | 23 | ||||
-rw-r--r-- | components/script/dom/webglrenderingcontext.rs | 17 | ||||
-rw-r--r-- | components/script/fetch.rs | 31 | ||||
-rw-r--r-- | components/script/stylesheet_loader.rs | 21 |
17 files changed, 232 insertions, 111 deletions
diff --git a/components/layout/context.rs b/components/layout/context.rs index 5c373ac668d..90b427b0e15 100644 --- a/components/layout/context.rs +++ b/components/layout/context.rs @@ -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, ); diff --git a/components/net/image_cache.rs b/components/net/image_cache.rs index 89397483ee3..1504f1c06e5 100644 --- a/components/net/image_cache.rs +++ b/components/net/image_cache.rs @@ -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), } diff --git a/components/net_traits/image_cache.rs b/components/net_traits/image_cache.rs index 3126b37d8b5..203941227d0 100644 --- a/components/net_traits/image_cache.rs +++ b/components/net_traits/image_cache.rs @@ -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>; diff --git a/components/net_traits/request.rs b/components/net_traits/request.rs index cfceb5ee888..dad0ae8ca35 100644 --- a/components/net_traits/request.rs +++ b/components/net_traits/request.rs @@ -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; diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs index 380b9a8312a..c964422c27c 100644 --- a/components/script/dom/canvasrenderingcontext2d.rs +++ b/components/script/dom/canvasrenderingcontext2d.rs @@ -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)?, }; diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 4b16db4699e..baf4fcd0878 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -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), diff --git a/components/script/dom/eventsource.rs b/components/script/dom/eventsource.rs index 02283a3c190..92eb2c2f414 100644 --- a/components/script/dom/eventsource.rs +++ b/components/script/dom/eventsource.rs @@ -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 diff --git a/components/script/dom/htmlanchorelement.rs b/components/script/dom/htmlanchorelement.rs index cb197a054f3..c5b3fb51362 100644 --- a/components/script/dom/htmlanchorelement.rs +++ b/components/script/dom/htmlanchorelement.rs @@ -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")) { diff --git a/components/script/dom/htmlcanvaselement.rs b/components/script/dom/htmlcanvaselement.rs index eb62d920ca9..9e638539995 100644 --- a/components/script/dom/htmlcanvaselement.rs +++ b/components/script/dom/htmlcanvaselement.rs @@ -442,13 +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, ); diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index 95f00a2b007..a8dcfca5b92 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -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}; @@ -58,9 +60,9 @@ use net_traits::image::base::{Image, ImageMetadata}; use net_traits::image_cache::UsePlaceholder; 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; @@ -263,15 +265,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 { @@ -282,6 +300,7 @@ impl HTMLImageElement { 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, ); @@ -344,6 +363,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 @@ -909,6 +935,7 @@ impl HTMLImageElement { 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, ); @@ -1065,6 +1092,7 @@ impl HTMLImageElement { 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, ); diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index e3cb0dfb5b2..fb14f32c789 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -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; @@ -822,16 +822,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()))) diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index 4d2cf061804..1bd0101c03d 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -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)) diff --git a/components/script/dom/htmlvideoelement.rs b/components/script/dom/htmlvideoelement.rs index 4a1816b5cd4..9b7fc13fcb1 100644 --- a/components/script/dom/htmlvideoelement.rs +++ b/components/script/dom/htmlvideoelement.rs @@ -133,6 +133,7 @@ impl HTMLVideoElement { let response = image_cache.find_image_or_metadata( poster_url.clone().into(), window.origin().immutable().clone(), + None, UsePlaceholder::No, CanRequestImages::Yes, ); diff --git a/components/script/dom/servoparser/prefetch.rs b/components/script/dom/servoparser/prefetch.rs index a2977cc15b1..716be8b0f51 100644 --- a/components/script/dom/servoparser/prefetch.rs +++ b/components/script/dom/servoparser/prefetch.rs @@ -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") { diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index 46b9c72c6ca..9aa8bfe053a 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -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 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 cors_setting = cors_setting_for_element(image.upcast()); + + 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); diff --git a/components/script/fetch.rs b/components/script/fetch.rs index 0b093c5501d..8c31f24f260 100644 --- a/components/script/fetch.rs +++ b/components/script/fetch.rs @@ -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) +} diff --git a/components/script/stylesheet_loader.rs b/components/script/stylesheet_loader.rs index 258286dd6ea..82b5bbdf532 100644 --- a/components/script/stylesheet_loader.rs +++ b/components/script/stylesheet_loader.rs @@ -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)) |