aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/promise.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom/promise.rs')
-rw-r--r--components/script/dom/promise.rs307
1 files changed, 175 insertions, 132 deletions
diff --git a/components/script/dom/promise.rs b/components/script/dom/promise.rs
index 13689023462..18be293c625 100644
--- a/components/script/dom/promise.rs
+++ b/components/script/dom/promise.rs
@@ -1,6 +1,6 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
//! Native representation of JS Promise values.
//!
@@ -11,207 +11,230 @@
//! native Promise values that refer to the same JS value yet are distinct native objects
//! (ie. address equality for the native objects is meaningless).
-use dom::bindings::callback::CallbackContainer;
-use dom::bindings::codegen::Bindings::PromiseBinding::AnyCallback;
-use dom::bindings::conversions::root_from_object;
-use dom::bindings::error::{Error, Fallible};
-use dom::bindings::reflector::{DomObject, MutDomObject, Reflector};
-use dom::bindings::utils::AsCCharPtrPtr;
-use dom::globalscope::GlobalScope;
-use dom::promisenativehandler::PromiseNativeHandler;
+use crate::dom::bindings::conversions::root_from_object;
+use crate::dom::bindings::error::{Error, Fallible};
+use crate::dom::bindings::reflector::{DomObject, MutDomObject, Reflector};
+use crate::dom::bindings::settings_stack::AutoEntryScript;
+use crate::dom::bindings::utils::AsCCharPtrPtr;
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::promisenativehandler::PromiseNativeHandler;
+use crate::realms::{enter_realm, AlreadyInRealm, InRealm};
+use crate::script_runtime::JSContext as SafeJSContext;
+use crate::script_thread::ScriptThread;
use dom_struct::dom_struct;
use js::conversions::ToJSValConvertible;
-use js::jsapi::{CallOriginalPromiseResolve, CallOriginalPromiseReject, CallOriginalPromiseThen};
-use js::jsapi::{JSAutoCompartment, CallArgs, JS_GetFunctionObject, JS_NewFunction};
-use js::jsapi::{JSContext, HandleValue, HandleObject, IsPromiseObject, GetFunctionNativeReserved};
-use js::jsapi::{JS_ClearPendingException, JSObject, AddRawValueRoot, RemoveRawValueRoot, PromiseState};
-use js::jsapi::{MutableHandleObject, NewPromiseObject, ResolvePromise, RejectPromise, GetPromiseState};
-use js::jsapi::{SetFunctionNativeReserved, NewFunctionWithReserved, AddPromiseReactions};
-use js::jsapi::Heap;
-use js::jsval::{JSVal, UndefinedValue, ObjectValue, Int32Value};
+use js::jsapi::{AddRawValueRoot, CallArgs, GetFunctionNativeReserved};
+use js::jsapi::{Heap, JS_ClearPendingException};
+use js::jsapi::{JSAutoRealm, JSContext, JSObject, JS_GetFunctionObject};
+use js::jsapi::{JS_NewFunction, NewFunctionWithReserved};
+use js::jsapi::{PromiseState, PromiseUserInputEventHandlingState};
+use js::jsapi::{RemoveRawValueRoot, SetFunctionNativeReserved};
+use js::jsval::{Int32Value, JSVal, ObjectValue, UndefinedValue};
+use js::rust::wrappers::{
+ AddPromiseReactions, CallOriginalPromiseReject, CallOriginalPromiseResolve,
+};
+use js::rust::wrappers::{GetPromiseState, IsPromiseObject, NewPromiseObject, RejectPromise};
+use js::rust::wrappers::{ResolvePromise, SetPromiseUserInputEventHandlingState};
+use js::rust::{HandleObject, HandleValue, MutableHandleObject, Runtime};
use std::ptr;
use std::rc::Rc;
#[dom_struct]
+#[unrooted_must_root_lint::allow_unrooted_in_rc]
pub struct Promise {
reflector: Reflector,
/// Since Promise values are natively reference counted without the knowledge of
/// the SpiderMonkey GC, an explicit root for the reflector is stored while any
/// native instance exists. This ensures that the reflector will never be GCed
/// while native code could still interact with its native representation.
- #[ignore_heap_size_of = "SM handles JS values"]
+ #[ignore_malloc_size_of = "SM handles JS values"]
permanent_js_root: Heap<JSVal>,
}
/// Private helper to enable adding new methods to Rc<Promise>.
trait PromiseHelper {
- #[allow(unsafe_code)]
- unsafe fn initialize(&self, cx: *mut JSContext);
+ fn initialize(&self, cx: SafeJSContext);
}
impl PromiseHelper for Rc<Promise> {
#[allow(unsafe_code)]
- unsafe fn initialize(&self, cx: *mut JSContext) {
+ fn initialize(&self, cx: SafeJSContext) {
let obj = self.reflector().get_jsobject();
self.permanent_js_root.set(ObjectValue(*obj));
- assert!(AddRawValueRoot(cx,
- self.permanent_js_root.get_unsafe(),
- b"Promise::root\0".as_c_char_ptr()));
+ unsafe {
+ assert!(AddRawValueRoot(
+ *cx,
+ self.permanent_js_root.get_unsafe(),
+ b"Promise::root\0".as_c_char_ptr()
+ ));
+ }
}
}
impl Drop for Promise {
#[allow(unsafe_code)]
fn drop(&mut self) {
- let cx = self.global().get_cx();
unsafe {
+ let object = self.permanent_js_root.get().to_object();
+ assert!(!object.is_null());
+ let cx = Runtime::get();
+ assert!(!cx.is_null());
RemoveRawValueRoot(cx, self.permanent_js_root.get_unsafe());
}
}
}
impl Promise {
- #[allow(unsafe_code)]
pub fn new(global: &GlobalScope) -> Rc<Promise> {
+ let realm = enter_realm(&*global);
+ let comp = InRealm::Entered(&realm);
+ Promise::new_in_current_realm(global, comp)
+ }
+
+ pub fn new_in_current_realm(global: &GlobalScope, _comp: InRealm) -> Rc<Promise> {
let cx = global.get_cx();
- rooted!(in(cx) let mut obj = ptr::null_mut());
- unsafe {
- Promise::create_js_promise(cx, HandleObject::null(), obj.handle_mut());
- Promise::new_with_js_promise(obj.handle(), cx)
- }
+ rooted!(in(*cx) let mut obj = ptr::null_mut::<JSObject>());
+ Promise::create_js_promise(cx, obj.handle_mut());
+ Promise::new_with_js_promise(obj.handle(), cx)
}
- #[allow(unsafe_code, unrooted_must_root)]
+ #[allow(unsafe_code)]
pub fn duplicate(&self) -> Rc<Promise> {
let cx = self.global().get_cx();
- unsafe {
- Promise::new_with_js_promise(self.reflector().get_jsobject(), cx)
- }
+ Promise::new_with_js_promise(self.reflector().get_jsobject(), cx)
}
#[allow(unsafe_code, unrooted_must_root)]
- unsafe fn new_with_js_promise(obj: HandleObject, cx: *mut JSContext) -> Rc<Promise> {
- assert!(IsPromiseObject(obj));
- let promise = Promise {
- reflector: Reflector::new(),
- permanent_js_root: Heap::default(),
- };
- let mut promise = Rc::new(promise);
- Rc::get_mut(&mut promise).unwrap().init_reflector(obj.get());
- promise.initialize(cx);
- promise
+ pub fn new_with_js_promise(obj: HandleObject, cx: SafeJSContext) -> Rc<Promise> {
+ unsafe {
+ assert!(IsPromiseObject(obj));
+ let promise = Promise {
+ reflector: Reflector::new(),
+ permanent_js_root: Heap::default(),
+ };
+ let promise = Rc::new(promise);
+ promise.init_reflector(obj.get());
+ promise.initialize(cx);
+ promise
+ }
}
#[allow(unsafe_code)]
- unsafe fn create_js_promise(cx: *mut JSContext, proto: HandleObject, obj: MutableHandleObject) {
- let do_nothing_func = JS_NewFunction(cx, Some(do_nothing_promise_executor), /* nargs = */ 2,
- /* flags = */ 0, ptr::null());
- assert!(!do_nothing_func.is_null());
- rooted!(in(cx) let do_nothing_obj = JS_GetFunctionObject(do_nothing_func));
- assert!(!do_nothing_obj.is_null());
- obj.set(NewPromiseObject(cx, do_nothing_obj.handle(), proto));
- assert!(!obj.is_null());
+ fn create_js_promise(cx: SafeJSContext, mut obj: MutableHandleObject) {
+ unsafe {
+ let do_nothing_func = JS_NewFunction(
+ *cx,
+ Some(do_nothing_promise_executor),
+ /* nargs = */ 2,
+ /* flags = */ 0,
+ ptr::null(),
+ );
+ assert!(!do_nothing_func.is_null());
+ rooted!(in(*cx) let do_nothing_obj = JS_GetFunctionObject(do_nothing_func));
+ assert!(!do_nothing_obj.is_null());
+ obj.set(NewPromiseObject(*cx, do_nothing_obj.handle()));
+ assert!(!obj.is_null());
+ let is_user_interacting = if ScriptThread::is_user_interacting() {
+ PromiseUserInputEventHandlingState::HadUserInteractionAtCreation
+ } else {
+ PromiseUserInputEventHandlingState::DidntHaveUserInteractionAtCreation
+ };
+ SetPromiseUserInputEventHandlingState(obj.handle(), is_user_interacting);
+ }
}
#[allow(unrooted_must_root, unsafe_code)]
- pub fn Resolve(global: &GlobalScope,
- cx: *mut JSContext,
- value: HandleValue) -> Fallible<Rc<Promise>> {
- let _ac = JSAutoCompartment::new(cx, global.reflector().get_jsobject().get());
- rooted!(in(cx) let p = unsafe { CallOriginalPromiseResolve(cx, value) });
+ pub fn new_resolved(
+ global: &GlobalScope,
+ cx: SafeJSContext,
+ value: HandleValue,
+ ) -> Fallible<Rc<Promise>> {
+ let _ac = JSAutoRealm::new(*cx, global.reflector().get_jsobject().get());
+ rooted!(in(*cx) let p = unsafe { CallOriginalPromiseResolve(*cx, value) });
assert!(!p.handle().is_null());
- unsafe {
- Ok(Promise::new_with_js_promise(p.handle(), cx))
- }
+ Ok(Promise::new_with_js_promise(p.handle(), cx))
}
#[allow(unrooted_must_root, unsafe_code)]
- pub fn Reject(global: &GlobalScope,
- cx: *mut JSContext,
- value: HandleValue) -> Fallible<Rc<Promise>> {
- let _ac = JSAutoCompartment::new(cx, global.reflector().get_jsobject().get());
- rooted!(in(cx) let p = unsafe { CallOriginalPromiseReject(cx, value) });
+ pub fn new_rejected(
+ global: &GlobalScope,
+ cx: SafeJSContext,
+ value: HandleValue,
+ ) -> Fallible<Rc<Promise>> {
+ let _ac = JSAutoRealm::new(*cx, global.reflector().get_jsobject().get());
+ rooted!(in(*cx) let p = unsafe { CallOriginalPromiseReject(*cx, value) });
assert!(!p.handle().is_null());
- unsafe {
- Ok(Promise::new_with_js_promise(p.handle(), cx))
- }
+ Ok(Promise::new_with_js_promise(p.handle(), cx))
}
#[allow(unsafe_code)]
- pub fn resolve_native<T>(&self, cx: *mut JSContext, val: &T) where T: ToJSValConvertible {
- rooted!(in(cx) let mut v = UndefinedValue());
+ pub fn resolve_native<T>(&self, val: &T)
+ where
+ T: ToJSValConvertible,
+ {
+ let cx = self.global().get_cx();
+ let _ac = enter_realm(&*self);
+ rooted!(in(*cx) let mut v = UndefinedValue());
unsafe {
- val.to_jsval(cx, v.handle_mut());
+ val.to_jsval(*cx, v.handle_mut());
}
self.resolve(cx, v.handle());
}
#[allow(unrooted_must_root, unsafe_code)]
- pub fn resolve(&self, cx: *mut JSContext, value: HandleValue) {
+ pub fn resolve(&self, cx: SafeJSContext, value: HandleValue) {
unsafe {
- if !ResolvePromise(cx, self.promise_obj(), value) {
- JS_ClearPendingException(cx);
+ if !ResolvePromise(*cx, self.promise_obj(), value) {
+ JS_ClearPendingException(*cx);
}
}
}
#[allow(unsafe_code)]
- pub fn reject_native<T>(&self, cx: *mut JSContext, val: &T) where T: ToJSValConvertible {
- rooted!(in(cx) let mut v = UndefinedValue());
+ pub fn reject_native<T>(&self, val: &T)
+ where
+ T: ToJSValConvertible,
+ {
+ let cx = self.global().get_cx();
+ let _ac = enter_realm(&*self);
+ rooted!(in(*cx) let mut v = UndefinedValue());
unsafe {
- val.to_jsval(cx, v.handle_mut());
+ val.to_jsval(*cx, v.handle_mut());
}
self.reject(cx, v.handle());
}
#[allow(unsafe_code)]
- pub fn reject_error(&self, cx: *mut JSContext, error: Error) {
- rooted!(in(cx) let mut v = UndefinedValue());
+ pub fn reject_error(&self, error: Error) {
+ let cx = self.global().get_cx();
+ let _ac = enter_realm(&*self);
+ rooted!(in(*cx) let mut v = UndefinedValue());
unsafe {
- error.to_jsval(cx, &self.global(), v.handle_mut());
+ error.to_jsval(*cx, &self.global(), v.handle_mut());
}
self.reject(cx, v.handle());
}
#[allow(unrooted_must_root, unsafe_code)]
- pub fn reject(&self,
- cx: *mut JSContext,
- value: HandleValue) {
+ pub fn reject(&self, cx: SafeJSContext, value: HandleValue) {
unsafe {
- if !RejectPromise(cx, self.promise_obj(), value) {
- JS_ClearPendingException(cx);
+ if !RejectPromise(*cx, self.promise_obj(), value) {
+ JS_ClearPendingException(*cx);
}
}
}
- #[allow(unrooted_must_root, unsafe_code)]
- pub fn then(&self,
- cx: *mut JSContext,
- _callee: HandleObject,
- cb_resolve: AnyCallback,
- cb_reject: AnyCallback,
- result: MutableHandleObject) {
- let promise = self.promise_obj();
- rooted!(in(cx) let resolve = cb_resolve.callback());
- rooted!(in(cx) let reject = cb_reject.callback());
- unsafe {
- rooted!(in(cx) let res =
- CallOriginalPromiseThen(cx, promise, resolve.handle(), reject.handle()));
- result.set(*res);
- }
- }
-
#[allow(unsafe_code)]
- pub fn is_settled(&self) -> bool {
+ pub fn is_fulfilled(&self) -> bool {
let state = unsafe { GetPromiseState(self.promise_obj()) };
match state {
PromiseState::Rejected | PromiseState::Fulfilled => true,
- _ => false
+ _ => false,
}
}
#[allow(unsafe_code)]
- fn promise_obj(&self) -> HandleObject {
+ pub fn promise_obj(&self) -> HandleObject {
let obj = self.reflector().get_jsobject();
unsafe {
assert!(IsPromiseObject(obj));
@@ -220,30 +243,37 @@ impl Promise {
}
#[allow(unsafe_code)]
- pub fn append_native_handler(&self, handler: &PromiseNativeHandler) {
+ pub fn append_native_handler(&self, handler: &PromiseNativeHandler, _comp: InRealm) {
+ let _ais = AutoEntryScript::new(&*handler.global());
let cx = self.global().get_cx();
- rooted!(in(cx) let resolve_func =
- create_native_handler_function(cx,
+ rooted!(in(*cx) let resolve_func =
+ create_native_handler_function(*cx,
handler.reflector().get_jsobject(),
NativeHandlerTask::Resolve));
- rooted!(in(cx) let reject_func =
- create_native_handler_function(cx,
+ rooted!(in(*cx) let reject_func =
+ create_native_handler_function(*cx,
handler.reflector().get_jsobject(),
NativeHandlerTask::Reject));
unsafe {
- let ok = AddPromiseReactions(cx,
- self.promise_obj(),
- resolve_func.handle(),
- reject_func.handle());
+ let ok = AddPromiseReactions(
+ *cx,
+ self.promise_obj(),
+ resolve_func.handle(),
+ reject_func.handle(),
+ );
assert!(ok);
}
}
}
#[allow(unsafe_code)]
-unsafe extern fn do_nothing_promise_executor(_cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
+unsafe extern "C" fn do_nothing_promise_executor(
+ _cx: *mut JSContext,
+ argc: u32,
+ vp: *mut JSVal,
+) -> bool {
let args = CallArgs::from_vp(vp, argc);
*args.rval() = UndefinedValue();
true
@@ -259,18 +289,33 @@ enum NativeHandlerTask {
}
#[allow(unsafe_code)]
-unsafe extern fn native_handler_callback(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
+unsafe extern "C" fn native_handler_callback(
+ cx: *mut JSContext,
+ argc: u32,
+ vp: *mut JSVal,
+) -> bool {
+ let cx = SafeJSContext::from_ptr(cx);
+ let in_realm_proof = AlreadyInRealm::assert_for_cx(cx);
+
let args = CallArgs::from_vp(vp, argc);
- rooted!(in(cx) let v = *GetFunctionNativeReserved(args.callee(), SLOT_NATIVEHANDLER));
+ rooted!(in(*cx) let v = *GetFunctionNativeReserved(args.callee(), SLOT_NATIVEHANDLER));
assert!(v.get().is_object());
- let handler = root_from_object::<PromiseNativeHandler>(v.to_object())
- .ok().expect("unexpected value for native handler in promise native handler callback");
+ let handler = root_from_object::<PromiseNativeHandler>(v.to_object(), *cx)
+ .expect("unexpected value for native handler in promise native handler callback");
- rooted!(in(cx) let v = *GetFunctionNativeReserved(args.callee(), SLOT_NATIVEHANDLER_TASK));
+ rooted!(in(*cx) let v = *GetFunctionNativeReserved(args.callee(), SLOT_NATIVEHANDLER_TASK));
match v.to_int32() {
- v if v == NativeHandlerTask::Resolve as i32 => handler.resolved_callback(cx, args.get(0)),
- v if v == NativeHandlerTask::Reject as i32 => handler.rejected_callback(cx, args.get(0)),
+ v if v == NativeHandlerTask::Resolve as i32 => handler.resolved_callback(
+ *cx,
+ HandleValue::from_raw(args.get(0)),
+ InRealm::Already(&in_realm_proof),
+ ),
+ v if v == NativeHandlerTask::Reject as i32 => handler.rejected_callback(
+ *cx,
+ HandleValue::from_raw(args.get(0)),
+ InRealm::Already(&in_realm_proof),
+ ),
_ => panic!("unexpected native handler task value"),
};
@@ -278,21 +323,19 @@ unsafe extern fn native_handler_callback(cx: *mut JSContext, argc: u32, vp: *mut
}
#[allow(unsafe_code)]
-fn create_native_handler_function(cx: *mut JSContext,
- holder: HandleObject,
- task: NativeHandlerTask) -> *mut JSObject {
+fn create_native_handler_function(
+ cx: *mut JSContext,
+ holder: HandleObject,
+ task: NativeHandlerTask,
+) -> *mut JSObject {
unsafe {
let func = NewFunctionWithReserved(cx, Some(native_handler_callback), 1, 0, ptr::null());
assert!(!func.is_null());
rooted!(in(cx) let obj = JS_GetFunctionObject(func));
assert!(!obj.is_null());
- SetFunctionNativeReserved(obj.get(),
- SLOT_NATIVEHANDLER,
- &ObjectValue(*holder));
- SetFunctionNativeReserved(obj.get(),
- SLOT_NATIVEHANDLER_TASK,
- &Int32Value(task as i32));
+ SetFunctionNativeReserved(obj.get(), SLOT_NATIVEHANDLER, &ObjectValue(*holder));
+ SetFunctionNativeReserved(obj.get(), SLOT_NATIVEHANDLER_TASK, &Int32Value(task as i32));
obj.get()
}
}