Support native promise callbacks.

This commit is contained in:
Josh Matthews 2016-08-12 11:44:56 -04:00
parent 7ba3172ce0
commit ab168204ed
5 changed files with 138 additions and 3 deletions

View file

@ -156,7 +156,7 @@ impl<T: JSTraceable> JSTraceable for Rc<T> {
} }
} }
impl<T: JSTraceable> JSTraceable for Box<T> { impl<T: JSTraceable + ?Sized> JSTraceable for Box<T> {
fn trace(&self, trc: *mut JSTracer) { fn trace(&self, trc: *mut JSTracer) {
(**self).trace(trc) (**self).trace(trc)
} }

View file

@ -374,6 +374,7 @@ pub mod popstateevent;
pub mod processinginstruction; pub mod processinginstruction;
pub mod progressevent; pub mod progressevent;
pub mod promise; pub mod promise;
pub mod promisenativehandler;
pub mod radionodelist; pub mod radionodelist;
pub mod range; pub mod range;
pub mod request; pub mod request;

View file

@ -4,6 +4,7 @@
use dom::bindings::callback::CallbackContainer; use dom::bindings::callback::CallbackContainer;
use dom::bindings::codegen::Bindings::PromiseBinding::AnyCallback; use dom::bindings::codegen::Bindings::PromiseBinding::AnyCallback;
use dom::bindings::conversions::root_from_object;
use dom::bindings::error::Fallible; use dom::bindings::error::Fallible;
use dom::bindings::global::GlobalRef; use dom::bindings::global::GlobalRef;
use dom::bindings::reflector::{Reflectable, MutReflectable, Reflector}; use dom::bindings::reflector::{Reflectable, MutReflectable, Reflector};
@ -82,7 +83,7 @@ impl Promise {
pub fn maybe_resolve_native<T>(&self, cx: *mut JSContext, val: &T) where T: ToJSValConvertible { pub fn maybe_resolve_native<T>(&self, cx: *mut JSContext, val: &T) where T: ToJSValConvertible {
rooted!(in(cx) let mut v = UndefinedValue()); rooted!(in(cx) let mut v = UndefinedValue());
unsafe { unsafe {
val.to_jsval(cx, m.handle_mut()); val.to_jsval(cx, v.handle_mut());
} }
self.maybe_resolve(cx, v.handle()); self.maybe_resolve(cx, v.handle());
} }
@ -102,7 +103,7 @@ impl Promise {
pub fn maybe_reject_native<T>(&self, cx: *mut JSContext, val: &T) where T: ToJSValConvertible { pub fn maybe_reject_native<T>(&self, cx: *mut JSContext, val: &T) where T: ToJSValConvertible {
rooted!(in(cx) let mut v = UndefinedValue()); rooted!(in(cx) let mut v = UndefinedValue());
unsafe { unsafe {
val.to_jsval(cx, m.handle_mut()); val.to_jsval(cx, v.handle_mut());
} }
self.maybe_reject(cx, v.handle()); self.maybe_reject(cx, v.handle());
} }
@ -143,6 +144,29 @@ impl Promise {
} }
obj obj
} }
#[allow(unsafe_code)]
pub fn append_native_handler(&self, handler: &PromiseNativeHandler) {
let global = self.global();
let cx = global.r().get_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,
handler.reflector().get_jsobject(),
NativeHandlerTask::Reject));
unsafe {
let ok = AddPromiseReactions(cx,
self.promise_obj(),
resolve_func.handle(),
reject_func.handle());
assert!(ok);
}
}
} }
#[allow(unsafe_code)] #[allow(unsafe_code)]
@ -151,3 +175,51 @@ unsafe extern fn do_nothing_promise_executor(_cx: *mut JSContext, argc: u32, vp:
*args.rval() = UndefinedValue(); *args.rval() = UndefinedValue();
true true
} }
const SLOT_NATIVEHANDLER: usize = 0;
const SLOT_NATIVEHANDLER_TASK: usize = 1;
#[derive(PartialEq)]
enum NativeHandlerTask {
Resolve = 0,
Reject = 1,
}
#[allow(unsafe_code)]
unsafe extern fn native_handler_callback(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, argc);
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");
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)),
_ => panic!("unexpected native handler task value"),
};
true
}
#[allow(unsafe_code)]
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));
obj.get()
}
}

View file

@ -0,0 +1,49 @@
/* 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/. */
use dom::bindings::codegen::Bindings::PromiseNativeHandlerBinding;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::Root;
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::trace::JSTraceable;
use heapsize::HeapSizeOf;
use js::jsapi::{JSContext, HandleValue};
pub trait Callback: JSTraceable + HeapSizeOf {
fn callback(&self, cx: *mut JSContext, v: HandleValue);
}
#[dom_struct]
pub struct PromiseNativeHandler {
reflector: Reflector,
resolve: Option<Box<Callback>>,
reject: Option<Box<Callback>>,
}
impl PromiseNativeHandler {
pub fn new(global: GlobalRef,
resolve: Option<Box<Callback>>,
reject: Option<Box<Callback>>)
-> Root<PromiseNativeHandler> {
reflect_dom_object(box PromiseNativeHandler {
reflector: Reflector::new(),
resolve: resolve,
reject: reject,
}, global, PromiseNativeHandlerBinding::Wrap)
}
fn callback(callback: &Option<Box<Callback>>, cx: *mut JSContext, v: HandleValue) {
if let Some(ref callback) = *callback {
callback.callback(cx, v)
}
}
pub fn resolved_callback(&self, cx: *mut JSContext, v: HandleValue) {
PromiseNativeHandler::callback(&self.resolve, cx, v)
}
pub fn rejected_callback(&self, cx: *mut JSContext, v: HandleValue) {
PromiseNativeHandler::callback(&self.reject, cx, v)
}
}

View file

@ -0,0 +1,13 @@
/* 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/. */
// This interface is entirely internal to Servo, and should not be accessible to
// web pages.
// Hack to allow us to have JS owning and properly tracing/CCing/etc a
// PromiseNativeHandler.
[NoInterfaceObject,
Exposed=(Window,Worker)]
interface PromiseNativeHandler {
};