diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs
index 77c33b2ab2c..34f95116b74 100644
--- a/components/script/dom/htmllinkelement.rs
+++ b/components/script/dom/htmllinkelement.rs
@@ -11,8 +11,13 @@ use base::id::WebViewId;
use dom_struct::dom_struct;
use embedder_traits::EmbedderMsg;
use html5ever::{LocalName, Prefix, local_name, ns};
+use ipc_channel::ipc::IpcSender;
use js::rust::HandleObject;
use mime::Mime;
+use net_traits::image_cache::{
+ Image, ImageCache, ImageCacheResponseMessage, ImageCacheResult, ImageLoadListener,
+ ImageOrMetadataAvailable, ImageResponse, PendingImageId, UsePlaceholder,
+};
use net_traits::mime_classifier::{MediaType, MimeClassifier};
use net_traits::policy_container::PolicyContainer;
use net_traits::request::{
@@ -20,15 +25,17 @@ use net_traits::request::{
RequestId,
};
use net_traits::{
- FetchMetadata, FetchResponseListener, NetworkError, ReferrerPolicy, ResourceFetchTiming,
- ResourceTimingType,
+ FetchMetadata, FetchResponseListener, FetchResponseMsg, NetworkError, ReferrerPolicy,
+ ResourceFetchTiming, ResourceTimingType,
};
+use pixels::PixelFormat;
use script_bindings::root::Dom;
use servo_arc::Arc;
use servo_url::{ImmutableOrigin, ServoUrl};
use style::attr::AttrValue;
use style::stylesheets::Stylesheet;
use stylo_atoms::Atom;
+use webrender_api::units::DeviceIntSize;
use crate::dom::attr::Attr;
use crate::dom::bindings::cell::DomRefCell;
@@ -272,8 +279,7 @@ impl VirtualMethods for HTMLLinkElement {
}
if self.relations.get().contains(LinkRelations::ICON) {
- let sizes = get_attr(self.upcast(), &local_name!("sizes"));
- self.handle_favicon_url(&attr.value(), &sizes);
+ self.handle_favicon_url();
}
// https://html.spec.whatwg.org/multipage/#link-type-prefetch
@@ -291,9 +297,7 @@ impl VirtualMethods for HTMLLinkElement {
}
},
local_name!("sizes") if self.relations.get().contains(LinkRelations::ICON) => {
- if let Some(ref href) = get_attr(self.upcast(), &local_name!("href")) {
- self.handle_favicon_url(href, &Some(attr.value().to_string()));
- }
+ self.handle_favicon_url();
},
local_name!("crossorigin") => {
// https://html.spec.whatwg.org/multipage/#link-type-prefetch
@@ -396,8 +400,7 @@ impl VirtualMethods for HTMLLinkElement {
}
if relations.contains(LinkRelations::ICON) {
- let sizes = get_attr(self.upcast(), &local_name!("sizes"));
- self.handle_favicon_url(&href, &sizes);
+ self.handle_favicon_url();
}
if relations.contains(LinkRelations::PREFETCH) {
@@ -483,6 +486,48 @@ impl HTMLLinkElement {
options
}
+ ///
+ ///
+ /// This method does not implement Step 7 (fetching the request) and instead returns the [RequestBuilder],
+ /// as the fetch context that should be used depends on the link type.
+ fn default_fetch_and_process_the_linked_resource(&self) -> Option {
+ // Step 1. Let options be the result of creating link options from el.
+ let options = self.processing_options();
+
+ // Step 2. Let request be the result of creating a link request given options.
+ let Some(request) = options.create_link_request(self.owner_window().webview_id()) else {
+ // Step 3. If request is null, then return.
+ return None;
+ };
+ // Step 4. Set request's synchronous flag.
+ let mut request = request.synchronous(true);
+
+ // Step 5. Run the linked resource fetch setup steps, given el and request. If the result is false, then return.
+ if !self.linked_resource_fetch_setup(&mut request) {
+ return None;
+ }
+
+ // TODO Step 6. Set request's initiator type to "css" if el's rel attribute
+ // contains the keyword stylesheet; "link" otherwise.
+
+ // Step 7. Fetch request with processResponseConsumeBody set to the following steps given response response and null,
+ // failure, or a byte sequence bodyBytes: [..]
+ Some(request)
+ }
+
+ ///
+ fn linked_resource_fetch_setup(&self, request: &mut RequestBuilder) -> bool {
+ if self.relations.get().contains(LinkRelations::ICON) {
+ // Step 1. Set request's destination to "image".
+ request.destination = Destination::Image;
+
+ // Step 2. Return true.
+ return true;
+ }
+
+ true
+ }
+
/// The `fetch and process the linked resource` algorithm for [`rel="prefetch"`](https://html.spec.whatwg.org/multipage/#link-type-prefetch)
fn fetch_and_process_prefetch_link(&self, href: &str) {
// Step 1. If el's href attribute's value is the empty string, then return.
@@ -589,17 +634,162 @@ impl HTMLLinkElement {
}
}
- fn handle_favicon_url(&self, href: &str, _sizes: &Option) {
- let document = self.owner_document();
- match document.base_url().join(href) {
- Ok(url) => {
- let window = document.window();
- if window.is_top_level() {
- let msg = EmbedderMsg::NewFavicon(document.webview_id(), url.clone());
- window.send_to_embedder(msg);
+ fn handle_favicon_url(&self) {
+ // The spec does not specify this, but we don't fetch favicons for iframes, as
+ // they won't be displayed anyways.
+ let window = self.owner_window();
+ if !window.is_top_level() {
+ return;
+ }
+ let Ok(href) = self.Href().parse() else {
+ return;
+ };
+
+ // Ignore all previous fetch operations
+ self.request_generation_id
+ .set(self.request_generation_id.get().increment());
+
+ let cache_result = window.image_cache().get_cached_image_status(
+ href,
+ window.origin().immutable().clone(),
+ cors_setting_for_element(self.upcast()),
+ UsePlaceholder::No,
+ );
+
+ match cache_result {
+ ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable {
+ image,
+ is_placeholder,
+ ..
+ }) => {
+ debug_assert!(!is_placeholder);
+
+ self.process_favicon_response(image);
+ },
+ ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(_, id)) |
+ ImageCacheResult::Pending(id) => {
+ let sender = self.register_image_cache_callback(id);
+ window.image_cache().add_listener(ImageLoadListener::new(
+ sender,
+ window.pipeline_id(),
+ id,
+ ));
+ },
+ ImageCacheResult::ReadyForRequest(id) => {
+ let Some(request) = self.default_fetch_and_process_the_linked_resource() else {
+ return;
+ };
+
+ let sender = self.register_image_cache_callback(id);
+ window.image_cache().add_listener(ImageLoadListener::new(
+ sender,
+ window.pipeline_id(),
+ id,
+ ));
+
+ let document = self.upcast::().owner_doc();
+ let fetch_context = FaviconFetchContext {
+ url: self.owner_document().base_url(),
+ image_cache: window.image_cache(),
+ id,
+ link: Trusted::new(self),
+ resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource),
+ };
+ document.fetch_background(request, fetch_context);
+ },
+ ImageCacheResult::LoadError => {},
+ };
+ }
+
+ fn register_image_cache_callback(
+ &self,
+ id: PendingImageId,
+ ) -> IpcSender {
+ let trusted_node = Trusted::new(self);
+ let window = self.owner_window();
+ let request_generation_id = self.get_request_generation_id();
+ window.register_image_cache_listener(id, move |response| {
+ let trusted_node = trusted_node.clone();
+ let link_element = trusted_node.root();
+ let window = link_element.owner_window();
+
+ let ImageResponse::Loaded(image, _) = response.response else {
+ // We don't care about metadata and such for favicons.
+ return;
+ };
+
+ if request_generation_id != link_element.get_request_generation_id() {
+ // This load is no longer relevant.
+ return;
+ };
+
+ window
+ .as_global_scope()
+ .task_manager()
+ .networking_task_source()
+ .queue(task!(process_favicon_response: move || {
+ let element = trusted_node.root();
+
+ if request_generation_id != element.get_request_generation_id() {
+ // This load is no longer relevant.
+ return;
+ };
+
+ element.process_favicon_response(image);
+ }));
+ })
+ }
+
+ /// Rasterizes a loaded favicon file if necessary and notifies the embedder about it.
+ fn process_favicon_response(&self, image: Image) {
+ // TODO: Include the size attribute here
+ let window = self.owner_window();
+
+ let send_rasterized_favicon_to_embedder = |raster_image: &pixels::RasterImage| {
+ // Let's not worry about animated favicons...
+ let frame = raster_image.first_frame();
+
+ let format = match raster_image.format {
+ PixelFormat::K8 => embedder_traits::PixelFormat::K8,
+ PixelFormat::KA8 => embedder_traits::PixelFormat::KA8,
+ PixelFormat::RGB8 => embedder_traits::PixelFormat::RGB8,
+ PixelFormat::RGBA8 => embedder_traits::PixelFormat::RGBA8,
+ PixelFormat::BGRA8 => embedder_traits::PixelFormat::BGRA8,
+ };
+
+ let embedder_image = embedder_traits::Image::new(
+ frame.width,
+ frame.height,
+ raster_image.bytes.clone(),
+ raster_image.frames[0].byte_range.clone(),
+ format,
+ );
+ window.send_to_embedder(EmbedderMsg::NewFavicon(window.webview_id(), embedder_image));
+ };
+
+ match image {
+ Image::Raster(raster_image) => send_rasterized_favicon_to_embedder(&raster_image),
+ Image::Vector(vector_image) => {
+ // This size is completely arbitrary.
+ let size = DeviceIntSize::new(250, 250);
+
+ let image_cache = window.image_cache();
+ if let Some(raster_image) =
+ image_cache.rasterize_vector_image(vector_image.id, size)
+ {
+ send_rasterized_favicon_to_embedder(&raster_image);
+ } else {
+ // The rasterization callback will end up calling "process_favicon_response" again,
+ // but this time with a raster image.
+ let image_cache_sender = self.register_image_cache_callback(vector_image.id);
+ image_cache.add_rasterization_complete_listener(
+ window.pipeline_id(),
+ vector_image.id,
+ size,
+ image_cache_sender,
+ );
}
},
- Err(e) => debug!("Parsing url {} failed: {}", href, e),
}
}
@@ -962,6 +1152,93 @@ fn translate_a_preload_destination(potential_destination: &str) -> Destination {
}
}
+struct FaviconFetchContext {
+ /// The `` element that caused this fetch operation
+ link: Trusted,
+ image_cache: std::sync::Arc,
+ id: PendingImageId,
+
+ /// The base url of the document that the `` element belongs to.
+ url: ServoUrl,
+
+ resource_timing: ResourceFetchTiming,
+}
+
+impl FetchResponseListener for FaviconFetchContext {
+ fn process_request_body(&mut self, _: RequestId) {}
+
+ fn process_request_eof(&mut self, _: RequestId) {}
+
+ fn process_response(
+ &mut self,
+ request_id: RequestId,
+ metadata: Result,
+ ) {
+ self.image_cache.notify_pending_response(
+ self.id,
+ FetchResponseMsg::ProcessResponse(request_id, metadata.clone()),
+ );
+ }
+
+ fn process_response_chunk(&mut self, request_id: RequestId, chunk: Vec) {
+ self.image_cache.notify_pending_response(
+ self.id,
+ FetchResponseMsg::ProcessResponseChunk(request_id, chunk),
+ );
+ }
+
+ fn process_response_eof(
+ &mut self,
+ request_id: RequestId,
+ response: Result,
+ ) {
+ self.image_cache.notify_pending_response(
+ self.id,
+ FetchResponseMsg::ProcessResponseEOF(request_id, response),
+ );
+ }
+
+ fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming {
+ &mut self.resource_timing
+ }
+
+ fn resource_timing(&self) -> &ResourceFetchTiming {
+ &self.resource_timing
+ }
+
+ fn submit_resource_timing(&mut self) {
+ submit_timing(self, CanGc::note())
+ }
+
+ fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) {
+ let global = &self.resource_timing_global();
+ let link = self.link.root();
+ let source_position = link
+ .upcast::()
+ .compute_source_position(link.line_number as u32);
+ global.report_csp_violations(violations, None, Some(source_position));
+ }
+}
+
+impl ResourceTimingListener for FaviconFetchContext {
+ fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
+ (
+ InitiatorType::LocalName("link".to_string()),
+ self.url.clone(),
+ )
+ }
+
+ fn resource_timing_global(&self) -> DomRoot {
+ self.link.root().upcast::().owner_doc().global()
+ }
+}
+
+impl PreInvoke for FaviconFetchContext {
+ fn should_invoke(&self) -> bool {
+ true
+ }
+}
+
struct PrefetchContext {
/// The `` element that caused this prefetch operation
link: Trusted,
diff --git a/components/servo/lib.rs b/components/servo/lib.rs
index 60564068063..1ab513151f6 100644
--- a/components/servo/lib.rs
+++ b/components/servo/lib.rs
@@ -799,9 +799,9 @@ impl Servo {
webview.set_cursor(cursor);
}
},
- EmbedderMsg::NewFavicon(webview_id, url) => {
+ EmbedderMsg::NewFavicon(webview_id, image) => {
if let Some(webview) = self.get_webview_handle(webview_id) {
- webview.set_favicon_url(url.into_url());
+ webview.set_favicon(image);
}
},
EmbedderMsg::NotifyLoadStatusChanged(webview_id, load_status) => {
diff --git a/components/servo/webview.rs b/components/servo/webview.rs
index bde21fa8bb4..cbf2caf9da9 100644
--- a/components/servo/webview.rs
+++ b/components/servo/webview.rs
@@ -13,7 +13,7 @@ use compositing_traits::WebViewTrait;
use constellation_traits::{EmbedderToConstellationMessage, TraversalDirection};
use dpi::PhysicalSize;
use embedder_traits::{
- Cursor, FocusId, InputEvent, JSValue, JavaScriptEvaluationError, LoadStatus,
+ Cursor, FocusId, Image, InputEvent, JSValue, JavaScriptEvaluationError, LoadStatus,
MediaSessionActionType, ScreenGeometry, Theme, TraversalId, ViewportDetails,
};
use euclid::{Point2D, Scale, Size2D};
@@ -84,7 +84,7 @@ pub(crate) struct WebViewInner {
url: Option,
status_text: Option,
page_title: Option,
- favicon_url: Option,
+ favicon: Option,
focused: bool,
animating: bool,
cursor: Cursor,
@@ -126,7 +126,7 @@ impl WebView {
url: None,
status_text: None,
page_title: None,
- favicon_url: None,
+ favicon: None,
focused: false,
animating: false,
cursor: Cursor::Pointer,
@@ -264,21 +264,13 @@ impl WebView {
self.delegate().notify_page_title_changed(self, new_value);
}
- pub fn favicon_url(&self) -> Option {
- self.inner().favicon_url.clone()
+ pub fn favicon(&self) -> Option[> {
+ Ref::filter_map(self.inner(), |inner| inner.favicon.as_ref()).ok()
}
- pub(crate) fn set_favicon_url(self, new_value: Url) {
- if self
- .inner()
- .favicon_url
- .as_ref()
- .is_some_and(|url| url == &new_value)
- {
- return;
- }
- self.inner_mut().favicon_url = Some(new_value.clone());
- self.delegate().notify_favicon_url_changed(self, new_value);
+ pub(crate) fn set_favicon(self, new_value: Image) {
+ self.inner_mut().favicon = Some(new_value);
+ self.delegate().notify_favicon_changed(self);
}
pub fn focused(&self) -> bool {
diff --git a/components/servo/webview_delegate.rs b/components/servo/webview_delegate.rs
index e55c0fb0134..58ff20605f0 100644
--- a/components/servo/webview_delegate.rs
+++ b/components/servo/webview_delegate.rs
@@ -432,9 +432,9 @@ pub trait WebViewDelegate {
/// The [`Cursor`] of the currently loaded page in this [`WebView`] has changed. The new
/// cursor can accessed via [`WebView::cursor`].
fn notify_cursor_changed(&self, _webview: WebView, _: Cursor) {}
- /// The favicon [`Url`] of the currently loaded page in this [`WebView`] has changed. The new
- /// favicon [`Url`] can accessed via [`WebView::favicon_url`].
- fn notify_favicon_url_changed(&self, _webview: WebView, _: Url) {}
+ /// The favicon of the currently loaded page in this [`WebView`] has changed. The new
+ /// favicon [`Image`] can accessed via [`WebView::favicon`].
+ fn notify_favicon_changed(&self, _webview: WebView) {}
/// Notify the embedder that it needs to present a new frame.
fn notify_new_frame_ready(&self, _webview: WebView) {}
diff --git a/components/shared/embedder/lib.rs b/components/shared/embedder/lib.rs
index e96a1c8a999..ac500519768 100644
--- a/components/shared/embedder/lib.rs
+++ b/components/shared/embedder/lib.rs
@@ -17,6 +17,7 @@ use std::collections::HashMap;
use std::ffi::c_void;
use std::fmt::{Debug, Display, Error, Formatter};
use std::hash::Hash;
+use std::ops::Range;
use std::path::PathBuf;
use std::sync::Arc;
@@ -24,7 +25,7 @@ use base::id::{PipelineId, WebViewId};
use crossbeam_channel::Sender;
use euclid::{Point2D, Scale, Size2D};
use http::{HeaderMap, Method, StatusCode};
-use ipc_channel::ipc::IpcSender;
+use ipc_channel::ipc::{IpcSender, IpcSharedMemory};
use log::warn;
use malloc_size_of::malloc_size_of_is_0;
use malloc_size_of_derive::MallocSizeOf;
@@ -358,6 +359,54 @@ impl TraversalId {
}
}
+#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)]
+pub enum PixelFormat {
+ /// Luminance channel only
+ K8,
+ /// Luminance + alpha
+ KA8,
+ /// RGB, 8 bits per channel
+ RGB8,
+ /// RGB + alpha, 8 bits per channel
+ RGBA8,
+ /// BGR + alpha, 8 bits per channel
+ BGRA8,
+}
+
+/// A raster image buffer.
+#[derive(Clone, Deserialize, Serialize)]
+pub struct Image {
+ pub width: u32,
+ pub height: u32,
+ pub format: PixelFormat,
+ /// A shared memory block containing the data of one or more image frames.
+ data: IpcSharedMemory,
+ range: Range,
+}
+
+impl Image {
+ pub fn new(
+ width: u32,
+ height: u32,
+ data: IpcSharedMemory,
+ range: Range,
+ format: PixelFormat,
+ ) -> Self {
+ Self {
+ width,
+ height,
+ format,
+ data,
+ range,
+ }
+ }
+
+ /// Return the bytes belonging to the first image frame.
+ pub fn data(&self) -> &[u8] {
+ &self.data[self.range.clone()]
+ }
+}
+
#[derive(Deserialize, IntoStaticStr, Serialize)]
pub enum EmbedderMsg {
/// A status message to be displayed by the browser chrome.
@@ -411,7 +460,7 @@ pub enum EmbedderMsg {
/// Changes the cursor.
SetCursor(WebViewId, Cursor),
/// A favicon was detected
- NewFavicon(WebViewId, ServoUrl),
+ NewFavicon(WebViewId, Image),
/// The history state has changed.
HistoryChanged(WebViewId, Vec, usize),
/// A history traversal operation completed.
diff --git a/components/url/lib.rs b/components/url/lib.rs
index 93906e5cf54..da44a794381 100644
--- a/components/url/lib.rs
+++ b/components/url/lib.rs
@@ -15,6 +15,7 @@ use std::hash::Hasher;
use std::net::IpAddr;
use std::ops::{Index, Range, RangeFrom, RangeFull, RangeTo};
use std::path::Path;
+use std::str::FromStr;
use malloc_size_of_derive::MallocSizeOf;
use serde::{Deserialize, Serialize};
@@ -307,3 +308,12 @@ impl From> for ServoUrl {
ServoUrl(url)
}
}
+
+impl FromStr for ServoUrl {
+ type Err = ::Err;
+
+ fn from_str(value: &str) -> Result {
+ let url = Url::from_str(value)?;
+ Ok(url.into())
+ }
+}
diff --git a/tests/wpt/meta/content-security-policy/img-src/icon-blocked.sub.html.ini b/tests/wpt/meta/content-security-policy/img-src/icon-blocked.sub.html.ini
deleted file mode 100644
index 1ed665f5245..00000000000
--- a/tests/wpt/meta/content-security-policy/img-src/icon-blocked.sub.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[icon-blocked.sub.html]
- expected: TIMEOUT
- [Test that spv event is fired]
- expected: NOTRUN
diff --git a/tests/wpt/meta/fetch/metadata/generated/element-link-icon.https.sub.html.ini b/tests/wpt/meta/fetch/metadata/generated/element-link-icon.https.sub.html.ini
index 413df63e004..ecfb73c57a0 100644
--- a/tests/wpt/meta/fetch/metadata/generated/element-link-icon.https.sub.html.ini
+++ b/tests/wpt/meta/fetch/metadata/generated/element-link-icon.https.sub.html.ini
@@ -1,67 +1,6 @@
[element-link-icon.https.sub.html]
- expected: TIMEOUT
- [sec-fetch-site - Same origin no attributes]
- expected: TIMEOUT
-
- [sec-fetch-site - Cross-site no attributes]
- expected: NOTRUN
-
- [sec-fetch-site - Same site no attributes]
- expected: NOTRUN
-
- [sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect no attributes]
- expected: NOTRUN
-
- [sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect no attributes]
- expected: NOTRUN
-
- [sec-fetch-site - Cross-Site -> Same Origin no attributes]
- expected: NOTRUN
-
- [sec-fetch-site - Cross-Site -> Same-Site no attributes]
- expected: NOTRUN
-
- [sec-fetch-site - Cross-Site -> Cross-Site no attributes]
- expected: NOTRUN
-
- [sec-fetch-site - Same-Origin -> Same Origin no attributes]
- expected: NOTRUN
-
- [sec-fetch-site - Same-Origin -> Same-Site no attributes]
- expected: NOTRUN
-
- [sec-fetch-site - Same-Origin -> Cross-Site no attributes]
- expected: NOTRUN
-
- [sec-fetch-site - Same-Site -> Same Origin no attributes]
- expected: NOTRUN
-
- [sec-fetch-site - Same-Site -> Same-Site no attributes]
- expected: NOTRUN
-
- [sec-fetch-site - Same-Site -> Cross-Site no attributes]
- expected: NOTRUN
-
- [sec-fetch-mode no attributes]
- expected: NOTRUN
-
- [sec-fetch-mode attributes: crossorigin]
- expected: NOTRUN
-
- [sec-fetch-mode attributes: crossorigin=anonymous]
- expected: NOTRUN
-
- [sec-fetch-mode attributes: crossorigin=use-credentials]
- expected: NOTRUN
-
[sec-fetch-dest no attributes]
- expected: NOTRUN
-
- [sec-fetch-user no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-storage-access - Cross-site no attributes]
- expected: NOTRUN
-
- [sec-fetch-storage-access - Same site no attributes]
- expected: NOTRUN
+ expected: FAIL
diff --git a/tests/wpt/meta/fetch/metadata/generated/element-link-icon.sub.html.ini b/tests/wpt/meta/fetch/metadata/generated/element-link-icon.sub.html.ini
index da5d7731a74..d44bd202a00 100644
--- a/tests/wpt/meta/fetch/metadata/generated/element-link-icon.sub.html.ini
+++ b/tests/wpt/meta/fetch/metadata/generated/element-link-icon.sub.html.ini
@@ -1,55 +1,6 @@
[element-link-icon.sub.html]
- expected: TIMEOUT
- [sec-fetch-site - Not sent to non-trustworthy same-origin destination no attributes]
- expected: TIMEOUT
-
- [sec-fetch-site - Not sent to non-trustworthy same-site destination no attributes]
- expected: NOTRUN
-
- [sec-fetch-site - Not sent to non-trustworthy cross-site destination no attributes]
- expected: NOTRUN
-
- [sec-fetch-mode - Not sent to non-trustworthy same-origin destination no attributes]
- expected: NOTRUN
-
- [sec-fetch-mode - Not sent to non-trustworthy same-site destination no attributes]
- expected: NOTRUN
-
- [sec-fetch-mode - Not sent to non-trustworthy cross-site destination no attributes]
- expected: NOTRUN
-
- [sec-fetch-dest - Not sent to non-trustworthy same-origin destination no attributes]
- expected: NOTRUN
-
- [sec-fetch-dest - Not sent to non-trustworthy same-site destination no attributes]
- expected: NOTRUN
-
- [sec-fetch-dest - Not sent to non-trustworthy cross-site destination no attributes]
- expected: NOTRUN
-
- [sec-fetch-user - Not sent to non-trustworthy same-origin destination no attributes]
- expected: NOTRUN
-
- [sec-fetch-user - Not sent to non-trustworthy same-site destination no attributes]
- expected: NOTRUN
-
- [sec-fetch-user - Not sent to non-trustworthy cross-site destination no attributes]
- expected: NOTRUN
-
[sec-fetch-site - HTTPS downgrade (header not sent) no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-site - HTTPS upgrade no attributes]
- expected: NOTRUN
-
- [sec-fetch-site - HTTPS downgrade-upgrade no attributes]
- expected: NOTRUN
-
- [sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination no attributes]
- expected: NOTRUN
-
- [sec-fetch-storage-access - Not sent to non-trustworthy same-site destination no attributes]
- expected: NOTRUN
-
- [sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination no attributes]
- expected: NOTRUN
+ expected: FAIL
]