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