aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/bindings/settings_stack.rs
diff options
context:
space:
mode:
authorMs2ger <Ms2ger@gmail.com>2017-01-05 16:22:44 +0100
committerMs2ger <Ms2ger@gmail.com>2017-01-17 12:57:02 +0100
commit51df04d93eefc17e33018458a585259c4725603b (patch)
tree8adddcbb229895cc49f5d8812a1807345906c4fb /components/script/dom/bindings/settings_stack.rs
parent933b74781bf203a836330a850b2bbd12b02a83c8 (diff)
downloadservo-51df04d93eefc17e33018458a585259c4725603b.tar.gz
servo-51df04d93eefc17e33018458a585259c4725603b.zip
Implement the incumbent global.
Fixes #10963.
Diffstat (limited to 'components/script/dom/bindings/settings_stack.rs')
-rw-r--r--components/script/dom/bindings/settings_stack.rs96
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))
+ })
+}