diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 67b8832efedd..f9d81ee210ba 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -156,7 +156,7 @@ impl JSTraceable for Rc { } } -impl JSTraceable for Box { +impl JSTraceable for Box { fn trace(&self, trc: *mut JSTracer) { (**self).trace(trc) } diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 3861e5123e14..24951c0c075f 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -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; diff --git a/components/script/dom/promise.rs b/components/script/dom/promise.rs index c99ae69792c0..79490e0bc13c 100644 --- a/components/script/dom/promise.rs +++ b/components/script/dom/promise.rs @@ -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}; @@ -82,7 +83,7 @@ impl Promise { pub fn maybe_resolve_native(&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()); } @@ -102,7 +103,7 @@ impl Promise { pub fn maybe_reject_native(&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()); } @@ -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)] @@ -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::(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() + } +} diff --git a/components/script/dom/promisenativehandler.rs b/components/script/dom/promisenativehandler.rs new file mode 100644 index 000000000000..603122556ef1 --- /dev/null +++ b/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>, + reject: Option>, +} + +impl PromiseNativeHandler { + pub fn new(global: GlobalRef, + resolve: Option>, + reject: Option>) + -> Root { + reflect_dom_object(box PromiseNativeHandler { + reflector: Reflector::new(), + resolve: resolve, + reject: reject, + }, global, PromiseNativeHandlerBinding::Wrap) + } + + fn callback(callback: &Option>, 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) + } +} diff --git a/components/script/dom/webidls/PromiseNativeHandler.webidl b/components/script/dom/webidls/PromiseNativeHandler.webidl new file mode 100644 index 000000000000..caa1692dfd37 --- /dev/null +++ b/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 { +};