Stub out Trusted Types interfaces (#36355)

Some methods are implemented fully, while others are implemented
partly. With these implementations, there are no observed crashes
when running the trusted-types web-platform-tests.

Most notably, the tests/wpt/tests/trusted-types/idlharness.window.js
is now fully passing.

Part of #36258

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
This commit is contained in:
Tim van der Lippe 2025-04-05 15:08:56 +02:00 committed by GitHub
parent 3f24b44e15
commit b87bf0b806
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
145 changed files with 3377 additions and 559 deletions

View file

@ -125,6 +125,7 @@ use crate::dom::promise::Promise;
use crate::dom::readablestream::ReadableStream;
use crate::dom::serviceworker::ServiceWorker;
use crate::dom::serviceworkerregistration::ServiceWorkerRegistration;
use crate::dom::trustedtypepolicyfactory::TrustedTypePolicyFactory;
use crate::dom::underlyingsourcecontainer::UnderlyingSourceType;
#[cfg(feature = "webgpu")]
use crate::dom::webgpu::gpudevice::GPUDevice;
@ -3300,6 +3301,16 @@ impl GlobalScope {
.borrow_mut()
.remove(&callback_id)
}
pub(crate) fn trusted_types(&self, can_gc: CanGc) -> DomRoot<TrustedTypePolicyFactory> {
if let Some(window) = self.downcast::<Window>() {
return window.TrustedTypes(can_gc);
}
if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
return worker.TrustedTypes(can_gc);
}
unreachable!();
}
}
/// Returns the Rust global scope from a JS global object.

View file

@ -575,6 +575,11 @@ pub(crate) mod touchlist;
pub(crate) mod trackevent;
pub(crate) mod transitionevent;
pub(crate) mod treewalker;
pub(crate) mod trustedhtml;
pub(crate) mod trustedscript;
pub(crate) mod trustedscripturl;
pub(crate) mod trustedtypepolicy;
pub(crate) mod trustedtypepolicyfactory;
pub(crate) mod uievent;
pub(crate) mod underlyingsourcecontainer;
pub(crate) mod url;

View file

