blob: 399e031b85cbbf3e9eb7207cf6e2260fb1ea5d3e [file]
/* 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::rc::Rc;
use dom_struct::dom_struct;
use js::rust::HandleValue;
use strum_macros::AsRefStr;
use crate::dom::bindings::callback::ExceptionHandling;
use crate::dom::bindings::codegen::Bindings::TrustedTypePolicyBinding::TrustedTypePolicyMethods;
use crate::dom::bindings::codegen::Bindings::TrustedTypePolicyFactoryBinding::{
CreateHTMLCallback, CreateScriptCallback, CreateScriptURLCallback, TrustedTypePolicyOptions,
};
use crate::dom::bindings::codegen::UnionTypes::TrustedHTMLOrTrustedScriptOrTrustedScriptURLOrString as TrustedTypeOrString;
use crate::dom::bindings::error::Error::Type;
use crate::dom::bindings::error::Fallible;
use crate::dom::bindings::reflector::{DomGlobal, DomObject, 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,
#[ignore_malloc_size_of = "Rc has unclear ownership"]
create_html: Option<Rc<CreateHTMLCallback>>,
#[ignore_malloc_size_of = "Rc has unclear ownership"]
create_script: Option<Rc<CreateScriptCallback>>,
#[ignore_malloc_size_of = "Rc has unclear ownership"]
create_script_url: Option<Rc<CreateScriptURLCallback>>,
}
#[derive(AsRefStr, Clone)]
pub(crate) enum TrustedType {
TrustedHTML,
TrustedScript,
TrustedScriptURL,
}
impl TrustedType {
pub(crate) fn matches_idl_trusted_type(&self, idl_trusted_type: &TrustedTypeOrString) -> bool {
match self {
TrustedType::TrustedHTML => {
matches!(idl_trusted_type, TrustedTypeOrString::TrustedHTML(_))
},
TrustedType::TrustedScript => {
matches!(idl_trusted_type, TrustedTypeOrString::TrustedScript(_))
},
TrustedType::TrustedScriptURL => {
matches!(idl_trusted_type, TrustedTypeOrString::TrustedScriptURL(_))
},
}
}
}
impl TrustedTypePolicy {
fn new_inherited(name: String, options: &TrustedTypePolicyOptions) -> Self {
Self {
reflector_: Reflector::new(),
name,
create_html: options.createHTML.clone(),
create_script: options.createScript.clone(),
create_script_url: options.createScriptURL.clone(),
}
}
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
pub(crate) fn new(
name: String,
options: &TrustedTypePolicyOptions,
global: &GlobalScope,
can_gc: CanGc,
) -> DomRoot<Self> {
reflect_dom_object(Box::new(Self::new_inherited(name, options)), global, can_gc)
}
/// <https://w3c.github.io/trusted-types/dist/spec/#get-trusted-type-policy-value-algorithm>
fn check_callback_if_missing(throw_if_missing: bool) -> Fallible<Option<DOMString>> {
// Step 3.1: If throwIfMissing throw a TypeError.
if throw_if_missing {
Err(Type("Cannot find type".to_owned()))
} else {
// Step 3.2: Else return null.
Ok(None)
}
}
/// <https://w3c.github.io/trusted-types/dist/spec/#get-trusted-type-policy-value-algorithm>
pub(crate) fn get_trusted_type_policy_value(
&self,
expected_type: TrustedType,
input: DOMString,
arguments: Vec<HandleValue>,
throw_if_missing: bool,
can_gc: CanGc,
) -> Fallible<Option<DOMString>> {
// Step 1: Let functionName be a function name for the given trustedTypeName, based on the following table:
match expected_type {
TrustedType::TrustedHTML => match &self.create_html {
// Step 3: If function is null, then:
None => TrustedTypePolicy::check_callback_if_missing(throw_if_missing),
// Step 2: Let function be policy’s options[functionName].
Some(callback) => {
// Step 4: Let policyValue be the result of invoking function with value as a first argument,
// items of arguments as subsequent arguments, and callback **this** value set to undefined,
// rethrowing any exceptions.
callback.Call__(input, arguments, ExceptionHandling::Rethrow, can_gc)
},
},
TrustedType::TrustedScript => match &self.create_script {
// Step 3: If function is null, then:
None => TrustedTypePolicy::check_callback_if_missing(throw_if_missing),
// Step 2: Let function be policy’s options[functionName].
Some(callback) => {
// Step 4: Let policyValue be the result of invoking function with value as a first argument,
// items of arguments as subsequent arguments, and callback **this** value set to undefined,
// rethrowing any exceptions.
callback.Call__(input, arguments, ExceptionHandling::Rethrow, can_gc)
},
},
TrustedType::TrustedScriptURL => match &self.create_script_url {
// Step 3: If function is null, then:
None => TrustedTypePolicy::check_callback_if_missing(throw_if_missing),
// Step 2: Let function be policy’s options[functionName].
Some(callback) => {
// Step 4: Let policyValue be the result of invoking function with value as a first argument,
// items of arguments as subsequent arguments, and callback **this** value set to undefined,
// rethrowing any exceptions.
callback
.Call__(input, arguments, ExceptionHandling::Rethrow, can_gc)
.map(|result| result.map(|str| DOMString::from(str.as_ref())))
},
},
}
}
/// This does not take all arguments as specified. That's because the return type of the
/// trusted type function and object are not the same. 2 of the 3 string callbacks return
/// a DOMString, while the other one returns an USVString. Additionally, all three callbacks
/// have a unique type signature in WebIDL.
///
/// To circumvent these type problems, rather than implementing the full functionality here,
/// part of the algorithm is implemented on the caller side. There, we only call the callback
/// and create the object. The rest of the machinery is ensuring the right values pass through
/// to the relevant callbacks.
///
/// <https://w3c.github.io/trusted-types/dist/spec/#create-a-trusted-type-algorithm>
fn create_trusted_type<R, TrustedTypeCallback>(
&self,
expected_type: TrustedType,
input: DOMString,
arguments: Vec<HandleValue>,
trusted_type_creation_callback: TrustedTypeCallback,
can_gc: CanGc,
) -> Fallible<DomRoot<R>>
where
R: DomObject,
TrustedTypeCallback: FnOnce(DOMString) -> DomRoot<R>,
{
// Step 1: Let policyValue be the result of executing Get Trusted Type policy value
// with the same arguments as this algorithm and additionally true as throwIfMissing.
let policy_value =
self.get_trusted_type_policy_value(expected_type, input, arguments, true, can_gc);
match policy_value {
// Step 2: If the algorithm threw an error, rethrow the error and abort the following steps.
Err(error) => Err(error),
Ok(policy_value) => {
// Step 3: Let dataString be the result of stringifying policyValue.
let data_string = match policy_value {
Some(value) => value,
// Step 4: If policyValue is null or undefined, set dataString to the empty string.
None => DOMString::new(),
};
// Step 5: Return a new instance of an interface with a type name trustedTypeName,
// with its associated data value set to dataString.
Ok(trusted_type_creation_callback(data_string))
},
}
}
}
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,
_cx: JSContext,
input: DOMString,
arguments: Vec<HandleValue>,
can_gc: CanGc,
) -> Fallible<DomRoot<TrustedHTML>> {
self.create_trusted_type(
TrustedType::TrustedHTML,
input,
arguments,
|data_string| TrustedHTML::new(data_string, &self.global(), can_gc),
can_gc,
)
}
/// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicy-createscript>
fn CreateScript(
&self,
_cx: JSContext,
input: DOMString,
arguments: Vec<HandleValue>,
can_gc: CanGc,
) -> Fallible<DomRoot<TrustedScript>> {
self.create_trusted_type(
TrustedType::TrustedScript,
input,
arguments,
|data_string| TrustedScript::new(data_string, &self.global(), can_gc),
can_gc,
)
}
/// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicy-createscripturl>
fn CreateScriptURL(
&self,
_cx: JSContext,
input: DOMString,
arguments: Vec<HandleValue>,
can_gc: CanGc,
) -> Fallible<DomRoot<TrustedScriptURL>> {
self.create_trusted_type(
TrustedType::TrustedScriptURL,
input,
arguments,
|data_string| TrustedScriptURL::new(data_string, &self.global(), can_gc),
can_gc,
)
}
}