diff options
author | Tim van der Lippe <TimvdLippe@users.noreply.github.com> | 2025-05-14 12:21:21 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-05-14 10:21:21 +0000 |
commit | a24fce3ae772cd2d16218545e3f584e30ea71533 (patch) | |
tree | 8912cb23758786996b4fb287706433523f0f00c7 /components/script/dom | |
parent | 3aff272e147698fa878f7ec660a19d0762fa35af (diff) | |
download | servo-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.rs | 2 | ||||
-rw-r--r-- | components/script/dom/element.rs | 78 | ||||
-rw-r--r-- | components/script/dom/htmlelement.rs | 20 | ||||
-rw-r--r-- | components/script/dom/htmllinkelement.rs | 3 | ||||
-rw-r--r-- | components/script/dom/htmlscriptelement.rs | 2 | ||||
-rw-r--r-- | components/script/dom/raredata.rs | 1 | ||||
-rw-r--r-- | components/script/dom/svgelement.rs | 38 |
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 { |