diff options
Diffstat (limited to 'components/script/dom/bindings/settings_stack.rs')
-rw-r--r-- | components/script/dom/bindings/settings_stack.rs | 96 |
1 files changed, 95 insertions, 1 deletions
diff --git a/components/script/dom/bindings/settings_stack.rs b/components/script/dom/bindings/settings_stack.rs index df4759d677a..10afc833cb4 100644 --- a/components/script/dom/bindings/settings_stack.rs +++ b/components/script/dom/bindings/settings_stack.rs @@ -5,15 +5,26 @@ use dom::bindings::js::{JS, Root}; use dom::bindings::trace::JSTraceable; use dom::globalscope::GlobalScope; +use js::jsapi::GetScriptedCallerGlobal; +use js::jsapi::HideScriptedCaller; use js::jsapi::JSTracer; +use js::jsapi::UnhideScriptedCaller; +use js::rust::Runtime; use std::cell::RefCell; thread_local!(static STACK: RefCell<Vec<StackEntry>> = RefCell::new(Vec::new())); +#[derive(PartialEq, Eq, Debug, JSTraceable)] +enum StackEntryKind { + Incumbent, + Entry, +} + #[allow(unrooted_must_root)] #[derive(JSTraceable)] struct StackEntry { global: JS<GlobalScope>, + kind: StackEntryKind, } /// Traces the script settings stack. @@ -36,6 +47,7 @@ impl AutoEntryScript { let mut stack = stack.borrow_mut(); stack.push(StackEntry { global: JS::from_ref(global), + kind: StackEntryKind::Entry, }); AutoEntryScript { global: global as *const _ as usize, @@ -53,6 +65,7 @@ impl Drop for AutoEntryScript { assert_eq!(&*entry.global as *const GlobalScope as usize, self.global, "Dropped AutoEntryScript out of order."); + assert_eq!(entry.kind, StackEntryKind::Entry); trace!("Clean up after running script with {:p}", &*entry.global); }) } @@ -64,7 +77,88 @@ impl Drop for AutoEntryScript { pub fn entry_global() -> Root<GlobalScope> { STACK.with(|stack| { stack.borrow() - .last() + .iter() + .rev() + .find(|entry| entry.kind == StackEntryKind::Entry) .map(|entry| Root::from_ref(&*entry.global)) }).unwrap() } + +/// RAII struct that pushes and pops entries from the script settings stack. +pub struct AutoIncumbentScript { + global: usize, +} + +impl AutoIncumbentScript { + /// https://html.spec.whatwg.org/multipage/#prepare-to-run-a-callback + pub fn new(global: &GlobalScope) -> Self { + // Step 2-3. + unsafe { + let cx = Runtime::get(); + assert!(!cx.is_null()); + HideScriptedCaller(cx); + } + STACK.with(|stack| { + trace!("Prepare to run a callback with {:p}", global); + // Step 1. + let mut stack = stack.borrow_mut(); + stack.push(StackEntry { + global: JS::from_ref(global), + kind: StackEntryKind::Incumbent, + }); + AutoIncumbentScript { + global: global as *const _ as usize, + } + }) + } +} + +impl Drop for AutoIncumbentScript { + /// https://html.spec.whatwg.org/multipage/#clean-up-after-running-a-callback + fn drop(&mut self) { + STACK.with(|stack| { + // Step 4. + let mut stack = stack.borrow_mut(); + let entry = stack.pop().unwrap(); + // Step 3. + assert_eq!(&*entry.global as *const GlobalScope as usize, + self.global, + "Dropped AutoIncumbentScript out of order."); + assert_eq!(entry.kind, StackEntryKind::Incumbent); + trace!("Clean up after running a callback with {:p}", &*entry.global); + }); + unsafe { + // Step 1-2. + let cx = Runtime::get(); + assert!(!cx.is_null()); + UnhideScriptedCaller(cx); + } + } +} + +/// Returns the ["incumbent"] global object. +/// +/// ["incumbent"]: https://html.spec.whatwg.org/multipage/#incumbent +pub fn incumbent_global() -> Option<Root<GlobalScope>> { + // https://html.spec.whatwg.org/multipage/#incumbent-settings-object + + // Step 1, 3: See what the JS engine has to say. If we've got a scripted + // caller override in place, the JS engine will lie to us and pretend that + // there's nothing on the JS stack, which will cause us to check the + // incumbent script stack below. + unsafe { + let cx = Runtime::get(); + assert!(!cx.is_null()); + let global = GetScriptedCallerGlobal(cx); + if !global.is_null() { + return Some(GlobalScope::from_object(global)); + } + } + + // Step 2: nothing from the JS engine. Let's use whatever's on the explicit stack. + STACK.with(|stack| { + stack.borrow() + .last() + .map(|entry| Root::from_ref(&*entry.global)) + }) +} |