Implement Trusted Types for ShadowRoot (#38595)

Also make TrustedHTML work the same as TrustedScript by
only taking 1 `&str` to make things easier.

Part of #36258

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
This commit is contained in:
Tim van der Lippe 2025-08-11 15:39:55 +02:00 committed by GitHub
parent abc549eff7
commit 3976fa77bc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 38 additions and 52 deletions

View file

@ -4128,8 +4128,7 @@ impl Document {
string = TrustedHTML::get_trusted_script_compliant_string( string = TrustedHTML::get_trusted_script_compliant_string(
&self.global(), &self.global(),
TrustedHTMLOrString::String(string.into()), TrustedHTMLOrString::String(string.into()),
containing_class, &format!("{} {}", containing_class, field),
field,
can_gc, can_gc,
)? )?
.as_ref() .as_ref()

View file

@ -3787,8 +3787,7 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
let html = TrustedHTML::get_trusted_script_compliant_string( let html = TrustedHTML::get_trusted_script_compliant_string(
&self.owner_global(), &self.owner_global(),
html, html,
"Element", "Element setHTMLUnsafe",
"setHTMLUnsafe",
can_gc, can_gc,
)?; )?;
// Step 2. Let target be this's template contents if this is a template element; otherwise this. // Step 2. Let target be this's template contents if this is a template element; otherwise this.
@ -3844,8 +3843,7 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
let value = TrustedHTML::get_trusted_script_compliant_string( let value = TrustedHTML::get_trusted_script_compliant_string(
&self.owner_global(), &self.owner_global(),
value.convert(), value.convert(),
"Element", "Element innerHTML",
"innerHTML",
can_gc, can_gc,
)?; )?;
// https://github.com/w3c/DOM-Parsing/issues/1 // https://github.com/w3c/DOM-Parsing/issues/1
@ -3902,8 +3900,7 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
let value = TrustedHTML::get_trusted_script_compliant_string( let value = TrustedHTML::get_trusted_script_compliant_string(
&self.owner_global(), &self.owner_global(),
value.convert(), value.convert(),
"Element", "Element outerHTML",
"outerHTML",
can_gc, can_gc,
)?; )?;
let context_document = self.owner_document(); let context_document = self.owner_document();
@ -4118,8 +4115,7 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
let text = TrustedHTML::get_trusted_script_compliant_string( let text = TrustedHTML::get_trusted_script_compliant_string(
&self.owner_global(), &self.owner_global(),
text, text,
"Element", "Element insertAdjacentHTML",
"insertAdjacentHTML",
can_gc, can_gc,
)?; )?;
let position = position.parse::<AdjacentPosition>()?; let position = position.parse::<AdjacentPosition>()?;

View file

@ -616,17 +616,15 @@ impl HTMLIFrameElementMethods<crate::DomTypeHolder> for HTMLIFrameElement {
// Get Trusted Type compliant string algorithm with TrustedHTML, // Get Trusted Type compliant string algorithm with TrustedHTML,
// this's relevant global object, the given value, "HTMLIFrameElement srcdoc", and "script". // this's relevant global object, the given value, "HTMLIFrameElement srcdoc", and "script".
let element = self.upcast::<Element>(); let element = self.upcast::<Element>();
let local_name = &local_name!("srcdoc");
let value = TrustedHTML::get_trusted_script_compliant_string( let value = TrustedHTML::get_trusted_script_compliant_string(
&element.owner_global(), &element.owner_global(),
value, value,
"HTMLIFrameElement", "HTMLIFrameElement srcdoc",
local_name,
can_gc, can_gc,
)?; )?;
// Step 2: Set an attribute value given this, srcdoc's local name, and compliantString. // Step 2: Set an attribute value given this, srcdoc's local name, and compliantString.
element.set_attribute( element.set_attribute(
local_name, &local_name!("srcdoc"),
AttrValue::String(value.as_ref().to_owned()), AttrValue::String(value.as_ref().to_owned()),
can_gc, can_gc,
); );

View file

@ -27,6 +27,9 @@ use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRoot_Bindi
use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{ use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
ShadowRootMode, SlotAssignmentMode, ShadowRootMode, SlotAssignmentMode,
}; };
use crate::dom::bindings::codegen::UnionTypes::{
TrustedHTMLOrNullIsEmptyString, TrustedHTMLOrString,
};
use crate::dom::bindings::frozenarray::CachedFrozenArray; use crate::dom::bindings::frozenarray::CachedFrozenArray;
use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::num::Finite; use crate::dom::bindings::num::Finite;
@ -46,6 +49,7 @@ use crate::dom::node::{
VecPreOrderInsertionHelper, VecPreOrderInsertionHelper,
}; };
use crate::dom::stylesheetlist::{StyleSheetList, StyleSheetListOwner}; use crate::dom::stylesheetlist::{StyleSheetList, StyleSheetListOwner};
use crate::dom::trustedhtml::TrustedHTML;
use crate::dom::types::EventTarget; use crate::dom::types::EventTarget;
use crate::dom::virtualmethods::{VirtualMethods, vtable_for}; use crate::dom::virtualmethods::{VirtualMethods, vtable_for};
use crate::dom::window::Window; use crate::dom::window::Window;
@ -459,18 +463,24 @@ impl ShadowRootMethods<crate::DomTypeHolder> for ShadowRoot {
} }
/// <https://html.spec.whatwg.org/multipage/#dom-shadowroot-innerhtml> /// <https://html.spec.whatwg.org/multipage/#dom-shadowroot-innerhtml>
fn GetInnerHTML(&self, can_gc: CanGc) -> Fallible<DOMString> { fn GetInnerHTML(&self, can_gc: CanGc) -> Fallible<TrustedHTMLOrNullIsEmptyString> {
// ShadowRoot's innerHTML getter steps are to return the result of running fragment serializing // ShadowRoot's innerHTML getter steps are to return the result of running fragment serializing
// algorithm steps with this and true. // algorithm steps with this and true.
self.upcast::<Node>() self.upcast::<Node>()
.fragment_serialization_algorithm(true, can_gc) .fragment_serialization_algorithm(true, can_gc)
.map(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString)
} }
/// <https://html.spec.whatwg.org/multipage/#dom-shadowroot-innerhtml> /// <https://html.spec.whatwg.org/multipage/#dom-shadowroot-innerhtml>
fn SetInnerHTML(&self, value: DOMString, can_gc: CanGc) -> ErrorResult { fn SetInnerHTML(&self, value: TrustedHTMLOrNullIsEmptyString, can_gc: CanGc) -> ErrorResult {
// TODO Step 1. Let compliantString be the result of invoking the Get Trusted Type compliant string algorithm // Step 1. Let compliantString be the result of invoking the Get Trusted Type compliant string algorithm
// with TrustedHTML, this's relevant global object, the given value, "ShadowRoot innerHTML", and "script". // with TrustedHTML, this's relevant global object, the given value, "ShadowRoot innerHTML", and "script".
let compliant_string = value; let value = TrustedHTML::get_trusted_script_compliant_string(
&self.owner_global(),
value.convert(),
"ShadowRoot innerHTML",
can_gc,
)?;
// Step 2. Let context be this's host. // Step 2. Let context be this's host.
let context = self.Host(); let context = self.Host();
@ -480,7 +490,7 @@ impl ShadowRootMethods<crate::DomTypeHolder> for ShadowRoot {
// //
// NOTE: The spec doesn't strictly tell us to bail out here, but // NOTE: The spec doesn't strictly tell us to bail out here, but
// we can't continue if parsing failed // we can't continue if parsing failed
let frag = context.parse_fragment(compliant_string, can_gc)?; let frag = context.parse_fragment(value, can_gc)?;
// Step 4. Replace all with fragment within this. // Step 4. Replace all with fragment within this.
Node::replace_all(Some(frag.upcast()), self.upcast(), can_gc); Node::replace_all(Some(frag.upcast()), self.upcast(), can_gc);
@ -493,12 +503,22 @@ impl ShadowRootMethods<crate::DomTypeHolder> for ShadowRoot {
} }
/// <https://html.spec.whatwg.org/multipage/#dom-shadowroot-sethtmlunsafe> /// <https://html.spec.whatwg.org/multipage/#dom-shadowroot-sethtmlunsafe>
fn SetHTMLUnsafe(&self, html: DOMString, can_gc: CanGc) { fn SetHTMLUnsafe(&self, value: TrustedHTMLOrString, can_gc: CanGc) -> ErrorResult {
// Step 1. Let compliantHTML be the result of invoking the
// Get Trusted Type compliant string algorithm with TrustedHTML,
// this's relevant global object, html, "ShadowRoot setHTMLUnsafe", and "script".
let value = TrustedHTML::get_trusted_script_compliant_string(
&self.owner_global(),
value,
"ShadowRoot setHTMLUnsafe",
can_gc,
)?;
// Step 2. Unsafely set HTMl given this, this's shadow host, and complaintHTML // Step 2. Unsafely set HTMl given this, this's shadow host, and complaintHTML
let target = self.upcast::<Node>(); let target = self.upcast::<Node>();
let context_element = self.Host(); let context_element = self.Host();
Node::unsafely_set_html(target, &context_element, html, can_gc); Node::unsafely_set_html(target, &context_element, value, can_gc);
Ok(())
} }
// https://dom.spec.whatwg.org/#dom-shadowroot-onslotchange // https://dom.spec.whatwg.org/#dom-shadowroot-onslotchange

View file

@ -43,18 +43,16 @@ impl TrustedHTML {
pub(crate) fn get_trusted_script_compliant_string( pub(crate) fn get_trusted_script_compliant_string(
global: &GlobalScope, global: &GlobalScope,
value: TrustedHTMLOrString, value: TrustedHTMLOrString,
containing_class: &str, sink: &str,
field: &str,
can_gc: CanGc, can_gc: CanGc,
) -> Fallible<DOMString> { ) -> Fallible<DOMString> {
match value { match value {
TrustedHTMLOrString::String(value) => { TrustedHTMLOrString::String(value) => {
let sink = format!("{} {}", containing_class, field);
TrustedTypePolicyFactory::get_trusted_type_compliant_string( TrustedTypePolicyFactory::get_trusted_type_compliant_string(
TrustedType::TrustedHTML, TrustedType::TrustedHTML,
global, global,
value, value,
&sink, sink,
"'script'", "'script'",
can_gc, can_gc,
) )

View file

@ -25,9 +25,8 @@ ShadowRoot includes DocumentOrShadowRoot;
// https://html.spec.whatwg.org/multipage/#dom-parsing-and-serialization // https://html.spec.whatwg.org/multipage/#dom-parsing-and-serialization
partial interface ShadowRoot { partial interface ShadowRoot {
[CEReactions] undefined setHTMLUnsafe(DOMString html); [CEReactions, Throws] undefined setHTMLUnsafe((TrustedHTML or DOMString) html);
DOMString getHTML(optional GetHTMLOptions options = {}); DOMString getHTML(optional GetHTMLOptions options = {});
// [CEReactions] attribute (TrustedHTML or [LegacyNullToEmptyString] DOMString) innerHTML; [CEReactions, Throws] attribute (TrustedHTML or [LegacyNullToEmptyString] DOMString) innerHTML;
[CEReactions, Throws] attribute [LegacyNullToEmptyString] DOMString innerHTML;
}; };

View file

@ -1,9 +0,0 @@
[block-string-assignment-to-ShadowRoot-innerHTML.html]
[`shadowRoot.innerHTML = string` throws.]
expected: FAIL
[`shadowRoot.innerHTML = null` throws.]
expected: FAIL
[`shadowRoot.innerHTML = string` assigned via default policy (successful HTML transformation).]
expected: FAIL

View file

@ -1,9 +0,0 @@
[block-string-assignment-to-ShadowRoot-setHTMLUnsafe.html]
[`shadowRoot.setHTMLUnsafe(string)` assigned via default policy (successful HTML transformation).]
expected: FAIL
[`shadowRoot.setHTMLUnsafe(string)` throws.]
expected: FAIL
[`shadowRoot.setHTMLUnsafe(null)` throws.]
expected: FAIL

View file

@ -1,3 +0,0 @@
[trusted-types-reporting-for-ShadowRoot-innerHTML.html]
[Violation report for plain string.]
expected: FAIL

View file

@ -1,3 +0,0 @@
[trusted-types-reporting-for-ShadowRoot-setHTMLUnsafe.html]
[Violation report for plain string.]
expected: FAIL