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}