@ -0,0 +1,45 @@
/* 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 dom_struct::dom_struct;
use crate::dom::bindings::codegen::Bindings::TrustedHTMLBinding::TrustedHTMLMethods;
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::globalscope::GlobalScope;
use crate::script_runtime::CanGc;
#[dom_struct]
pub struct TrustedHTML {
reflector_: Reflector,
data: String,
}
impl TrustedHTML {
fn new_inherited(data: String) -> Self {
Self {
reflector_: Reflector::new(),
data,
}
}
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
pub(crate) fn new(data: String, global: &GlobalScope, can_gc: CanGc) -> DomRoot<Self> {
reflect_dom_object(Box::new(Self::new_inherited(data)), global, can_gc)
}
}
impl TrustedHTMLMethods<crate::DomTypeHolder> for TrustedHTML {
/// <https://www.w3.org/TR/trusted-types/#trustedhtml-stringification-behavior>
fn Stringifier(&self) -> DOMString {
DOMString::from(&*self.data)
}
/// <https://www.w3.org/TR/trusted-types/#dom-trustedhtml-tojson>
fn ToJSON(&self) -> DOMString {
DOMString::from(&*self.data)
}
}

View file

@ -0,0 +1,45 @@
/* 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 dom_struct::dom_struct;
use crate::dom::bindings::codegen::Bindings::TrustedScriptBinding::TrustedScriptMethods;
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::globalscope::GlobalScope;
use crate::script_runtime::CanGc;
#[dom_struct]
pub struct TrustedScript {
reflector_: Reflector,
data: String,
}
impl TrustedScript {
fn new_inherited(data: String) -> Self {
Self {
reflector_: Reflector::new(),
data,
}
}
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
pub(crate) fn new(data: String, global: &GlobalScope, can_gc: CanGc) -> DomRoot<Self> {
reflect_dom_object(Box::new(Self::new_inherited(data)), global, can_gc)
}
}
impl TrustedScriptMethods<crate::DomTypeHolder> for TrustedScript {
/// <https://www.w3.org/TR/trusted-types/#trustedscript-stringification-behavior>
fn Stringifier(&self) -> DOMString {
DOMString::from(&*self.data)
}
/// <https://www.w3.org/TR/trusted-types/#dom-trustedscript-tojson>
fn ToJSON(&self) -> DOMString {
DOMString::from(&*self.data)
}
}

View file

@ -0,0 +1,45 @@
/* 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 dom_struct::dom_struct;
use crate::dom::bindings::codegen::Bindings::TrustedScriptURLBinding::TrustedScriptURLMethods;
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::globalscope::GlobalScope;
use crate::script_runtime::CanGc;
#[dom_struct]
pub struct TrustedScriptURL {
reflector_: Reflector,
data: String,
}
impl TrustedScriptURL {
fn new_inherited(data: String) -> Self {
Self {
reflector_: Reflector::new(),
data,
}
}
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
pub(crate) fn new(data: String, global: &GlobalScope, can_gc: CanGc) -> DomRoot<Self> {
reflect_dom_object(Box::new(Self::new_inherited(data)), global, can_gc)
}
}
impl TrustedScriptURLMethods<crate::DomTypeHolder> for TrustedScriptURL {
/// <https://www.w3.org/TR/trusted-types/#trustedscripturl-stringification-behavior>
fn Stringifier(&self) -> DOMString {
DOMString::from(&*self.data)
}
/// <https://www.w3.org/TR/trusted-types/#dom-trustedscripturl-tojson>
fn ToJSON(&self) -> DOMString {
DOMString::from(&*self.data)
}
}

View file

@ -0,0 +1,77 @@
/* 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 dom_struct::dom_struct;
use js::rust::HandleValue;
use crate::dom::bindings::codegen::Bindings::TrustedTypePolicyBinding::TrustedTypePolicyMethods;
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::globalscope::GlobalScope;
use crate::dom::trustedhtml::TrustedHTML;
use crate::dom::trustedscript::TrustedScript;
use crate::dom::trustedscripturl::TrustedScriptURL;
use crate::script_runtime::{CanGc, JSContext};
#[dom_struct]
pub struct TrustedTypePolicy {
reflector_: Reflector,
name: String,
}
impl TrustedTypePolicy {
fn new_inherited(name: String) -> Self {
Self {
reflector_: Reflector::new(),
name,
}
}
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
pub(crate) fn new(name: String, global: &GlobalScope, can_gc: CanGc) -> DomRoot<Self> {
reflect_dom_object(Box::new(Self::new_inherited(name)), global, can_gc)
}
}
impl TrustedTypePolicyMethods<crate::DomTypeHolder> for TrustedTypePolicy {
/// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicy-name>
fn Name(&self) -> DOMString {
DOMString::from(&*self.name)
}
/// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicy-createhtml>
fn CreateHTML(
&self,
_: JSContext,
data: DOMString,
_: Vec<HandleValue>,
can_gc: CanGc,
) -> DomRoot<TrustedHTML> {
// TODO(36258): handle arguments
TrustedHTML::new(data.to_string(), &self.global(), can_gc)
}
/// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicy-createscript>
fn CreateScript(
&self,
_: JSContext,
data: DOMString,
_: Vec<HandleValue>,
can_gc: CanGc,
) -> DomRoot<TrustedScript> {
// TODO(36258): handle arguments
TrustedScript::new(data.to_string(), &self.global(), can_gc)
}
/// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicy-createscripturl>
fn CreateScriptURL(
&self,
_: JSContext,
data: DOMString,
_: Vec<HandleValue>,
can_gc: CanGc,
) -> DomRoot<TrustedScriptURL> {
// TODO(36258): handle arguments
TrustedScriptURL::new(data.to_string(), &self.global(), can_gc)
}
}

View file

@ -0,0 +1,160 @@
/* 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 std::cell::RefCell;
use dom_struct::dom_struct;
use js::rust::HandleValue;
use crate::dom::bindings::codegen::Bindings::TrustedTypePolicyFactoryBinding::{
TrustedTypePolicyFactoryMethods, TrustedTypePolicyOptions,
};
use crate::dom::bindings::conversions::root_from_object;
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::bindings::str::DOMString;
use crate::dom::globalscope::GlobalScope;
use crate::dom::trustedhtml::TrustedHTML;
use crate::dom::trustedscript::TrustedScript;
use crate::dom::trustedscripturl::TrustedScriptURL;
use crate::dom::trustedtypepolicy::TrustedTypePolicy;
use crate::script_runtime::{CanGc, JSContext};
#[dom_struct]
pub struct TrustedTypePolicyFactory {
reflector_: Reflector,
default_policy: MutNullableDom<TrustedTypePolicy>,
policy_names: RefCell<Vec<String>>,
}
impl TrustedTypePolicyFactory {
fn new_inherited() -> Self {
Self {
reflector_: Reflector::new(),
default_policy: Default::default(),
policy_names: RefCell::new(vec![]),
}
}
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
pub(crate) fn new(global: &GlobalScope, can_gc: CanGc) -> DomRoot<Self> {
reflect_dom_object(Box::new(Self::new_inherited()), global, can_gc)
}
/// <https://www.w3.org/TR/trusted-types/#create-trusted-type-policy-algorithm>
fn create_trusted_type_policy(
&self,
policy_name: String,
_options: &TrustedTypePolicyOptions,
global: &GlobalScope,
can_gc: CanGc,
) -> Fallible<DomRoot<TrustedTypePolicy>> {
// TODO(36258): implement proper CSP check
// Step 1: Let allowedByCSP be the result of executing Should Trusted Type policy creation be blocked by
// Content Security Policy? algorithm with global, policyName and factorys created policy names value.
let allowed_by_csp = true;
// Step 2: If allowedByCSP is "Blocked", throw a TypeError and abort further steps.
if !allowed_by_csp {
return Err(Error::Type("Not allowed by CSP".to_string()));
}
// Step 3: If policyName is default and the factorys default policy value is not null, throw a TypeError
// and abort further steps.
if policy_name == "default" && self.default_policy.get().is_some() {
return Err(Error::Type(
"Already set default policy for factory".to_string(),
));
}
// Step 4: Let policy be a new TrustedTypePolicy object.
// Step 5: Set policys name property value to policyName.
let policy = TrustedTypePolicy::new(policy_name.clone(), global, can_gc);
// Step 6: Set policys options value to «[ "createHTML" ->
// options["createHTML", "createScript" -> options["createScript",
// "createScriptURL" -> options["createScriptURL" ]».
// TODO(36258): implement step 6
// Step 7: If the policyName is default, set the factorys default policy value to policy.
if policy_name == "default" {
self.default_policy.set(Some(&policy))
}
// Step 8: Append policyName to factorys created policy names.
self.policy_names.borrow_mut().push(policy_name);
// Step 9: Return policy.
Ok(policy)
}
}
impl TrustedTypePolicyFactoryMethods<crate::DomTypeHolder> for TrustedTypePolicyFactory {
/// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicyfactory-createpolicy>
fn CreatePolicy(
&self,
policy_name: DOMString,
options: &TrustedTypePolicyOptions,
can_gc: CanGc,
) -> Fallible<DomRoot<TrustedTypePolicy>> {
self.create_trusted_type_policy(policy_name.to_string(), options, &self.global(), can_gc)
}
/// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicyfactory-ishtml>
#[allow(unsafe_code)]
fn IsHTML(&self, cx: JSContext, value: HandleValue) -> bool {
if !value.get().is_object() {
return false;
}
rooted!(in(*cx) let object = value.to_object());
unsafe { root_from_object::<TrustedHTML>(object.get(), *cx).is_ok() }
}
/// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicyfactory-isscript>
#[allow(unsafe_code)]
fn IsScript(&self, cx: JSContext, value: HandleValue) -> bool {
if !value.get().is_object() {
return false;
}
rooted!(in(*cx) let object = value.to_object());
unsafe { root_from_object::<TrustedScript>(object.get(), *cx).is_ok() }
}
/// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicyfactory-isscripturl>
#[allow(unsafe_code)]
fn IsScriptURL(&self, cx: JSContext, value: HandleValue) -> bool {
if !value.get().is_object() {
return false;
}
rooted!(in(*cx) let object = value.to_object());
unsafe { root_from_object::<TrustedScriptURL>(object.get(), *cx).is_ok() }
}
/// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicyfactory-emptyhtml>
fn EmptyHTML(&self, can_gc: CanGc) -> DomRoot<TrustedHTML> {
TrustedHTML::new("".to_string(), &self.global(), can_gc)
}
/// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicyfactory-emptyscript>
fn EmptyScript(&self, can_gc: CanGc) -> DomRoot<TrustedScript> {
TrustedScript::new("".to_string(), &self.global(), can_gc)
}
/// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicyfactory-getattributetype>
fn GetAttributeType(
&self,
_: DOMString,
_: DOMString,
_: Option<DOMString>,
_: Option<DOMString>,
) -> Option<DOMString> {
// TODO(36258): implement algorithm
Some(DOMString::from("".to_string()))
}
/// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicyfactory-getpropertytype>
fn GetPropertyType(
&self,
_: DOMString,
_: DOMString,
_: Option<DOMString>,
) -> Option<DOMString> {
// TODO(36258): implement algorithm
Some(DOMString::from("".to_string()))
}
/// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicyfactory-defaultpolicy>
fn GetDefaultPolicy(&self) -> Option<DomRoot<TrustedTypePolicy>> {
self.default_policy.get()
}
}

View file

@ -145,6 +145,7 @@ use crate::dom::selection::Selection;
use crate::dom::storage::Storage;
#[cfg(feature = "bluetooth")]
use crate::dom::testrunner::TestRunner;
use crate::dom::trustedtypepolicyfactory::TrustedTypePolicyFactory;
use crate::dom::types::UIEvent;
use crate::dom::webglrenderingcontext::WebGLCommandSender;
#[cfg(feature = "webgpu")]
@ -247,6 +248,7 @@ pub(crate) struct Window {
session_storage: MutNullableDom<Storage>,
local_storage: MutNullableDom<Storage>,
status: DomRefCell<DOMString>,
trusted_types: MutNullableDom<TrustedTypePolicyFactory>,
/// For sending timeline markers. Will be ignored if
/// no devtools server
@ -1696,6 +1698,11 @@ impl WindowMethods<crate::DomTypeHolder> for Window {
self.as_global_scope()
.structured_clone(cx, value, options, retval)
}
fn TrustedTypes(&self, can_gc: CanGc) -> DomRoot<TrustedTypePolicyFactory> {
self.trusted_types
.or_init(|| TrustedTypePolicyFactory::new(self.as_global_scope(), can_gc))
}
}
impl Window {
@ -2922,6 +2929,7 @@ impl Window {
layout_marker: DomRefCell::new(Rc::new(Cell::new(true))),
current_event: DomRefCell::new(None),
theme: Cell::new(PrefersColorScheme::Light),
trusted_types: Default::default(),
});
unsafe {

View file

@ -52,6 +52,7 @@ use crate::dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
use crate::dom::globalscope::GlobalScope;
use crate::dom::performance::Performance;
use crate::dom::promise::Promise;
use crate::dom::trustedtypepolicyfactory::TrustedTypePolicyFactory;
#[cfg(feature = "webgpu")]
use crate::dom::webgpu::identityhub::IdentityHub;
use crate::dom::window::{base64_atob, base64_btoa};
@ -122,6 +123,7 @@ pub(crate) struct WorkerGlobalScope {
#[no_trace]
navigation_start: CrossProcessInstant,
performance: MutNullableDom<Performance>,
trusted_types: MutNullableDom<TrustedTypePolicyFactory>,
/// A [`TimerScheduler`] used to schedule timers for this [`WorkerGlobalScope`].
/// Timers are handled in the service worker event loop.
@ -184,6 +186,7 @@ impl WorkerGlobalScope {
performance: Default::default(),
timer_scheduler: RefCell::default(),
insecure_requests_policy,
trusted_types: Default::default(),
}
}
@ -477,6 +480,14 @@ impl WorkerGlobalScopeMethods<crate::DomTypeHolder> for WorkerGlobalScope {
self.upcast::<GlobalScope>()
.structured_clone(cx, value, options, retval)
}
/// <https://www.w3.org/TR/trusted-types/#dom-windoworworkerglobalscope-trustedtypes>
fn TrustedTypes(&self, can_gc: CanGc) -> DomRoot<TrustedTypePolicyFactory> {
self.trusted_types.or_init(|| {
let global_scope = self.upcast::<GlobalScope>();
TrustedTypePolicyFactory::new(global_scope, can_gc)
})
}
}
impl WorkerGlobalScope {