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_macros::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    #[ignore_malloc_size_of = "Rc has unclear ownership"]
35    create_html: Option<Rc<CreateHTMLCallback>>,
36    #[ignore_malloc_size_of = "Rc has unclear ownership"]
37    create_script: Option<Rc<CreateScriptCallback>>,
38    #[ignore_malloc_size_of = "Rc has unclear ownership"]
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    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
77    pub(crate) fn new(
78        name: String,
79        options: &TrustedTypePolicyOptions,
80        global: &GlobalScope,
81        can_gc: CanGc,
82    ) -> DomRoot<Self> {
83        reflect_dom_object(Box::new(Self::new_inherited(name, options)), global, can_gc)
84    }
85
86    /// <https://w3c.github.io/trusted-types/dist/spec/#get-trusted-type-policy-value-algorithm>
87    fn check_callback_if_missing(throw_if_missing: bool) -> Fallible<Option<DOMString>> {
88        // Step 3.1: If throwIfMissing throw a TypeError.
89        if throw_if_missing {
90            Err(Type("Cannot find type".to_owned()))
91        } else {
92            // Step 3.2: Else return null.
93            Ok(None)
94        }
95    }
96
97    /// <https://w3c.github.io/trusted-types/dist/spec/#get-trusted-type-policy-value-algorithm>
98    pub(crate) fn get_trusted_type_policy_value(
99        &self,
100        expected_type: TrustedType,
101        input: DOMString,
102        arguments: Vec<HandleValue>,
103        throw_if_missing: bool,
104        can_gc: CanGc,
105    ) -> Fallible<Option<DOMString>> {
106        // Step 1: Let functionName be a function name for the given trustedTypeName, based on the following table:
107        match expected_type {
108            TrustedType::TrustedHTML => match &self.create_html {
109                // Step 3: If function is null, then:
110                None => TrustedTypePolicy::check_callback_if_missing(throw_if_missing),
111                // Step 2: Let function be policy’s options[functionName].
112                Some(callback) => {
113                    // Step 4: Let policyValue be the result of invoking function with value as a first argument,
114                    // items of arguments as subsequent arguments, and callback **this** value set to undefined,
115                    // rethrowing any exceptions.
116                    callback.Call__(input, arguments, ExceptionHandling::Rethrow, can_gc)
117                },
118            },
119            TrustedType::TrustedScript => match &self.create_script {
120                // Step 3: If function is null, then:
121                None => TrustedTypePolicy::check_callback_if_missing(throw_if_missing),
122                // Step 2: Let function be policy’s options[functionName].
123                Some(callback) => {
124                    // Step 4: Let policyValue be the result of invoking function with value as a first argument,
125                    // items of arguments as subsequent arguments, and callback **this** value set to undefined,
126                    // rethrowing any exceptions.
127                    callback.Call__(input, arguments, ExceptionHandling::Rethrow, can_gc)
128                },
129            },
130            TrustedType::TrustedScriptURL => match &self.create_script_url {
131                // Step 3: If function is null, then:
132                None => TrustedTypePolicy::check_callback_if_missing(throw_if_missing),
133                // Step 2: Let function be policy’s options[functionName].
134                Some(callback) => {
135                    // Step 4: Let policyValue be the result of invoking function with value as a first argument,
136                    // items of arguments as subsequent arguments, and callback **this** value set to undefined,
137                    // rethrowing any exceptions.
138                    callback
139                        .Call__(input, arguments, ExceptionHandling::Rethrow, can_gc)
140                        .map(|result| result.map(|str| DOMString::from(str.as_ref())))
141                },
142            },
143        }
144    }
145
146    /// This does not take all arguments as specified. That's because the return type of the
147    /// trusted type function and object are not the same. 2 of the 3 string callbacks return
148    /// a DOMString, while the other one returns an USVString. Additionally, all three callbacks
149    /// have a unique type signature in WebIDL.
150    ///
151    /// To circumvent these type problems, rather than implementing the full functionality here,
152    /// part of the algorithm is implemented on the caller side. There, we only call the callback
153    /// and create the object. The rest of the machinery is ensuring the right values pass through
154    /// to the relevant callbacks.
155    ///
156    /// <https://w3c.github.io/trusted-types/dist/spec/#create-a-trusted-type-algorithm>
157    fn create_trusted_type<R, TrustedTypeCallback>(
158        &self,
159        expected_type: TrustedType,
160        input: DOMString,
161        arguments: Vec<HandleValue>,
162        trusted_type_creation_callback: TrustedTypeCallback,
163        can_gc: CanGc,
164    ) -> Fallible<DomRoot<R>>
165    where
166        R: DomObject,
167        TrustedTypeCallback: FnOnce(DOMString) -> DomRoot<R>,
168    {
169        // Step 1: Let policyValue be the result of executing Get Trusted Type policy value
170        // with the same arguments as this algorithm and additionally true as throwIfMissing.
171        let policy_value =
172            self.get_trusted_type_policy_value(expected_type, input, arguments, true, can_gc);
173        match policy_value {
174            // Step 2: If the algorithm threw an error, rethrow the error and abort the following steps.
175            Err(error) => Err(error),
176            Ok(policy_value) => {
177                // Step 3: Let dataString be the result of stringifying policyValue.
178                let data_string = match policy_value {
179                    Some(value) => value,
180                    // Step 4: If policyValue is null or undefined, set dataString to the empty string.
181                    None => DOMString::new(),
182                };
183                // Step 5: Return a new instance of an interface with a type name trustedTypeName,
184                // with its associated data value set to dataString.
185                Ok(trusted_type_creation_callback(data_string))
186            },
187        }
188    }
189}
190
191impl TrustedTypePolicyMethods<crate::DomTypeHolder> for TrustedTypePolicy {
192    /// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicy-name>
193    fn Name(&self) -> DOMString {
194        DOMString::from(&*self.name)
195    }
196    /// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicy-createhtml>
197    fn CreateHTML(
198        &self,
199        _cx: JSContext,
200        input: DOMString,
201        arguments: Vec<HandleValue>,
202        can_gc: CanGc,
203    ) -> Fallible<DomRoot<TrustedHTML>> {
204        self.create_trusted_type(
205            TrustedType::TrustedHTML,
206            input,
207            arguments,
208            |data_string| TrustedHTML::new(data_string, &self.global(), can_gc),
209            can_gc,
210        )
211    }
212    /// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicy-createscript>
213    fn CreateScript(
214        &self,
215        _cx: JSContext,
216        input: DOMString,
217        arguments: Vec<HandleValue>,
218        can_gc: CanGc,
219    ) -> Fallible<DomRoot<TrustedScript>> {
220        self.create_trusted_type(
221            TrustedType::TrustedScript,
222            input,
223            arguments,
224            |data_string| TrustedScript::new(data_string, &self.global(), can_gc),
225            can_gc,
226        )
227    }
228    /// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicy-createscripturl>
229    fn CreateScriptURL(
230        &self,
231        _cx: JSContext,
232        input: DOMString,
233        arguments: Vec<HandleValue>,
234        can_gc: CanGc,
235    ) -> Fallible<DomRoot<TrustedScriptURL>> {
236        self.create_trusted_type(
237            TrustedType::TrustedScriptURL,
238            input,
239            arguments,
240            |data_string| TrustedScriptURL::new(data_string, &self.global(), can_gc),
241            can_gc,
242        )
243    }
244}