From 9bdc46d66b68ceac79d026118d9602a8e4c20118 Mon Sep 17 00:00:00 2001 From: Sebastian C Date: Mon, 7 Apr 2025 17:29:12 -0500 Subject: [PATCH] fix: meta referrer updating to follow spec (#36390) Previously the referrer policy used tree order but the spec only cares about the most-recently-updated or most-recently-added meta referrer. Testing: change has existing WPT tests --------- Signed-off-by: Sebastian C --- components/script/dom/htmlheadelement.rs | 33 +------------------ components/script/dom/htmlmetaelement.rs | 32 +++++++++++++++--- ...a-changed-after-second-added.http.html.ini | 3 -- ...eta-referrer-outofhead-fetch.http.html.ini | 3 -- .../meta-referrer-removed-2.http.html.ini | 3 -- 5 files changed, 28 insertions(+), 46 deletions(-) delete mode 100644 tests/wpt/meta/referrer-policy/generic/first-meta-changed-after-second-added.http.html.ini delete mode 100644 tests/wpt/meta/referrer-policy/generic/meta-referrer-outofhead-fetch.http.html.ini delete mode 100644 tests/wpt/meta/referrer-policy/generic/meta-referrer-removed-2.http.html.ini diff --git a/components/script/dom/htmlheadelement.rs b/components/script/dom/htmlheadelement.rs index 51b2c025982..b4845fc949c 100644 --- a/components/script/dom/htmlheadelement.rs +++ b/components/script/dom/htmlheadelement.rs @@ -10,7 +10,7 @@ use js::rust::HandleObject; use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::root::DomRoot; -use crate::dom::document::{Document, determine_policy_for_token}; +use crate::dom::document::Document; use crate::dom::element::Element; use crate::dom::htmlelement::HTMLElement; use crate::dom::htmlmetaelement::HTMLMetaElement; @@ -54,37 +54,6 @@ impl HTMLHeadElement { n } - /// - pub(crate) fn set_document_referrer(&self) { - let doc = self.owner_document(); - - if doc.GetHead().as_deref() != Some(self) { - return; - } - - let node = self.upcast::(); - let candidates = node - .traverse_preorder(ShadowIncluding::No) - .filter_map(DomRoot::downcast::) - .filter(|elem| elem.is::()) - .filter(|elem| elem.get_name() == Some(atom!("referrer"))) - .filter(|elem| { - elem.get_attribute(&ns!(), &local_name!("content")) - .is_some() - }); - - for meta in candidates { - if let Some(ref content) = meta.get_attribute(&ns!(), &local_name!("content")) { - let content = content.value(); - let content_val = content.trim(); - if !content_val.is_empty() { - doc.set_referrer_policy(determine_policy_for_token(content_val)); - return; - } - } - } - } - /// pub(crate) fn set_content_security_policy(&self) { let doc = self.owner_document(); diff --git a/components/script/dom/htmlmetaelement.rs b/components/script/dom/htmlmetaelement.rs index fd95ac1abbf..ac4dcbf0db6 100644 --- a/components/script/dom/htmlmetaelement.rs +++ b/components/script/dom/htmlmetaelement.rs @@ -8,7 +8,7 @@ use std::time::Duration; use constellation_traits::NavigationHistoryBehavior; use dom_struct::dom_struct; -use html5ever::{LocalName, Prefix}; +use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; use js::rust::HandleObject; use regex::bytes::Regex; use servo_url::ServoUrl; @@ -21,7 +21,7 @@ use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; -use crate::dom::document::{DeclarativeRefresh, Document}; +use crate::dom::document::{DeclarativeRefresh, Document, determine_policy_for_token}; use crate::dom::element::{AttributeMutation, Element}; use crate::dom::htmlelement::HTMLElement; use crate::dom::htmlheadelement::HTMLHeadElement; @@ -115,9 +115,31 @@ impl HTMLMetaElement { /// fn apply_referrer(&self) { - if let Some(parent) = self.upcast::().GetParentElement() { - if let Some(head) = parent.downcast::() { - head.set_document_referrer(); + let doc = self.owner_document(); + // From spec: For historical reasons, unlike other standard metadata names, the processing model for referrer + // is not responsive to element removals, and does not use tree order. Only the most-recently-inserted or + // most-recently-modified meta element in this state has an effect. + // 1. If element is not in a document tree, then return. + let meta_node = self.upcast::(); + if !meta_node.is_in_a_document_tree() { + return; + } + + // 2. If element does not have a name attribute whose value is an ASCII case-insensitive match for "referrer", + // then return. + if self.upcast::().get_name() != Some(atom!("referrer")) { + return; + } + + // 3. If element does not have a content attribute, or that attribute's value is the empty string, then return. + let content = self + .upcast::() + .get_attribute(&ns!(), &local_name!("content")); + if let Some(attr) = content { + let attr = attr.value(); + let attr_val = attr.trim(); + if !attr_val.is_empty() { + doc.set_referrer_policy(determine_policy_for_token(attr_val)); } } } diff --git a/tests/wpt/meta/referrer-policy/generic/first-meta-changed-after-second-added.http.html.ini b/tests/wpt/meta/referrer-policy/generic/first-meta-changed-after-second-added.http.html.ini deleted file mode 100644 index e348a3023a8..00000000000 --- a/tests/wpt/meta/referrer-policy/generic/first-meta-changed-after-second-added.http.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[first-meta-changed-after-second-added.http.html] - [document referrer policy is the value of the most recently modified is removed] - expected: FAIL