diff --git a/components/layout/context.rs b/components/layout/context.rs index 536d108ea04..00e0c5a0cc1 100644 --- a/components/layout/context.rs +++ b/components/layout/context.rs @@ -163,7 +163,7 @@ impl<'a> LayoutContext<'a> { } match self.get_or_request_image_or_meta(node, url.clone(), use_placeholder) { - Some(ImageOrMetadataAvailable::ImageAvailable(image, _)) => { + Some(ImageOrMetadataAvailable::ImageAvailable { image, .. }) => { let image_info = WebRenderImageInfo::from_image(&*image); if image_info.key.is_none() { Some(image_info) diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 289dc3f3094..062db9c0a7e 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -431,8 +431,8 @@ impl ImageFragmentInfo { layout_context .get_or_request_image_or_meta(node.opaque(), url, UsePlaceholder::Yes) .map(|result| match result { - ImageOrMetadataAvailable::ImageAvailable(i, _) => { - ImageOrMetadata::Image(i) + ImageOrMetadataAvailable::ImageAvailable { image, .. } => { + ImageOrMetadata::Image(image) }, ImageOrMetadataAvailable::MetadataAvailable(m) => { ImageOrMetadata::Metadata(m) diff --git a/components/layout_2020/context.rs b/components/layout_2020/context.rs index 109a1a6935b..1c9a0f7162e 100644 --- a/components/layout_2020/context.rs +++ b/components/layout_2020/context.rs @@ -113,7 +113,7 @@ impl<'a> LayoutContext<'a> { } match self.get_or_request_image_or_meta(node, url.clone(), use_placeholder) { - Some(ImageOrMetadataAvailable::ImageAvailable(image, _)) => { + Some(ImageOrMetadataAvailable::ImageAvailable { image, .. }) => { let image_info = WebRenderImageInfo { width: image.width, height: image.height, diff --git a/components/layout_2020/replaced.rs b/components/layout_2020/replaced.rs index f3501ce0e71..d10e03198ac 100644 --- a/components/layout_2020/replaced.rs +++ b/components/layout_2020/replaced.rs @@ -125,7 +125,7 @@ impl ReplacedContent { image_url.clone(), UsePlaceholder::No, ) { - Some(ImageOrMetadataAvailable::ImageAvailable(image, _)) => { + Some(ImageOrMetadataAvailable::ImageAvailable { image, .. }) => { (Some(image.clone()), image.width as f32, image.height as f32) }, Some(ImageOrMetadataAvailable::MetadataAvailable(metadata)) => { diff --git a/components/net/http_loader.rs b/components/net/http_loader.rs index c7002c4c9b4..db14a9ca4d3 100644 --- a/components/net/http_loader.rs +++ b/components/net/http_loader.rs @@ -33,6 +33,7 @@ use http::{HeaderMap, Request as HyperRequest}; use hyper::{Body, Client, Method, Response as HyperResponse, StatusCode}; use hyper_serde::Serde; use msg::constellation_msg::{HistoryStateId, PipelineId}; +use net_traits::pub_domains::reg_suffix; use net_traits::quality::{quality_to_value, Quality, QualityItem}; use net_traits::request::Origin::Origin as SpecificOrigin; use net_traits::request::{is_cors_safelisted_method, is_cors_safelisted_request_header}; @@ -189,6 +190,28 @@ fn strict_origin_when_cross_origin(referrer_url: ServoUrl, url: ServoUrl) -> Opt strip_url(referrer_url, cross_origin) } +/// https://html.spec.whatwg.org/multipage/#schemelessly-same-site +fn is_schemelessy_same_site(site_a: &ImmutableOrigin, site_b: &ImmutableOrigin) -> bool { + // Step 1 + if !site_a.is_tuple() && !site_b.is_tuple() && site_a == site_b { + true + } else if site_a.is_tuple() && site_b.is_tuple() { + // Step 2.1 + let host_a = site_a.host().map(|h| h.to_string()).unwrap_or_default(); + let host_b = site_b.host().map(|h| h.to_string()).unwrap_or_default(); + + let host_a_reg = reg_suffix(&host_a); + let host_b_reg = reg_suffix(&host_b); + + // Step 2.2-2.3 + (site_a.host() == site_b.host() && host_a_reg == "") || + (host_a_reg == host_b_reg && host_a_reg != "") + } else { + // Step 3 + false + } +} + /// fn strip_url(mut referrer_url: ServoUrl, origin_only: bool) -> Option { const MAX_REFERRER_URL_LENGTH: usize = 4096; @@ -1251,7 +1274,74 @@ fn http_network_or_cache_fetch( // TODO: if necessary set response's range-requested flag // Step 9 - // TODO: handle CORS not set and cross-origin blocked + // https://fetch.spec.whatwg.org/#cross-origin-resource-policy-check + #[derive(PartialEq)] + enum CrossOriginResourcePolicy { + Allowed, + Blocked, + } + + fn cross_origin_resource_policy_check( + request: &Request, + response: &Response, + ) -> CrossOriginResourcePolicy { + // Step 1 + if request.mode != RequestMode::NoCors { + return CrossOriginResourcePolicy::Allowed; + } + + // Step 2 + let current_url_origin = request.current_url().origin(); + let same_origin = if let Origin::Origin(ref origin) = request.origin { + *origin == request.current_url().origin() + } else { + false + }; + + if same_origin { + return CrossOriginResourcePolicy::Allowed; + } + + // Step 3 + let policy = response + .headers + .get(HeaderName::from_static("cross-origin-resource-policy")) + .map(|h| h.to_str().unwrap_or("")) + .unwrap_or(""); + + // Step 4 + if policy == "same-origin" { + return CrossOriginResourcePolicy::Blocked; + } + + // Step 5 + if let Origin::Origin(ref request_origin) = request.origin { + let schemeless_same_origin = + is_schemelessy_same_site(&request_origin, ¤t_url_origin); + if schemeless_same_origin && + (request_origin.scheme() == Some("https") || + response.https_state == HttpsState::None) + { + return CrossOriginResourcePolicy::Allowed; + } + }; + + // Step 6 + if policy == "same-site" { + return CrossOriginResourcePolicy::Blocked; + } + + CrossOriginResourcePolicy::Allowed + } + + if http_request.response_tainting != ResponseTainting::CorsTainting && + cross_origin_resource_policy_check(&http_request, &response) == + CrossOriginResourcePolicy::Blocked + { + return Response::network_error(NetworkError::Internal( + "Cross-origin resource policy check failed".into(), + )); + } // Step 10 // FIXME: Figure out what to do with request window objects diff --git a/components/net/image_cache.rs b/components/net/image_cache.rs index d32061da06a..8a5e6a53749 100644 --- a/components/net/image_cache.rs +++ b/components/net/image_cache.rs @@ -466,9 +466,12 @@ impl ImageCache for ImageCacheImpl { match result { Ok((image, image_url)) => { debug!("{} is available", url); - return ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable( - image, image_url, - )); + let is_placeholder = image_url == store.placeholder_url; + return ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable { + image, + url: image_url, + is_placeholder, + }); }, Err(()) => { debug!("{} is not available", url); @@ -515,9 +518,14 @@ impl ImageCache for ImageCacheImpl { // TODO: make this behaviour configurable according to the caller's needs. store.handle_decoder(decoded); match store.get_completed_image_if_available(url, origin, cors_setting, use_placeholder) { - Some(Ok((image, image_url))) => ImageCacheResult::Available( - ImageOrMetadataAvailable::ImageAvailable(image, image_url), - ), + Some(Ok((image, image_url))) => { + let is_placeholder = image_url == store.placeholder_url; + ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable { + image, + url: image_url, + is_placeholder, + }) + }, _ => ImageCacheResult::LoadError, } } diff --git a/components/net_traits/image_cache.rs b/components/net_traits/image_cache.rs index 39d501a271f..e3c1a565881 100644 --- a/components/net_traits/image_cache.rs +++ b/components/net_traits/image_cache.rs @@ -17,7 +17,12 @@ use std::sync::Arc; /// Indicating either entire image or just metadata availability #[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] pub enum ImageOrMetadataAvailable { - ImageAvailable(#[ignore_malloc_size_of = "Arc"] Arc, ServoUrl), + ImageAvailable { + #[ignore_malloc_size_of = "Arc"] + image: Arc, + url: ServoUrl, + is_placeholder: bool, + }, MetadataAvailable(ImageMetadata), } diff --git a/components/net_traits/response.rs b/components/net_traits/response.rs index b2342a332b4..0c12546bebe 100644 --- a/components/net_traits/response.rs +++ b/components/net_traits/response.rs @@ -61,7 +61,7 @@ pub enum CacheState { } /// [Https state](https://fetch.spec.whatwg.org/#concept-response-https-state) -#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, Serialize)] +#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] pub enum HttpsState { None, Deprecated, diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index 8041fb5685e..2e99e491653 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -329,8 +329,16 @@ impl HTMLImageElement { ); match cache_result { - ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable(image, url)) => { - self.process_image_response(ImageResponse::Loaded(image, url)) + ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable { + image, + url, + is_placeholder, + }) => { + if is_placeholder { + self.process_image_response(ImageResponse::PlaceholderLoaded(image, url)) + } else { + self.process_image_response(ImageResponse::Loaded(image, url)) + } }, ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(m)) => { self.process_image_response(ImageResponse::MetadataLoaded(m)) @@ -1109,7 +1117,7 @@ impl HTMLImageElement { ); match cache_result { - ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable(_image, _url)) => { + ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable { .. }) => { // Step 15 self.finish_reacting_to_environment_change( selected_source, diff --git a/components/script/dom/htmlvideoelement.rs b/components/script/dom/htmlvideoelement.rs index c06dc083a22..cea37bc173c 100644 --- a/components/script/dom/htmlvideoelement.rs +++ b/components/script/dom/htmlvideoelement.rs @@ -166,8 +166,12 @@ impl HTMLVideoElement { ); match cache_result { - ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable(img, url)) => { - self.process_image_response(ImageResponse::Loaded(img, url)); + ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable { + image, + url, + .. + }) => { + self.process_image_response(ImageResponse::Loaded(image, url)); }, ImageCacheResult::ReadyForRequest(id) => { self.do_fetch_poster_frame(poster_url, id, cancel_receiver) diff --git a/tests/wpt/metadata/MANIFEST.json b/tests/wpt/metadata/MANIFEST.json index 71913f1d6d6..aa1de55396a 100644 --- a/tests/wpt/metadata/MANIFEST.json +++ b/tests/wpt/metadata/MANIFEST.json @@ -424608,7 +424608,7 @@ ] ], "image-loads.html": [ - "8a0458f107abdf2b7d6664fb8194e6b4b0222989", + "060b7551ea516837cf416c797e85474658857632", [ null, {} @@ -424654,7 +424654,7 @@ ] ], "script-loads.html": [ - "5850e0109f18c23e40d73686bef5e4b6a6b40686", + "a9690fc70be13885d7ca6448730c83f755810774", [ null, {} diff --git a/tests/wpt/metadata/fetch/cross-origin-resource-policy/fetch-in-iframe.html.ini b/tests/wpt/metadata/fetch/cross-origin-resource-policy/fetch-in-iframe.html.ini index 3951cd266d5..293bf2f9f27 100644 --- a/tests/wpt/metadata/fetch/cross-origin-resource-policy/fetch-in-iframe.html.ini +++ b/tests/wpt/metadata/fetch/cross-origin-resource-policy/fetch-in-iframe.html.ini @@ -5,15 +5,3 @@ [fetch-in-iframe] expected: FAIL - [Cross-origin fetch in a data: iframe load fails if the server blocks cross-origin loads with a 'Cross-Origin-Resource-Policy: same-origin' response header.] - expected: FAIL - - [Cross-origin fetch in a data: iframe load fails if the server blocks cross-origin loads with a 'Cross-Origin-Resource-Policy: same-site' response header.] - expected: FAIL - - [Cross-origin fetch in a cross origin iframe load fails if the server blocks cross-origin loads with a 'Cross-Origin-Resource-Policy: same-origin' response header.] - expected: FAIL - - [Cross-origin fetch in a cross origin iframe load fails if the server blocks cross-origin loads with a 'Cross-Origin-Resource-Policy: same-site' response header.] - expected: FAIL - diff --git a/tests/wpt/metadata/fetch/cross-origin-resource-policy/fetch.any.js.ini b/tests/wpt/metadata/fetch/cross-origin-resource-policy/fetch.any.js.ini index 55e4b8ec274..20f8d0fffc6 100644 --- a/tests/wpt/metadata/fetch/cross-origin-resource-policy/fetch.any.js.ini +++ b/tests/wpt/metadata/fetch/cross-origin-resource-policy/fetch.any.js.ini @@ -7,53 +7,11 @@ [fetch] expected: FAIL - [Valid cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-site' response header.] - expected: FAIL - - [Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header after a redirection.] - expected: FAIL - - [Cross-origin no-cors fetch to a same-site URL with a 'Cross-Origin-Resource-Policy: same-origin' response header.] - expected: FAIL - - [Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' redirect response header.] - expected: FAIL - - [Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header.] - expected: FAIL - - [Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-site' response header.] - expected: FAIL - - [Cross-scheme (HTTP to HTTPS) no-cors fetch to a same-site URL with a 'Cross-Origin-Resource-Policy: same-site' response header.] - expected: FAIL - [fetch.any.worker.html] [fetch] expected: FAIL - [Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header.] - expected: FAIL - - [Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-site' response header.] - expected: FAIL - - [Cross-origin no-cors fetch to a same-site URL with a 'Cross-Origin-Resource-Policy: same-origin' response header.] - expected: FAIL - - [Valid cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-site' response header.] - expected: FAIL - - [Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header after a redirection.] - expected: FAIL - - [Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' redirect response header.] - expected: FAIL - - [Cross-scheme (HTTP to HTTPS) no-cors fetch to a same-site URL with a 'Cross-Origin-Resource-Policy: same-site' response header.] - expected: FAIL - [fetch.any.sharedworker.html] expected: ERROR diff --git a/tests/wpt/metadata/fetch/cross-origin-resource-policy/fetch.https.any.js.ini b/tests/wpt/metadata/fetch/cross-origin-resource-policy/fetch.https.any.js.ini index 2c43b33a3a1..db4cfcfdf94 100644 --- a/tests/wpt/metadata/fetch/cross-origin-resource-policy/fetch.https.any.js.ini +++ b/tests/wpt/metadata/fetch/cross-origin-resource-policy/fetch.https.any.js.ini @@ -1,16 +1,4 @@ [fetch.https.any.html] - [Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header after a redirection.] - expected: FAIL - - [Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' redirect response header.] - expected: FAIL - - [Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header.] - expected: FAIL - - [Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-site' response header.] - expected: FAIL - [fetch.https.any.serviceworker.html] expected: ERROR @@ -25,18 +13,6 @@ [fetch.https.any.worker.html] - [Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header after a redirection.] - expected: FAIL - - [Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' redirect response header.] - expected: FAIL - - [Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header.] - expected: FAIL - - [Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-site' response header.] - expected: FAIL - [fetch] expected: FAIL diff --git a/tests/wpt/metadata/fetch/cross-origin-resource-policy/iframe-loads.html.ini b/tests/wpt/metadata/fetch/cross-origin-resource-policy/iframe-loads.html.ini index 62283b995da..df9e6ef13e0 100644 --- a/tests/wpt/metadata/fetch/cross-origin-resource-policy/iframe-loads.html.ini +++ b/tests/wpt/metadata/fetch/cross-origin-resource-policy/iframe-loads.html.ini @@ -1,8 +1,6 @@ [iframe-loads.html] [Untitled] expected: FAIL - [Load an iframe that has Cross-Origin-Resource-Policy header] - expected: FAIL [iframe-loads] expected: FAIL diff --git a/tests/wpt/metadata/fetch/cross-origin-resource-policy/image-loads.html.ini b/tests/wpt/metadata/fetch/cross-origin-resource-policy/image-loads.html.ini deleted file mode 100644 index e28824f5560..00000000000 --- a/tests/wpt/metadata/fetch/cross-origin-resource-policy/image-loads.html.ini +++ /dev/null @@ -1,19 +0,0 @@ -[image-loads.html] - [Same-origin image load with a 'Cross-Origin-Resource-Policy: same-origin' response header.] - expected: FAIL - - [Same-origin image load with a 'Cross-Origin-Resource-Policy: same-site' response header.] - expected: FAIL - - [Cross-origin cors image load with a 'Cross-Origin-Resource-Policy: same-origin' response header.] - expected: FAIL - - [Cross-origin cors image load with a 'Cross-Origin-Resource-Policy: same-site' response header.] - expected: FAIL - - [Cross-origin no-cors image load with a 'Cross-Origin-Resource-Policy: same-origin' response header.] - expected: FAIL - - [Cross-origin no-cors image load with a 'Cross-Origin-Resource-Policy: same-site' response header.] - expected: FAIL - diff --git a/tests/wpt/metadata/fetch/cross-origin-resource-policy/scheme-restriction.any.js.ini b/tests/wpt/metadata/fetch/cross-origin-resource-policy/scheme-restriction.any.js.ini deleted file mode 100644 index 92ed8a87544..00000000000 --- a/tests/wpt/metadata/fetch/cross-origin-resource-policy/scheme-restriction.any.js.ini +++ /dev/null @@ -1,9 +0,0 @@ -[scheme-restriction.any.html] - [Cross-Origin-Resource-Policy: same-site blocks retrieving HTTPS from HTTP] - expected: FAIL - - -[scheme-restriction.any.worker.html] - [Cross-Origin-Resource-Policy: same-site blocks retrieving HTTPS from HTTP] - expected: FAIL - diff --git a/tests/wpt/metadata/fetch/cross-origin-resource-policy/script-loads.html.ini b/tests/wpt/metadata/fetch/cross-origin-resource-policy/script-loads.html.ini deleted file mode 100644 index 1a9132484c8..00000000000 --- a/tests/wpt/metadata/fetch/cross-origin-resource-policy/script-loads.html.ini +++ /dev/null @@ -1,19 +0,0 @@ -[script-loads.html] - [Same-origin script load with a 'Cross-Origin-Resource-Policy: same-origin' response header.] - expected: FAIL - - [Same-origin script load with a 'Cross-Origin-Resource-Policy: same-site' response header.] - expected: FAIL - - [Cross-origin cors script load with a 'Cross-Origin-Resource-Policy: same-origin' response header.] - expected: FAIL - - [Cross-origin cors script load with a 'Cross-Origin-Resource-Policy: same-site' response header.] - expected: FAIL - - [Cross-origin no-cors script load with a 'Cross-Origin-Resource-Policy: same-origin' response header.] - expected: FAIL - - [Cross-origin no-cors script load with a 'Cross-Origin-Resource-Policy: same-site' response header.] - expected: FAIL - diff --git a/tests/wpt/web-platform-tests/fetch/cross-origin-resource-policy/image-loads.html b/tests/wpt/web-platform-tests/fetch/cross-origin-resource-policy/image-loads.html index 8a0458f107a..060b7551ea5 100644 --- a/tests/wpt/web-platform-tests/fetch/cross-origin-resource-policy/image-loads.html +++ b/tests/wpt/web-platform-tests/fetch/cross-origin-resource-policy/image-loads.html @@ -16,6 +16,7 @@ const noCors = false; function loadImage(url, shoudLoad, corsMode, title) { + const testDiv = document.getElementById("testDiv"); promise_test(() => { const img = new Image(); if (corsMode) diff --git a/tests/wpt/web-platform-tests/fetch/cross-origin-resource-policy/script-loads.html b/tests/wpt/web-platform-tests/fetch/cross-origin-resource-policy/script-loads.html index 5850e0109f1..a9690fc70be 100644 --- a/tests/wpt/web-platform-tests/fetch/cross-origin-resource-policy/script-loads.html +++ b/tests/wpt/web-platform-tests/fetch/cross-origin-resource-policy/script-loads.html @@ -16,6 +16,7 @@ const noCors = false; function loadScript(url, shoudLoad, corsMode, title) { + const testDiv = document.getElementById("testDiv"); promise_test(() => { const script = document.createElement("script"); if (corsMode)