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