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///
33/// ["entry"]: https://html.spec.whatwg.org/multipage/#entry
34pub(crate) fn entry_global() -> DomRoot<GlobalScope> {
35    STACK
36        .with(|stack| {
37            stack
38                .borrow()
39                .iter()
40                .rev()
41                .find(|entry| entry.kind == StackEntryKind::Entry)
42                .map(|entry| DomRoot::from_ref(&*entry.global))
43        })
44        .unwrap()
45}
46
47/// Returns the ["incumbent"] global object.
48///
49/// ["incumbent"]: https://html.spec.whatwg.org/multipage/#incumbent
50pub(crate) fn incumbent_global() -> Option<DomRoot<GlobalScope>> {
51    // https://html.spec.whatwg.org/multipage/#incumbent-settings-object
52
53    // Step 1, 3: See what the JS engine has to say. If we've got a scripted
54    // caller override in place, the JS engine will lie to us and pretend that
55    // there's nothing on the JS stack, which will cause us to check the
56    // incumbent script stack below.
57    unsafe {
58        let Some(cx) = Runtime::get() else {
59            // It's not meaningful to return a global object if the runtime
60            // no longer exists.
61            return None;
62        };
63        let global = GetScriptedCallerGlobal(cx.as_ptr());
64        if !global.is_null() {
65            return Some(GlobalScope::from_object(global));
66        }
67    }
68
69    // Step 2: nothing from the JS engine. Let's use whatever's on the explicit stack.
70    STACK.with(|stack| {
71        stack
72            .borrow()
73            .last()
74            .map(|entry| DomRoot::from_ref(&*entry.global))
75    })
76}