script_bindings/
settings_stack.rs1use std::thread;
6
7use js::context::JSContext;
8use js::jsapi::{HideScriptedCaller, UnhideScriptedCaller};
9use js::rust::Runtime;
10
11use crate::DomTypes;
12use crate::interfaces::{DomHelpers, GlobalScopeHelpers};
13use crate::root::Dom;
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, R, C>(cx: &mut C, global: &D::GlobalScope, f: impl FnOnce(&mut C) -> R) -> R
33where
34 C: AsMut<JSContext>,
35 D: DomTypes,
36{
37 let settings_stack = <D as DomHelpers<D>>::settings_stack();
38 let _span = settings_stack.with(|stack| {
39 trace!("Prepare to run script with {:p}", global);
40 let mut stack = stack.borrow_mut();
41 stack.push(StackEntry {
42 global: Dom::from_ref(global),
43 kind: StackEntryKind::Entry,
44 });
45 profile_traits::info_span!("ScriptEvaluate", url = global.get_url().to_string()).entered()
46 });
47 let result = f(cx);
48 let stack_is_empty = settings_stack.with(|stack| {
49 let mut stack = stack.borrow_mut();
50 let entry = stack.pop().unwrap();
51 assert_eq!(
52 &*entry.global as *const D::GlobalScope, global as *const D::GlobalScope,
53 "Dropped AutoEntryScript out of order."
54 );
55 assert_eq!(entry.kind, StackEntryKind::Entry);
56 trace!("Clean up after running script with {:p}", &*entry.global);
57 stack.is_empty()
58 });
59
60 if !thread::panicking() && stack_is_empty {
62 global.perform_a_microtask_checkpoint(cx.as_mut());
63 }
64 result
65}
66
67pub fn run_a_callback<D: DomTypes, R>(global: &D::GlobalScope, f: impl FnOnce() -> R) -> R {
72 let settings_stack = <D as DomHelpers<D>>::settings_stack();
73 let cx = Runtime::get().expect("Creating a new incumbent script after runtime shutdown");
74 unsafe {
77 HideScriptedCaller(cx.as_ptr());
78 }
79 settings_stack.with(|stack| {
80 trace!("Prepare to run a callback with {:p}", global);
81 let mut stack = stack.borrow_mut();
83 stack.push(StackEntry {
84 global: Dom::from_ref(global),
85 kind: StackEntryKind::Incumbent,
86 });
87 });
88 let result = f();
89 settings_stack.with(|stack| {
91 let mut stack = stack.borrow_mut();
93 let entry = stack.pop().unwrap();
94 assert_eq!(
96 &*entry.global as *const D::GlobalScope as usize,
97 global as *const D::GlobalScope as usize,
98 "Dropped AutoIncumbentScript out of order."
99 );
100 assert_eq!(entry.kind, StackEntryKind::Incumbent);
101 trace!(
102 "Clean up after running a callback with {:p}",
103 &*entry.global
104 );
105 });
106 unsafe {
107 UnhideScriptedCaller(cx.as_ptr());
109 }
110 result
111}