Skip to content

Commit

Permalink
Support native promise callbacks.
Browse files Browse the repository at this point in the history
  • Loading branch information
jdm committed Sep 22, 2016
1 parent 7ba3172 commit ab16820
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 3 deletions.
2 changes: 1 addition & 1 deletion components/script/dom/bindings/trace.rs
Expand Up @@ -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) {
(**self).trace(trc)
}
Expand Down
1 change: 1 addition & 0 deletions components/script/dom/mod.rs
Expand Up @@ -374,6 +374,7 @@ pub mod popstateevent;
pub mod processinginstruction;
pub mod progressevent;
pub mod promise;
pub mod promisenativehandler;
pub mod radionodelist;
pub mod range;
pub mod request;
Expand Down
76 changes: 74 additions & 2 deletions components/script/dom/promise.rs
Expand Up @@ -4,6 +4,7 @@

use dom::bindings::callback::CallbackContainer;
use dom::bindings::codegen::Bindings::PromiseBinding::AnyCallback;
use dom::bindings::conversions::root_from_object;
use dom::bindings::error::Fallible;
use dom::bindings::global::GlobalRef;
use dom::bindings::reflector::{Reflectable, MutReflectable, Reflector};
Expand Down Expand Up @@ -82,7 +83,7 @@ impl Promise {
pub fn maybe_resolve_native<T>(&self, cx: *mut JSContext, val: &T) where T: ToJSValConvertible {
rooted!(in(cx) let mut v = UndefinedValue());
unsafe {
val.to_jsval(cx, m.handle_mut());
val.to_jsval(cx, v.handle_mut());
}
self.maybe_resolve(cx, v.handle());
}
Expand All @@ -102,7 +103,7 @@ impl Promise {
pub fn maybe_reject_native<T>(&self, cx: *mut JSContext, val: &T) where T: ToJSValConvertible {
rooted!(in(cx) let mut v = UndefinedValue());
unsafe {
val.to_jsval(cx, m.handle_mut());
val.to_jsval(cx, v.handle_mut());
}
self.maybe_reject(cx, v.handle());
}
Expand Down Expand Up @@ -143,6 +144,29 @@ impl Promise {
}
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)]
Expand All @@ -151,3 +175,51 @@ unsafe extern fn do_nothing_promise_executor(_cx: *mut JSContext, argc: u32, vp:
*args.rval() = UndefinedValue();
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()
}
}
49 changes: 49 additions & 0 deletions components/script/dom/promisenativehandler.rs
@@ -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)
}
}
13 changes: 13 additions & 0 deletions components/script/dom/webidls/PromiseNativeHandler.webidl
@@ -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 {
};

0 comments on commit ab16820

Please sign in to comment.