Skip to main content

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 script_bindings::reflector::{DomObject, Reflector, reflect_dom_object_with_cx};
10use strum::AsRefStr;
11
12use crate::dom::bindings::callback::ExceptionHandling;
13use crate::dom::bindings::codegen::Bindings::TrustedTypePolicyBinding::TrustedTypePolicyMethods;
14use crate::dom::bindings::codegen::Bindings::TrustedTypePolicyFactoryBinding::{
15    CreateHTMLCallback, CreateScriptCallback, CreateScriptURLCallback, TrustedTypePolicyOptions,
16};
17use crate::dom::bindings::codegen::UnionTypes::TrustedHTMLOrTrustedScriptOrTrustedScriptURLOrString as TrustedTypeOrString;
18use crate::dom::bindings::error::Error::Type;
19use crate::dom::bindings::error::Fallible;
20use crate::dom::bindings::reflector::DomGlobal;
21use crate::dom::bindings::root::DomRoot;
22use crate::dom::bindings::str::DOMString;
23use crate::dom::globalscope::GlobalScope;
24use crate::dom::trustedtypes::trustedhtml::TrustedHTML;
25use crate::dom::trustedtypes::trustedscript::TrustedScript;
26use crate::dom::trustedtypes::trustedscripturl::TrustedScriptURL;
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        cx: &mut js::context::JSContext,
78        name: String,
79        options: &TrustedTypePolicyOptions,
80        global: &GlobalScope,
81    ) -> DomRoot<Self> {
82        reflect_dom_object_with_cx(Box::new(Self::new_inherited(name, options)), global, cx)
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(c"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        cx: &mut js::context::JSContext,
100        expected_type: TrustedType,
101        input: DOMString,
102        arguments: Vec<HandleValue>,
103        throw_if_missing: bool,
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__(cx, input, arguments, ExceptionHandling::Rethrow)
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__(cx, input, arguments, ExceptionHandling::Rethrow)
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__(cx, input, arguments, ExceptionHandling::Rethrow)
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        cx: &mut js::context::JSContext,
159        expected_type: TrustedType,
160        input: DOMString,
161        arguments: Vec<HandleValue>,
162        trusted_type_creation_callback: TrustedTypeCallback,
163    ) -> Fallible<DomRoot<R>>
164    where
165        R: DomObject,
166        TrustedTypeCallback: FnOnce(&mut js::context::JSContext, 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(cx, expected_type, input, arguments, true);
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(cx, 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: &mut js::context::JSContext,
199        input: DOMString,
200        arguments: Vec<HandleValue>,
201    ) -> Fallible<DomRoot<TrustedHTML>> {
202        self.create_trusted_type(
203            cx,
204            TrustedType::TrustedHTML,
205            input,
206            arguments,
207            |cx, data_string| TrustedHTML::new(cx, data_string, &self.global()),
208        )
209    }
210    /// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicy-createscript>
211    fn CreateScript(
212        &self,
213        cx: &mut js::context::JSContext,
214        input: DOMString,
215        arguments: Vec<HandleValue>,
216    ) -> Fallible<DomRoot<TrustedScript>> {
217        self.create_trusted_type(
218            cx,
219            TrustedType::TrustedScript,
220            input,
221            arguments,
222            |cx, data_string| TrustedScript::new(cx, data_string, &self.global()),
223        )
224    }
225    /// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicy-createscripturl>
226    fn CreateScriptURL(
227        &self,
228        cx: &mut js::context::JSContext,
229        input: DOMString,
230        arguments: Vec<HandleValue>,
231    ) -> Fallible<DomRoot<TrustedScriptURL>> {
232        self.create_trusted_type(
233            cx,
234            TrustedType::TrustedScriptURL,
235            input,
236            arguments,
237            |cx, data_string| TrustedScriptURL::new(cx, data_string, &self.global()),
238        )
239    }
240}