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