aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/link_relations.rs
diff options
context:
space:
mode:
authorSimon Wülker <simon.wuelker@arcor.de>2024-09-09 20:52:56 +0200
committerGitHub <noreply@github.com>2024-09-09 18:52:56 +0000
commit10e5bb72d9e16655b625b8971e346ff479b17fd2 (patch)
tree3b88c6083e89992487e99a73ed8fe204373803e3 /components/script/link_relations.rs
parent2993577ac0ea2638a1dde3cfb9e4cb7b45b542ae (diff)
downloadservo-10e5bb72d9e16655b625b8971e346ff479b17fd2.tar.gz
servo-10e5bb72d9e16655b625b8971e346ff479b17fd2.zip
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>
Diffstat (limited to 'components/script/link_relations.rs')
-rw-r--r--components/script/link_relations.rs131
1 files changed, 131 insertions, 0 deletions
diff --git a/components/script/link_relations.rs b/components/script/link_relations.rs
new file mode 100644
index 00000000000..d32ea9e4e92
--- /dev/null
+++ b/components/script/link_relations.rs
@@ -0,0 +1,131 @@
+/* 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);