/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ //! Machinery to conditionally expose things. use js::rust::HandleObject; use servo_config::prefs::get; use crate::DomTypes; use crate::codegen::Globals::Globals; use crate::interface::is_exposed_in; use crate::interfaces::GlobalScopeHelpers; use crate::realms::{AlreadyInRealm, InRealm}; use crate::script_runtime::JSContext; /// A container with a list of conditions. pub(crate) struct Guard { conditions: &'static [Condition], value: T, } impl Guard { /// Construct a new guarded value. pub(crate) const fn new(conditions: &'static [Condition], value: T) -> Self { Guard { conditions, value } } /// Expose the value if the conditions are satisfied. /// /// The passed handle is the object on which the value may be exposed. pub(crate) fn expose( &self, cx: JSContext, obj: HandleObject, global: HandleObject, ) -> Option { let mut exposed_on_global = false; let conditions_satisfied = self.conditions.iter().all(|c| match c { Condition::Satisfied => { exposed_on_global = true; true }, // If there are multiple Exposed conditions, we just need one of them to be true Condition::Exposed(globals) => { exposed_on_global |= is_exposed_in(global, *globals); true }, _ => c.is_satisfied::(cx, obj, global), }); if conditions_satisfied && exposed_on_global { Some(self.value) } else { None } } } /// A condition to expose things. #[derive(Clone, Copy)] pub(crate) enum Condition { /// The condition is satisfied if the function returns true. Func(fn(JSContext, HandleObject) -> bool), /// The condition is satisfied if the preference is set. Pref(&'static str), // The condition is satisfied if the interface is exposed in the global. Exposed(Globals), SecureContext(), /// The condition is always satisfied. Satisfied, } fn is_secure_context(cx: JSContext) -> bool { unsafe { let in_realm_proof = AlreadyInRealm::assert_for_cx(JSContext::from_ptr(*cx)); D::GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof)).is_secure_context() } } impl Condition { pub(crate) fn is_satisfied( &self, cx: JSContext, obj: HandleObject, global: HandleObject, ) -> bool { match *self { Condition::Pref(name) => get().get_value(name).try_into().unwrap_or(false), Condition::Func(f) => f(cx, obj), Condition::Exposed(globals) => is_exposed_in(global, globals), Condition::SecureContext() => is_secure_context::(cx), Condition::Satisfied => true, } } }