script_bindings/
settings_stack.rs1use std::thread;
6
7use js::jsapi::{HideScriptedCaller, UnhideScriptedCaller};
8use js::rust::Runtime;
9
10use crate::DomTypes;
11use crate::interfaces::{DomHelpers, GlobalScopeHelpers};
12use crate::root::Dom;
13use crate::script_runtime::temp_cx;
14
15#[derive(Debug, Eq, JSTraceable, PartialEq)]
16pub enum StackEntryKind {
17 Incumbent,
18 Entry,
19}
20
21#[cfg_attr(crown, expect(crown::unrooted_must_root))]
22#[derive(JSTraceable)]
23pub struct StackEntry<D: DomTypes> {
24 pub global: Dom<D::GlobalScope>,
25 pub kind: StackEntryKind,
26}
27
28pub fn run_a_script<D: DomTypes, R>(global: &D::GlobalScope, f: impl FnOnce() -> R) -> R {
33 let settings_stack = <D as DomHelpers<D>>::settings_stack();
34 let _span = settings_stack.with(|stack| {
35 trace!("Prepare to run script with {:p}", global);
36 let mut stack = stack.borrow_mut();
37 stack.push(StackEntry {
38 global: Dom::from_ref(global),
39 kind: StackEntryKind::Entry,
40 });
41 profile_traits::info_span!("ScriptEvaluate", url = global.get_url().to_string()).entered()
42 });
43 let result = f();
44 let stack_is_empty = settings_stack.with(|stack| {
45 let mut stack = stack.borrow_mut();
46 let entry = stack.pop().unwrap();
47 assert_eq!(
48 &*entry.global as *const D::GlobalScope, global as *const D::GlobalScope,
49 "Dropped AutoEntryScript out of order."
50 );
51 assert_eq!(entry.kind, StackEntryKind::Entry);
52 trace!("Clean up after running script with {:p}", &*entry.global);
53 stack.is_empty()
54 });
55
56 if !thread::panicking() && stack_is_empty {
58 let mut cx = unsafe { temp_cx() };
59 global.perform_a_microtask_checkpoint(&mut cx);
60 }
61 result
62}
63
64pub fn run_a_callback<D: DomTypes, R>(global: &D::GlobalScope, f: impl FnOnce() -> R) -> R {
69 let settings_stack = <D as DomHelpers<D>>::settings_stack();
70 let cx = Runtime::get().expect("Creating a new incumbent script after runtime shutdown");
71 unsafe {
74 HideScriptedCaller(cx.as_ptr());
75 }
76 settings_stack.with(|stack| {
77 trace!("Prepare to run a callback with {:p}", global);
78 let mut stack = stack.borrow_mut();
80 stack.push(StackEntry {
81 global: Dom::from_ref(global),
82 kind: StackEntryKind::Incumbent,
83 });
84 });
85 let result = f();
86 settings_stack.with(|stack| {
88 let mut stack = stack.borrow_mut();
90 let entry = stack.pop().unwrap();
91 assert_eq!(
93 &*entry.global as *const D::GlobalScope as usize,
94 global as *const D::GlobalScope as usize,
95 "Dropped AutoIncumbentScript out of order."
96 );
97 assert_eq!(entry.kind, StackEntryKind::Incumbent);
98 trace!(
99 "Clean up after running a callback with {:p}",
100 &*entry.global
101 );
102 });
103 unsafe {
104 UnhideScriptedCaller(cx.as_ptr());
106 }
107 result
108}