script_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::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
28/// Wrapper that pushes and pops entries from the script settings stack.
29///
30/// <https://html.spec.whatwg.org/multipage/#prepare-to-run-script>
31/// <https://html.spec.whatwg.org/multipage/#clean-up-after-running-script>
32pub 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    // Step 5
57    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
64/// Wrapper that pushes and pops entries from the script settings stack.
65///
66/// <https://html.spec.whatwg.org/multipage/#prepare-to-run-a-callback>
67/// <https://html.spec.whatwg.org/multipage/#clean-up-after-running-a-callback>
68pub 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    // <https://html.spec.whatwg.org/multipage/#prepare-to-run-a-callback>
72    // Step 2-3.
73    unsafe {
74        HideScriptedCaller(cx.as_ptr());
75    }
76    settings_stack.with(|stack| {
77        trace!("Prepare to run a callback with {:p}", global);
78        // Step 1.
79        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    // <https://html.spec.whatwg.org/multipage/#clean-up-after-running-a-callback>
87    settings_stack.with(|stack| {
88        // Step 4.
89        let mut stack = stack.borrow_mut();
90        let entry = stack.pop().unwrap();
91        // Step 3.
92        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        // Step 1-2.
105        UnhideScriptedCaller(cx.as_ptr());
106    }
107    result
108}