Skip to main content

script/dom/bindings/
settings_stack.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::cell::RefCell;
6
7use js::jsapi::{GetScriptedCallerGlobal, JSTracer};
8use js::rust::Runtime;
9use script_bindings::settings_stack::*;
10
11// use script_bindings::interfaces::{DomHelpers, GlobalScopeHelpers};
12use crate::dom::bindings::root::DomRoot;
13use crate::dom::bindings::trace::JSTraceable;
14use crate::dom::globalscope::GlobalScope;
15
16thread_local!(pub(super) static STACK: RefCell<Vec<StackEntry<crate::DomTypeHolder>>> = const {
17    RefCell::new(Vec::new())
18});
19
20/// Traces the script settings stack.
21pub(crate) unsafe fn trace(tracer: *mut JSTracer) {
22    STACK.with(|stack| {
23        unsafe { stack.borrow().trace(tracer) };
24    })
25}
26
27pub(crate) fn is_execution_stack_empty() -> bool {
28    STACK.with(|stack| stack.borrow().is_empty())
29}
30
31/// Returns the ["entry"] global object.
32/// Panics if there is no valid entry object on the stack.
33///
34/// ["entry"]: https://html.spec.whatwg.org/multipage/#entry
35pub(crate) fn entry_global() -> DomRoot<GlobalScope> {
36    maybe_entry_global().unwrap()
37}
38
39/// Returns the ["entry"] global object, if it exists.
40///
41/// ["entry"]: https://html.spec.whatwg.org/multipage/#entry
42pub(crate) fn maybe_entry_global() -> Option<DomRoot<GlobalScope>> {
43    STACK.with(|stack| {
44        stack
45            .borrow()
46            .iter()
47            .rev()
48            .find(|entry| entry.kind == StackEntryKind::Entry)
49            .map(|entry| DomRoot::from_ref(&*entry.global))
50    })
51}
52
53/// Returns the ["incumbent"] global object.
54///
55/// ["incumbent"]: https://html.spec.whatwg.org/multipage/#incumbent
56pub(crate) fn incumbent_global() -> Option<DomRoot<GlobalScope>> {
57    // https://html.spec.whatwg.org/multipage/#incumbent-settings-object
58
59    // Step 1, 3: See what the JS engine has to say. If we've got a scripted
60    // caller override in place, the JS engine will lie to us and pretend that
61    // there's nothing on the JS stack, which will cause us to check the
62    // incumbent script stack below.
63    unsafe {
64        let Some(cx) = Runtime::get() else {
65            // It's not meaningful to return a global object if the runtime
66            // no longer exists.
67            return None;
68        };
69        let global = GetScriptedCallerGlobal(cx.as_ptr());
70        if !global.is_null() {
71            return Some(GlobalScope::from_object(global));
72        }
73    }
74
75    // Step 2: nothing from the JS engine. Let's use whatever's on the explicit stack.
76    STACK.with(|stack| {
77        stack
78            .borrow()
79            .last()
80            .map(|entry| DomRoot::from_ref(&*entry.global))
81    })
82}