servo/components/script/link_relations.rs
Simon Wülker 10e5bb72d9
Initial support for <link rel="prefetch"> (#33345)
* Properly store link relations

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

* Send fetch request for prefetch links

We don't actually *do* anything with the response yet
(handle errors etc) but its a first step.

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

* Fire load/error events for prefetch loads

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

* Set prefetch destination/cors setting correctly

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

* Update WPT expectations

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

* Fix ./mach test-tidy errors

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

* Set correct "Accept" value for prefetch requests

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

* Add spec text to individual steps

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

---------

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
2024-09-09 18:52:56 +00:00

131 lines
4.9 KiB
Rust

/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 html5ever::{local_name, namespace_url, ns};
use malloc_size_of::malloc_size_of_is_0;
use style::str::HTML_SPACE_CHARACTERS;
use crate::dom::types::Element;
bitflags::bitflags! {
#[derive(Clone, Copy, Debug)]
pub struct LinkRelations: u32 {
const ALTERNATE = 1;
const AUTHOR = 1 << 1;
const BOOKMARK = 1 << 2;
const CANONICAL = 1 << 3;
const DNS_PREFETCH = 1 << 4;
const EXPECT = 1 << 5;
const EXTERNAL = 1 << 6;
const HELP = 1 << 7;
const ICON = 1 << 8;
const LICENSE = 1 << 9;
const NEXT = 1 << 10;
const MANIFEST = 1 << 11;
const MODULE_PRELOAD = 1 << 12;
const NO_FOLLOW = 1 << 13;
const NO_OPENER = 1 << 14;
const NO_REFERRER = 1 << 15;
const OPENER = 1 << 16;
const PING_BACK = 1 << 17;
const PRECONNECT = 1 << 18;
const PREFETCH = 1 << 19;
const PRELOAD = 1 << 20;
const PREV = 1 << 21;
const PRIVACY_POLICY = 1 << 22;
const SEARCH = 1 << 23;
const STYLESHEET = 1 << 24;
const TAG = 1 << 25;
const TermsOfService = 1 << 26;
}
}
impl LinkRelations {
pub fn for_element(element: &Element) -> Self {
let rel = element.get_attribute(&ns!(), &local_name!("rel")).map(|e| {
let value = e.value();
(**value).to_owned()
});
// FIXME: for a, area and form elements we need to allow a different
// set of attributes
let mut relations = rel
.map(|attribute| {
attribute
.split(HTML_SPACE_CHARACTERS)
.map(Self::from_single_keyword_for_link_element)
.collect()
})
.unwrap_or(Self::empty());
// For historical reasons, "rev=made" is treated as if the "author" relation was specified
let has_legacy_author_relation = element
.get_attribute(&ns!(), &local_name!("rev"))
.is_some_and(|rev| &**rev.value() == "made");
if has_legacy_author_relation {
relations |= Self::AUTHOR;
}
relations
}
/// Parse one of the relations allowed for the `<link>` element
///
/// If the keyword is invalid then `Self::empty` is returned.
fn from_single_keyword_for_link_element(keyword: &str) -> Self {
if keyword.eq_ignore_ascii_case("alternate") {
Self::ALTERNATE
} else if keyword.eq_ignore_ascii_case("canonical") {
Self::CANONICAL
} else if keyword.eq_ignore_ascii_case("author") {
Self::AUTHOR
} else if keyword.eq_ignore_ascii_case("dns-prefetch") {
Self::DNS_PREFETCH
} else if keyword.eq_ignore_ascii_case("expect") {
Self::EXPECT
} else if keyword.eq_ignore_ascii_case("help") {
Self::HELP
} else if keyword.eq_ignore_ascii_case("icon") ||
keyword.eq_ignore_ascii_case("shortcut icon") ||
keyword.eq_ignore_ascii_case("apple-touch-icon")
{
// TODO: "apple-touch-icon" is not in the spec. Where did it come from? Do we need it?
// There is also "apple-touch-icon-precomposed" listed in
// https://github.com/servo/servo/blob/e43e4778421be8ea30db9d5c553780c042161522/components/script/dom/htmllinkelement.rs#L452-L467
Self::ICON
} else if keyword.eq_ignore_ascii_case("manifest") {
Self::MANIFEST
} else if keyword.eq_ignore_ascii_case("modulepreload") {
Self::MODULE_PRELOAD
} else if keyword.eq_ignore_ascii_case("license") ||
keyword.eq_ignore_ascii_case("copyright")
{
Self::LICENSE
} else if keyword.eq_ignore_ascii_case("next") {
Self::NEXT
} else if keyword.eq_ignore_ascii_case("pingback") {
Self::PING_BACK
} else if keyword.eq_ignore_ascii_case("preconnect") {
Self::PRECONNECT
} else if keyword.eq_ignore_ascii_case("prefetch") {
Self::PREFETCH
} else if keyword.eq_ignore_ascii_case("preload") {
Self::PRELOAD
} else if keyword.eq_ignore_ascii_case("prev") || keyword.eq_ignore_ascii_case("previous") {
Self::PREV
} else if keyword.eq_ignore_ascii_case("privacy-policy") {
Self::PRIVACY_POLICY
} else if keyword.eq_ignore_ascii_case("search") {
Self::SEARCH
} else if keyword.eq_ignore_ascii_case("stylesheet") {
Self::STYLESHEET
} else if keyword.eq_ignore_ascii_case("terms-of-service") {
Self::TermsOfService
} else {
Self::empty()
}
}
}
malloc_size_of_is_0!(LinkRelations);