Skip to main content

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::context::JSContext;
8use js::realm::CurrentRealm;
9use js::rust::HandleObject;
10use servo_config::prefs::get;
11
12use crate::DomTypes;
13use crate::codegen::Globals::Globals;
14use crate::interface::is_exposed_in;
15use crate::interfaces::GlobalScopeHelpers;
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: &mut 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(&mut 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: &mut JSContext) -> bool {
75    let realm = CurrentRealm::assert(cx);
76    D::GlobalScope::from_current_realm(&realm).is_secure_context()
77}
78
79impl Condition {
80    pub(crate) fn is_satisfied<D: DomTypes>(
81        &self,
82        cx: &mut JSContext,
83        obj: HandleObject,
84        global: HandleObject,
85    ) -> bool {
86        match *self {
87            Condition::Pref(name) => get().get_value(name).try_into().unwrap_or(false),
88            Condition::Func(f) => f(cx, obj),
89            Condition::Exposed(globals) => is_exposed_in(global, globals),
90            Condition::SecureContext() => is_secure_context::<D>(cx),
91            Condition::Satisfied => true,
92        }
93    }
94}