script/dom/
trustedtypepolicy.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::rc::Rc;
6
7use dom_struct::dom_struct;
8use js::rust::HandleValue;
9use strum::AsRefStr;
10
11use crate::dom::bindings::callback::ExceptionHandling;
12use crate::dom::bindings::codegen::Bindings::TrustedTypePolicyBinding::TrustedTypePolicyMethods;
13use crate::dom::bindings::codegen::Bindings::TrustedTypePolicyFactoryBinding::{
14    CreateHTMLCallback, CreateScriptCallback, CreateScriptURLCallback, TrustedTypePolicyOptions,
15};
16use crate::dom::bindings::codegen::UnionTypes::TrustedHTMLOrTrustedScriptOrTrustedScriptURLOrString as TrustedTypeOrString;
17use crate::dom::bindings::error::Error::Type;
18use crate::dom::bindings::error::Fallible;
19use crate::dom::bindings::reflector::{DomGlobal, DomObject, Reflector, reflect_dom_object};
20use crate::dom::bindings::root::DomRoot;
21use crate::dom::bindings::str::DOMString;
22use crate::dom::globalscope::GlobalScope;
23use crate::dom::trustedhtml::TrustedHTML;
24use crate::dom::trustedscript::TrustedScript;
25use crate::dom::trustedscripturl::TrustedScriptURL;
26use crate::script_runtime::{CanGc, JSContext};
27
28#[dom_struct]
29pub struct TrustedTypePolicy {
30    reflector_: Reflector,
31
32    name: String,
33
34    #[conditional_malloc_size_of]
35    create_html: Option<Rc<CreateHTMLCallback>>,
36    #[conditional_malloc_size_of]
37    create_script: Option<Rc<CreateScriptCallback>>,
38    #[conditional_malloc_size_of]
39    create_script_url: Option<Rc<CreateScriptURLCallback>>,
40}
41
42#[derive(AsRefStr, Clone)]
43pub(crate) enum TrustedType {
44    TrustedHTML,
45    TrustedScript,
46    TrustedScriptURL,
47}
48
49impl TrustedType {
50    pub(crate) fn matches_idl_trusted_type(&self, idl_trusted_type: &TrustedTypeOrString) -> bool {
51        match self {
52            TrustedType::TrustedHTML => {
53                matches!(idl_trusted_type, TrustedTypeOrString::TrustedHTML(_))
54            },
55            TrustedType::TrustedScript => {
56                matches!(idl_trusted_type, TrustedTypeOrString::TrustedScript(_))
57            },
58            TrustedType::TrustedScriptURL => {
59                matches!(idl_trusted_type, TrustedTypeOrString::TrustedScriptURL(_))
60            },
61        }
62    }
63}
64
65impl TrustedTypePolicy {
66    fn new_inherited(name: String, options: &TrustedTypePolicyOptions) -> Self {
67        Self {
68            reflector_: Reflector::new(),
69            name,
70            create_html: options.createHTML.clone(),
71            create_script: options.createScript.clone(),
72            create_script_url: options.createScriptURL.clone(),
73        }
74    }
75
76    pub(crate) fn new(
77        name: String,
78        options: &TrustedTypePolicyOptions,
79        global: &GlobalScope,
80        can_gc: CanGc,
81    ) -> DomRoot<Self> {
82        reflect_dom_object(Box::new(Self::new_inherited(name, options)), global, can_gc)
83    }
84
85    /// <https://w3c.github.io/trusted-types/dist/spec/#get-trusted-type-policy-value-algorithm>
86    fn check_callback_if_missing(throw_if_missing: bool) -> Fallible<Option<DOMString>> {
87        // Step 3.1: If throwIfMissing throw a TypeError.
88        if throw_if_missing {
89            Err(Type("Cannot find type".to_owned()))
90        } else {
91            // Step 3.2: Else return null.
92            Ok(None)
93        }
94    }
95
96    /// <https://w3c.github.io/trusted-types/dist/spec/#get-trusted-type-policy-value-algorithm>
97    pub(crate) fn get_trusted_type_policy_value(
98        &self,
99        expected_type: TrustedType,
100        input: DOMString,
101        arguments: Vec<HandleValue>,
102        throw_if_missing: bool,
103        can_gc: CanGc,
104    ) -> Fallible<Option<DOMString>> {
105        // Step 1: Let functionName be a function name for the given trustedTypeName, based on the following table:
106        match expected_type {
107            TrustedType::TrustedHTML => match &self.create_html {
108                // Step 3: If function is null, then:
109                None => TrustedTypePolicy::check_callback_if_missing(throw_if_missing),
110                // Step 2: Let function be policy’s options[functionName].
111                Some(callback) => {
112                    // Step 4: Let policyValue be the result of invoking function with value as a first argument,
113                    // items of arguments as subsequent arguments, and callback **this** value set to undefined,
114                    // rethrowing any exceptions.
115                    callback.Call__(input, arguments, ExceptionHandling::Rethrow, can_gc)
116                },
117            },
118            TrustedType::TrustedScript => match &self.create_script {
119                // Step 3: If function is null, then:
120                None => TrustedTypePolicy::check_callback_if_missing(throw_if_missing),
121                // Step 2: Let function be policy’s options[functionName].
122                Some(callback) => {
123                    // Step 4: Let policyValue be the result of invoking function with value as a first argument,
124                    // items of arguments as subsequent arguments, and callback **this** value set to undefined,
125                    // rethrowing any exceptions.
126                    callback.Call__(input, arguments, ExceptionHandling::Rethrow, can_gc)
127                },
128            },
129            TrustedType::TrustedScriptURL => match &self.create_script_url {
130                // Step 3: If function is null, then:
131                None => TrustedTypePolicy::check_callback_if_missing(throw_if_missing),
132                // Step 2: Let function be policy’s options[functionName].
133                Some(callback) => {
134                    // Step 4: Let policyValue be the result of invoking function with value as a first argument,
135                    // items of arguments as subsequent arguments, and callback **this** value set to undefined,
136                    // rethrowing any exceptions.
137                    callback
138                        .Call__(input, arguments, ExceptionHandling::Rethrow, can_gc)
139                        .map(|result| result.map(DOMString::from))
140                },
141            },
142        }
143    }
144
145    /// This does not take all arguments as specified. That's because the return type of the
146    /// trusted type function and object are not the same. 2 of the 3 string callbacks return
147    /// a DOMString, while the other one returns an USVString. Additionally, all three callbacks
148    /// have a unique type signature in WebIDL.
149    ///
150    /// To circumvent these type problems, rather than implementing the full functionality here,
151    /// part of the algorithm is implemented on the caller side. There, we only call the callback
152    /// and create the object. The rest of the machinery is ensuring the right values pass through
153    /// to the relevant callbacks.
154    ///
155    /// <https://w3c.github.io/trusted-types/dist/spec/#create-a-trusted-type-algorithm>
156    fn create_trusted_type<R, TrustedTypeCallback>(
157        &self,
158        expected_type: TrustedType,
159        input: DOMString,
160        arguments: Vec<HandleValue>,
161        trusted_type_creation_callback: TrustedTypeCallback,
162        can_gc: CanGc,
163    ) -> Fallible<DomRoot<R>>
164    where
165        R: DomObject,
166        TrustedTypeCallback: FnOnce(DOMString) -> DomRoot<R>,
167    {
168        // Step 1: Let policyValue be the result of executing Get Trusted Type policy value
169        // with the same arguments as this algorithm and additionally true as throwIfMissing.
170        let policy_value =
171            self.get_trusted_type_policy_value(expected_type, input, arguments, true, can_gc);
172        match policy_value {
173            // Step 2: If the algorithm threw an error, rethrow the error and abort the following steps.
174            Err(error) => Err(error),
175            Ok(policy_value) => {
176                // Step 3: Let dataString be the result of stringifying policyValue.
177                let data_string = match policy_value {
178                    Some(value) => value,
179                    // Step 4: If policyValue is null or undefined, set dataString to the empty string.
180                    None => DOMString::new(),
181                };
182                // Step 5: Return a new instance of an interface with a type name trustedTypeName,
183                // with its associated data value set to dataString.
184                Ok(trusted_type_creation_callback(data_string))
185            },
186        }
187    }
188}
189
190impl TrustedTypePolicyMethods<crate::DomTypeHolder> for TrustedTypePolicy {
191    /// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicy-name>
192    fn Name(&self) -> DOMString {
193        DOMString::from(&*self.name)
194    }
195    /// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicy-createhtml>
196    fn CreateHTML(
197        &self,
198        _cx: JSContext,
199        input: DOMString,
200        arguments: Vec<HandleValue>,
201        can_gc: CanGc,
202    ) -> Fallible<DomRoot<TrustedHTML>> {
203        self.create_trusted_type(
204            TrustedType::TrustedHTML,
205            input,
206            arguments,
207            |data_string| TrustedHTML::new(data_string, &self.global(), can_gc),
208            can_gc,
209        )
210    }
211    /// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicy-createscript>
212    fn CreateScript(
213        &self,
214        _cx: JSContext,
215        input: DOMString,
216        arguments: Vec<HandleValue>,
217        can_gc: CanGc,
218    ) -> Fallible<DomRoot<TrustedScript>> {
219        self.create_trusted_type(
220            TrustedType::TrustedScript,
221            input,
222            arguments,
223            |data_string| TrustedScript::new(data_string, &self.global(), can_gc),
224            can_gc,
225        )
226    }
227    /// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicy-createscripturl>
228    fn CreateScriptURL(
229        &self,
230        _cx: JSContext,
231        input: DOMString,
232        arguments: Vec<HandleValue>,
233        can_gc: CanGc,
234    ) -> Fallible<DomRoot<TrustedScriptURL>> {
235        self.create_trusted_type(
236            TrustedType::TrustedScriptURL,
237            input,
238            arguments,
239            |data_string| TrustedScriptURL::new(data_string, &self.global(), can_gc),
240            can_gc,
241        )
242    }
243}