script_bindings/
guard.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
5//! Machinery to conditionally expose things.
6
7use js::rust::HandleObject;
8use servo_config::prefs::get;
9
10use crate::DomTypes;
11use crate::codegen::Globals::Globals;
12use crate::interface::is_exposed_in;
13use crate::interfaces::GlobalScopeHelpers;
14use crate::realms::{AlreadyInRealm, InRealm};
15use crate::script_runtime::JSContext;
16
17/// A container with a list of conditions.
18pub(crate) struct Guard<T: Clone + Copy> {
19    conditions: &'static [Condition],
20    value: T,
21}
22
23impl<T: Clone + Copy> Guard<T> {
24    /// Construct a new guarded value.
25    pub(crate) const fn new(conditions: &'static [Condition], value: T) -> Self {
26        Guard { conditions, value }
27    }
28
29    /// Expose the value if the conditions are satisfied.
30    ///
31    /// The passed handle is the object on which the value may be exposed.
32    pub(crate) fn expose<D: DomTypes>(
33        &self,
34        cx: JSContext,
35        obj: HandleObject,
36        global: HandleObject,
37    ) -> Option<T> {
38        let mut exposed_on_global = false;
39        let conditions_satisfied = self.conditions.iter().all(|c| match c {
40            Condition::Satisfied => {
41                exposed_on_global = true;
42                true
43            },
44            // If there are multiple Exposed conditions, we just need one of them to be true
45            Condition::Exposed(globals) => {
46                exposed_on_global |= is_exposed_in(global, *globals);
47                true
48            },
49            _ => c.is_satisfied::<D>(cx, obj, global),
50        });
51
52        if conditions_satisfied && exposed_on_global {
53            Some(self.value)
54        } else {
55            None
56        }
57    }
58}
59
60/// A condition to expose things.
61#[derive(Clone, Copy)]
62pub(crate) enum Condition {
63    /// The condition is satisfied if the function returns true.
64    Func(fn(JSContext, HandleObject) -> bool),
65    /// The condition is satisfied if the preference is set.
66    Pref(&'static str),
67    // The condition is satisfied if the interface is exposed in the global.
68    Exposed(Globals),
69    SecureContext(),
70    /// The condition is always satisfied.
71    Satisfied,
72}
73
74fn is_secure_context<D: DomTypes>(cx: JSContext) -> bool {
75    unsafe {
76        let in_realm_proof = AlreadyInRealm::assert_for_cx(JSContext::from_ptr(*cx));
77        D::GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof)).is_secure_context()
78    }
79}
80
81impl Condition {
82    pub(crate) fn is_satisfied<D: DomTypes>(
83        &self,
84        cx: JSContext,
85        obj: HandleObject,
86        global: HandleObject,
87    ) -> bool {
88        match *self {
89            Condition::Pref(name) => get().get_value(name).try_into().unwrap_or(false),
90            Condition::Func(f) => f(cx, obj),
91            Condition::Exposed(globals) => is_exposed_in(global, globals),
92            Condition::SecureContext() => is_secure_context::<D>(cx),
93            Condition::Satisfied => true,
94        }
95    }
96}