aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom
diff options
context:
space:
mode:
authorTim van der Lippe <TimvdLippe@users.noreply.github.com>2025-05-14 12:21:21 +0200
committerGitHub <noreply@github.com>2025-05-14 10:21:21 +0000
commita24fce3ae772cd2d16218545e3f584e30ea71533 (patch)
tree8912cb23758786996b4fb287706433523f0f00c7 /components/script/dom
parent3aff272e147698fa878f7ec660a19d0762fa35af (diff)
downloadservo-a24fce3ae772cd2d16218545e3f584e30ea71533.tar.gz
servo-a24fce3ae772cd2d16218545e3f584e30ea71533.zip
Implement inner slot for cryptographic nonce (#36965)
Also update the `html/dom/reflection-metadata.html` test to handle the case where `nonce` does not reflect back to the attribute after an IDL change. Part of https://github.com/servo/servo/issues/4577 Fixes https://github.com/web-platform-tests/wpt/issues/43286 Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
Diffstat (limited to 'components/script/dom')
-rw-r--r--components/script/dom/document.rs2
-rw-r--r--components/script/dom/element.rs78
-rw-r--r--components/script/dom/htmlelement.rs20
-rw-r--r--components/script/dom/htmllinkelement.rs3
-rw-r--r--components/script/dom/htmlscriptelement.rs2
-rw-r--r--components/script/dom/raredata.rs1
-rw-r--r--components/script/dom/svgelement.rs38
7 files changed, 125 insertions, 19 deletions
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
index ad95b9b9a94..78cb2c33075 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -4313,7 +4313,7 @@ impl Document {
},
Some(csp_list) => {
let element = csp::Element {
- nonce: el.nonce_attribute_if_nonceable().map(Cow::Owned),
+ nonce: el.nonce_value_if_nonceable().map(Cow::Owned),
};
csp_list.should_elements_inline_type_behavior_be_blocked(&element, type_, source)
},
diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs
index cb120f0b174..ed58548a3e5 100644
--- a/components/script/dom/element.rs
+++ b/components/script/dom/element.rs
@@ -146,8 +146,8 @@ use crate::dom::intersectionobserver::{IntersectionObserver, IntersectionObserve
use crate::dom::mutationobserver::{Mutation, MutationObserver};
use crate::dom::namednodemap::NamedNodeMap;
use crate::dom::node::{
- BindContext, ChildrenMutation, LayoutNodeHelpers, Node, NodeDamage, NodeFlags, NodeTraits,
- ShadowIncluding, UnbindContext,
+ BindContext, ChildrenMutation, CloneChildrenFlag, LayoutNodeHelpers, Node, NodeDamage,
+ NodeFlags, NodeTraits, ShadowIncluding, UnbindContext,
};
use crate::dom::nodelist::NodeList;
use crate::dom::promise::Promise;
@@ -2188,10 +2188,53 @@ impl Element {
};
}
+ /// <https://html.spec.whatwg.org/multipage/#nonce-attributes>
+ pub(crate) fn update_nonce_internal_slot(&self, nonce: String) {
+ self.ensure_rare_data().cryptographic_nonce = nonce;
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#nonce-attributes>
+ pub(crate) fn nonce_value(&self) -> String {
+ match self.rare_data().as_ref() {
+ None => String::new(),
+ Some(rare_data) => rare_data.cryptographic_nonce.clone(),
+ }
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#nonce-attributes>
+ pub(crate) fn update_nonce_post_connection(&self) {
+ // Whenever an element including HTMLOrSVGElement becomes browsing-context connected,
+ // the user agent must execute the following steps on the element:
+ if !self.upcast::<Node>().is_connected_with_browsing_context() {
+ return;
+ }
+ let global = self.owner_global();
+ // Step 1: Let CSP list be element's shadow-including root's policy container's CSP list.
+ let csp_list = match global.get_csp_list() {
+ None => return,
+ Some(csp_list) => csp_list,
+ };
+ // Step 2: If CSP list contains a header-delivered Content Security Policy,
+ // and element has a nonce content attribute whose value is not the empty string, then:
+ if !csp_list.contains_a_header_delivered_content_security_policy() ||
+ self.get_string_attribute(&local_name!("nonce")).is_empty()
+ {
+ return;
+ }
+ // Step 2.1: Let nonce be element's [[CryptographicNonce]].
+ let nonce = self.nonce_value();
+ // Step 2.2: Set an attribute value for element using "nonce" and the empty string.
+ self.set_string_attribute(&local_name!("nonce"), "".into(), CanGc::note());
+ // Step 2.3: Set element's [[CryptographicNonce]] to nonce.
+ self.update_nonce_internal_slot(nonce);
+ }
+
/// <https://www.w3.org/TR/CSP/#is-element-nonceable>
- pub(crate) fn nonce_attribute_if_nonceable(&self) -> Option<String> {
+ pub(crate) fn nonce_value_if_nonceable(&self) -> Option<String> {
// Step 1: If element does not have an attribute named "nonce", return "Not Nonceable".
- let nonce_attribute = self.get_attribute(&ns!(), &local_name!("nonce"))?;
+ if !self.has_attribute(&local_name!("nonce")) {
+ return None;
+ }
// Step 2: If element is a script element, then for each attribute of element’s attribute list:
if self.downcast::<HTMLScriptElement>().is_some() {
for attr in self.attrs().iter() {
@@ -2213,7 +2256,7 @@ impl Element {
// TODO(https://github.com/servo/servo/issues/4577 and https://github.com/whatwg/html/issues/3257):
// Figure out how to retrieve this information from the parser
// Step 4: Return "Nonceable".
- Some(nonce_attribute.value().to_string().trim().to_owned())
+ Some(self.nonce_value().trim().to_owned())
}
// https://dom.spec.whatwg.org/#insert-adjacent
@@ -4197,6 +4240,31 @@ impl VirtualMethods for Element {
self.tag_name.clear();
}
}
+
+ fn post_connection_steps(&self) {
+ if let Some(s) = self.super_type() {
+ s.post_connection_steps();
+ }
+
+ self.update_nonce_post_connection();
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#nonce-attributes%3Aconcept-node-clone-ext>
+ fn cloning_steps(
+ &self,
+ copy: &Node,
+ maybe_doc: Option<&Document>,
+ clone_children: CloneChildrenFlag,
+ can_gc: CanGc,
+ ) {
+ if let Some(s) = self.super_type() {
+ s.cloning_steps(copy, maybe_doc, clone_children, can_gc);
+ }
+ let elem = copy.downcast::<Element>().unwrap();
+ if let Some(rare_data) = self.rare_data().as_ref() {
+ elem.update_nonce_internal_slot(rare_data.cryptographic_nonce.clone());
+ }
+ }
}
#[derive(Clone, PartialEq)]
diff --git a/components/script/dom/htmlelement.rs b/components/script/dom/htmlelement.rs
index 32a979ad138..f41370386e9 100644
--- a/components/script/dom/htmlelement.rs
+++ b/components/script/dom/htmlelement.rs
@@ -645,13 +645,16 @@ impl HTMLElementMethods<crate::DomTypeHolder> for HTMLElement {
Ok(internals)
}
- // FIXME: The nonce should be stored in an internal slot instead of an
- // attribute (https://html.spec.whatwg.org/multipage/#cryptographicnonce)
// https://html.spec.whatwg.org/multipage/#dom-noncedelement-nonce
- make_getter!(Nonce, "nonce");
+ fn Nonce(&self) -> DOMString {
+ self.as_element().nonce_value().into()
+ }
// https://html.spec.whatwg.org/multipage/#dom-noncedelement-nonce
- make_setter!(SetNonce, "nonce");
+ fn SetNonce(&self, value: DOMString) {
+ self.as_element()
+ .update_nonce_internal_slot(value.to_string())
+ }
// https://html.spec.whatwg.org/multipage/#dom-fe-autofocus
fn Autofocus(&self) -> bool {
@@ -1138,6 +1141,15 @@ impl VirtualMethods for HTMLElement {
},
}
},
+ (&local_name!("nonce"), mutation) => match mutation {
+ AttributeMutation::Set(_) => {
+ let nonce = &**attr.value();
+ element.update_nonce_internal_slot(nonce.to_owned());
+ },
+ AttributeMutation::Removed => {
+ element.update_nonce_internal_slot("".to_owned());
+ },
+ },
_ => {},
}
}
diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs
index 18bd426acdb..f4e7683cf2a 100644
--- a/components/script/dom/htmllinkelement.rs
+++ b/components/script/dom/htmllinkelement.rs
@@ -31,7 +31,6 @@ use crate::dom::attr::Attr;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::DOMTokenListBinding::DOMTokenList_Binding::DOMTokenListMethods;
use crate::dom::bindings::codegen::Bindings::HTMLLinkElementBinding::HTMLLinkElementMethods;
-use crate::dom::bindings::codegen::GenericBindings::HTMLElementBinding::HTMLElement_Binding::HTMLElementMethods;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::DomGlobal;
@@ -344,7 +343,7 @@ impl HTMLLinkElement {
destination: Some(destination),
integrity: String::new(),
link_type: String::new(),
- cryptographic_nonce_metadata: self.upcast::<HTMLElement>().Nonce().into(),
+ cryptographic_nonce_metadata: self.upcast::<Element>().nonce_value(),
cross_origin: cors_setting_for_element(element),
referrer_policy: referrer_policy_for_element(element),
policy_container: document.policy_container().to_owned(),
diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs
index 4ee1397b4ed..d1b3cfd3467 100644
--- a/components/script/dom/htmlscriptelement.rs
+++ b/components/script/dom/htmlscriptelement.rs
@@ -781,7 +781,7 @@ impl HTMLScriptElement {
};
// Step 24. Let cryptographic nonce be el's [[CryptographicNonce]] internal slot's value.
- let cryptographic_nonce = self.upcast::<HTMLElement>().Nonce().into();
+ let cryptographic_nonce = self.upcast::<Element>().nonce_value();
// Step 25. If el has an integrity attribute, then let integrity metadata be that attribute's value.
// Otherwise, let integrity metadata be the empty string.
diff --git a/components/script/dom/raredata.rs b/components/script/dom/raredata.rs
index 3afa000511e..0c048956217 100644
--- a/components/script/dom/raredata.rs
+++ b/components/script/dom/raredata.rs
@@ -75,4 +75,5 @@ pub(crate) struct ElementRareData {
/// > Element objects have an internal [[RegisteredIntersectionObservers]] slot,
/// > which is initialized to an empty list. This list holds IntersectionObserverRegistration records, which have:
pub(crate) registered_intersection_observers: Vec<IntersectionObserverRegistration>,
+ pub(crate) cryptographic_nonce: String,
}
diff --git a/components/script/dom/svgelement.rs b/components/script/dom/svgelement.rs
index 0f36d942f3e..a380dcff5ac 100644
--- a/components/script/dom/svgelement.rs
+++ b/components/script/dom/svgelement.rs
@@ -8,12 +8,13 @@ use js::rust::HandleObject;
use script_bindings::str::DOMString;
use stylo_dom::ElementState;
+use crate::dom::attr::Attr;
use crate::dom::bindings::codegen::Bindings::SVGElementBinding::SVGElementMethods;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
use crate::dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner};
use crate::dom::document::Document;
-use crate::dom::element::Element;
+use crate::dom::element::{AttributeMutation, Element};
use crate::dom::node::{Node, NodeTraits};
use crate::dom::virtualmethods::VirtualMethods;
use crate::script_runtime::CanGc;
@@ -59,11 +60,33 @@ impl SVGElement {
can_gc,
)
}
+
+ fn as_element(&self) -> &Element {
+ self.upcast::<Element>()
+ }
}
impl VirtualMethods for SVGElement {
fn super_type(&self) -> Option<&dyn VirtualMethods> {
- Some(self.upcast::<Element>() as &dyn VirtualMethods)
+ Some(self.as_element() as &dyn VirtualMethods)
+ }
+
+ fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
+ self.super_type()
+ .unwrap()
+ .attribute_mutated(attr, mutation, can_gc);
+ let element = self.as_element();
+ if let (&local_name!("nonce"), mutation) = (attr.local_name(), mutation) {
+ match mutation {
+ AttributeMutation::Set(_) => {
+ let nonce = &**attr.value();
+ element.update_nonce_internal_slot(nonce.to_owned());
+ },
+ AttributeMutation::Removed => {
+ element.update_nonce_internal_slot(String::new());
+ },
+ }
+ }
}
}
@@ -85,13 +108,16 @@ impl SVGElementMethods<crate::DomTypeHolder> for SVGElement {
// <https://html.spec.whatwg.org/multipage/#globaleventhandlers>
global_event_handlers!();
- // FIXME: The nonce should be stored in an internal slot instead of an
- // attribute (https://html.spec.whatwg.org/multipage/#cryptographicnonce)
// https://html.spec.whatwg.org/multipage/#dom-noncedelement-nonce
- make_getter!(Nonce, "nonce");
+ fn Nonce(&self) -> DOMString {
+ self.as_element().nonce_value().into()
+ }
// https://html.spec.whatwg.org/multipage/#dom-noncedelement-nonce
- make_setter!(SetNonce, "nonce");
+ fn SetNonce(&self, value: DOMString) {
+ self.as_element()
+ .update_nonce_internal_slot(value.to_string())
+ }
// https://html.spec.whatwg.org/multipage/#dom-fe-autofocus
fn Autofocus(&self) -> bool {