1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/promise/PromiseCallback.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,378 @@ 1.4 +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "PromiseCallback.h" 1.11 +#include "mozilla/dom/Promise.h" 1.12 +#include "mozilla/dom/PromiseNativeHandler.h" 1.13 + 1.14 +#include "js/OldDebugAPI.h" 1.15 + 1.16 +namespace mozilla { 1.17 +namespace dom { 1.18 + 1.19 +NS_IMPL_CYCLE_COLLECTING_ADDREF(PromiseCallback) 1.20 +NS_IMPL_CYCLE_COLLECTING_RELEASE(PromiseCallback) 1.21 + 1.22 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PromiseCallback) 1.23 + NS_INTERFACE_MAP_ENTRY(nsISupports) 1.24 +NS_INTERFACE_MAP_END 1.25 + 1.26 +NS_IMPL_CYCLE_COLLECTION_CLASS(PromiseCallback) 1.27 + 1.28 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(PromiseCallback) 1.29 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.30 + 1.31 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(PromiseCallback) 1.32 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.33 + 1.34 +PromiseCallback::PromiseCallback() 1.35 +{ 1.36 + MOZ_COUNT_CTOR(PromiseCallback); 1.37 +} 1.38 + 1.39 +PromiseCallback::~PromiseCallback() 1.40 +{ 1.41 + MOZ_COUNT_DTOR(PromiseCallback); 1.42 +} 1.43 + 1.44 +// ResolvePromiseCallback 1.45 + 1.46 +NS_IMPL_CYCLE_COLLECTION_CLASS(ResolvePromiseCallback) 1.47 + 1.48 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ResolvePromiseCallback, 1.49 + PromiseCallback) 1.50 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise) 1.51 + tmp->mGlobal = nullptr; 1.52 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.53 + 1.54 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ResolvePromiseCallback, 1.55 + PromiseCallback) 1.56 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise) 1.57 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS 1.58 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.59 + 1.60 +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ResolvePromiseCallback) 1.61 + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal) 1.62 +NS_IMPL_CYCLE_COLLECTION_TRACE_END 1.63 + 1.64 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ResolvePromiseCallback) 1.65 +NS_INTERFACE_MAP_END_INHERITING(PromiseCallback) 1.66 + 1.67 +NS_IMPL_ADDREF_INHERITED(ResolvePromiseCallback, PromiseCallback) 1.68 +NS_IMPL_RELEASE_INHERITED(ResolvePromiseCallback, PromiseCallback) 1.69 + 1.70 +ResolvePromiseCallback::ResolvePromiseCallback(Promise* aPromise, 1.71 + JS::Handle<JSObject*> aGlobal) 1.72 + : mPromise(aPromise) 1.73 + , mGlobal(aGlobal) 1.74 +{ 1.75 + MOZ_ASSERT(aPromise); 1.76 + MOZ_ASSERT(aGlobal); 1.77 + MOZ_COUNT_CTOR(ResolvePromiseCallback); 1.78 + HoldJSObjects(this); 1.79 +} 1.80 + 1.81 +ResolvePromiseCallback::~ResolvePromiseCallback() 1.82 +{ 1.83 + MOZ_COUNT_DTOR(ResolvePromiseCallback); 1.84 + DropJSObjects(this); 1.85 +} 1.86 + 1.87 +void 1.88 +ResolvePromiseCallback::Call(JS::Handle<JS::Value> aValue) 1.89 +{ 1.90 + // Run resolver's algorithm with value and the synchronous flag set. 1.91 + ThreadsafeAutoSafeJSContext cx; 1.92 + 1.93 + JSAutoCompartment ac(cx, mGlobal); 1.94 + JS::Rooted<JS::Value> value(cx, aValue); 1.95 + if (!JS_WrapValue(cx, &value)) { 1.96 + NS_WARNING("Failed to wrap value into the right compartment."); 1.97 + return; 1.98 + } 1.99 + 1.100 + mPromise->ResolveInternal(cx, value, Promise::SyncTask); 1.101 +} 1.102 + 1.103 +// RejectPromiseCallback 1.104 + 1.105 +NS_IMPL_CYCLE_COLLECTION_CLASS(RejectPromiseCallback) 1.106 + 1.107 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(RejectPromiseCallback, 1.108 + PromiseCallback) 1.109 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise) 1.110 + tmp->mGlobal = nullptr; 1.111 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.112 + 1.113 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(RejectPromiseCallback, 1.114 + PromiseCallback) 1.115 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise) 1.116 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS 1.117 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.118 + 1.119 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(RejectPromiseCallback) 1.120 +NS_INTERFACE_MAP_END_INHERITING(PromiseCallback) 1.121 + 1.122 +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(RejectPromiseCallback, 1.123 + PromiseCallback) 1.124 + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal) 1.125 +NS_IMPL_CYCLE_COLLECTION_TRACE_END 1.126 + 1.127 +NS_IMPL_ADDREF_INHERITED(RejectPromiseCallback, PromiseCallback) 1.128 +NS_IMPL_RELEASE_INHERITED(RejectPromiseCallback, PromiseCallback) 1.129 + 1.130 +RejectPromiseCallback::RejectPromiseCallback(Promise* aPromise, 1.131 + JS::Handle<JSObject*> aGlobal) 1.132 + : mPromise(aPromise) 1.133 + , mGlobal(aGlobal) 1.134 +{ 1.135 + MOZ_ASSERT(aPromise); 1.136 + MOZ_ASSERT(mGlobal); 1.137 + MOZ_COUNT_CTOR(RejectPromiseCallback); 1.138 + HoldJSObjects(this); 1.139 +} 1.140 + 1.141 +RejectPromiseCallback::~RejectPromiseCallback() 1.142 +{ 1.143 + MOZ_COUNT_DTOR(RejectPromiseCallback); 1.144 + DropJSObjects(this); 1.145 +} 1.146 + 1.147 +void 1.148 +RejectPromiseCallback::Call(JS::Handle<JS::Value> aValue) 1.149 +{ 1.150 + // Run resolver's algorithm with value and the synchronous flag set. 1.151 + ThreadsafeAutoSafeJSContext cx; 1.152 + 1.153 + JSAutoCompartment ac(cx, mGlobal); 1.154 + JS::Rooted<JS::Value> value(cx, aValue); 1.155 + if (!JS_WrapValue(cx, &value)) { 1.156 + NS_WARNING("Failed to wrap value into the right compartment."); 1.157 + return; 1.158 + } 1.159 + 1.160 + 1.161 + mPromise->RejectInternal(cx, value, Promise::SyncTask); 1.162 +} 1.163 + 1.164 +// WrapperPromiseCallback 1.165 +NS_IMPL_CYCLE_COLLECTION_CLASS(WrapperPromiseCallback) 1.166 + 1.167 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WrapperPromiseCallback, 1.168 + PromiseCallback) 1.169 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mNextPromise) 1.170 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback) 1.171 + tmp->mGlobal = nullptr; 1.172 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.173 + 1.174 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WrapperPromiseCallback, 1.175 + PromiseCallback) 1.176 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNextPromise) 1.177 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback) 1.178 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS 1.179 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.180 + 1.181 +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(WrapperPromiseCallback) 1.182 + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal) 1.183 +NS_IMPL_CYCLE_COLLECTION_TRACE_END 1.184 + 1.185 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(WrapperPromiseCallback) 1.186 +NS_INTERFACE_MAP_END_INHERITING(PromiseCallback) 1.187 + 1.188 +NS_IMPL_ADDREF_INHERITED(WrapperPromiseCallback, PromiseCallback) 1.189 +NS_IMPL_RELEASE_INHERITED(WrapperPromiseCallback, PromiseCallback) 1.190 + 1.191 +WrapperPromiseCallback::WrapperPromiseCallback(Promise* aNextPromise, 1.192 + JS::Handle<JSObject*> aGlobal, 1.193 + AnyCallback* aCallback) 1.194 + : mNextPromise(aNextPromise) 1.195 + , mGlobal(aGlobal) 1.196 + , mCallback(aCallback) 1.197 +{ 1.198 + MOZ_ASSERT(aNextPromise); 1.199 + MOZ_ASSERT(aGlobal); 1.200 + MOZ_COUNT_CTOR(WrapperPromiseCallback); 1.201 + HoldJSObjects(this); 1.202 +} 1.203 + 1.204 +WrapperPromiseCallback::~WrapperPromiseCallback() 1.205 +{ 1.206 + MOZ_COUNT_DTOR(WrapperPromiseCallback); 1.207 + DropJSObjects(this); 1.208 +} 1.209 + 1.210 +void 1.211 +WrapperPromiseCallback::Call(JS::Handle<JS::Value> aValue) 1.212 +{ 1.213 + ThreadsafeAutoSafeJSContext cx; 1.214 + 1.215 + JSAutoCompartment ac(cx, mGlobal); 1.216 + JS::Rooted<JS::Value> value(cx, aValue); 1.217 + if (!JS_WrapValue(cx, &value)) { 1.218 + NS_WARNING("Failed to wrap value into the right compartment."); 1.219 + return; 1.220 + } 1.221 + 1.222 + ErrorResult rv; 1.223 + 1.224 + // If invoking callback threw an exception, run resolver's reject with the 1.225 + // thrown exception as argument and the synchronous flag set. 1.226 + JS::Rooted<JS::Value> retValue(cx); 1.227 + mCallback->Call(value, &retValue, rv, CallbackObject::eRethrowExceptions); 1.228 + 1.229 + rv.WouldReportJSException(); 1.230 + 1.231 + if (rv.Failed() && rv.IsJSException()) { 1.232 + JS::Rooted<JS::Value> value(cx); 1.233 + rv.StealJSException(cx, &value); 1.234 + 1.235 + if (!JS_WrapValue(cx, &value)) { 1.236 + NS_WARNING("Failed to wrap value into the right compartment."); 1.237 + return; 1.238 + } 1.239 + 1.240 + mNextPromise->RejectInternal(cx, value, Promise::SyncTask); 1.241 + return; 1.242 + } 1.243 + 1.244 + // If the return value is the same as the promise itself, throw TypeError. 1.245 + if (retValue.isObject()) { 1.246 + JS::Rooted<JSObject*> valueObj(cx, &retValue.toObject()); 1.247 + Promise* returnedPromise; 1.248 + nsresult r = UNWRAP_OBJECT(Promise, valueObj, returnedPromise); 1.249 + 1.250 + if (NS_SUCCEEDED(r) && returnedPromise == mNextPromise) { 1.251 + const char* fileName = nullptr; 1.252 + uint32_t lineNumber = 0; 1.253 + 1.254 + // Try to get some information about the callback to report a sane error, 1.255 + // but don't try too hard (only deals with scripted functions). 1.256 + JS::Rooted<JSObject*> unwrapped(cx, 1.257 + js::CheckedUnwrap(mCallback->Callback())); 1.258 + 1.259 + if (unwrapped) { 1.260 + JSAutoCompartment ac(cx, unwrapped); 1.261 + if (JS_ObjectIsFunction(cx, unwrapped)) { 1.262 + JS::Rooted<JS::Value> asValue(cx, JS::ObjectValue(*unwrapped)); 1.263 + JS::Rooted<JSFunction*> func(cx, JS_ValueToFunction(cx, asValue)); 1.264 + 1.265 + MOZ_ASSERT(func); 1.266 + JSScript* script = JS_GetFunctionScript(cx, func); 1.267 + if (script) { 1.268 + fileName = JS_GetScriptFilename(script); 1.269 + lineNumber = JS_GetScriptBaseLineNumber(cx, script); 1.270 + } 1.271 + } 1.272 + } 1.273 + 1.274 + // We're back in aValue's compartment here. 1.275 + JS::Rooted<JSString*> stack(cx, JS_GetEmptyString(JS_GetRuntime(cx))); 1.276 + JS::Rooted<JSString*> fn(cx, JS_NewStringCopyZ(cx, fileName)); 1.277 + if (!fn) { 1.278 + // Out of memory. Promise will stay unresolved. 1.279 + JS_ClearPendingException(cx); 1.280 + return; 1.281 + } 1.282 + 1.283 + JS::Rooted<JSString*> message(cx, 1.284 + JS_NewStringCopyZ(cx, 1.285 + "then() cannot return same Promise that it resolves.")); 1.286 + if (!message) { 1.287 + // Out of memory. Promise will stay unresolved. 1.288 + JS_ClearPendingException(cx); 1.289 + return; 1.290 + } 1.291 + 1.292 + JS::Rooted<JS::Value> typeError(cx); 1.293 + if (!JS::CreateTypeError(cx, stack, fn, lineNumber, 0, 1.294 + nullptr, message, &typeError)) { 1.295 + // Out of memory. Promise will stay unresolved. 1.296 + JS_ClearPendingException(cx); 1.297 + return; 1.298 + } 1.299 + 1.300 + mNextPromise->RejectInternal(cx, typeError, Promise::SyncTask); 1.301 + return; 1.302 + } 1.303 + } 1.304 + 1.305 + // Otherwise, run resolver's resolve with value and the synchronous flag 1.306 + // set. 1.307 + if (!JS_WrapValue(cx, &retValue)) { 1.308 + NS_WARNING("Failed to wrap value into the right compartment."); 1.309 + return; 1.310 + } 1.311 + 1.312 + mNextPromise->ResolveInternal(cx, retValue, Promise::SyncTask); 1.313 +} 1.314 + 1.315 +// NativePromiseCallback 1.316 + 1.317 +NS_IMPL_CYCLE_COLLECTION_INHERITED(NativePromiseCallback, 1.318 + PromiseCallback, mHandler) 1.319 + 1.320 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(NativePromiseCallback) 1.321 +NS_INTERFACE_MAP_END_INHERITING(PromiseCallback) 1.322 + 1.323 +NS_IMPL_ADDREF_INHERITED(NativePromiseCallback, PromiseCallback) 1.324 +NS_IMPL_RELEASE_INHERITED(NativePromiseCallback, PromiseCallback) 1.325 + 1.326 +NativePromiseCallback::NativePromiseCallback(PromiseNativeHandler* aHandler, 1.327 + Promise::PromiseState aState) 1.328 + : mHandler(aHandler) 1.329 + , mState(aState) 1.330 +{ 1.331 + MOZ_ASSERT(aHandler); 1.332 + MOZ_COUNT_CTOR(NativePromiseCallback); 1.333 +} 1.334 + 1.335 +NativePromiseCallback::~NativePromiseCallback() 1.336 +{ 1.337 + MOZ_COUNT_DTOR(NativePromiseCallback); 1.338 +} 1.339 + 1.340 +void 1.341 +NativePromiseCallback::Call(JS::Handle<JS::Value> aValue) 1.342 +{ 1.343 + if (mState == Promise::Resolved) { 1.344 + mHandler->ResolvedCallback(aValue); 1.345 + return; 1.346 + } 1.347 + 1.348 + if (mState == Promise::Rejected) { 1.349 + mHandler->RejectedCallback(aValue); 1.350 + return; 1.351 + } 1.352 + 1.353 + NS_NOTREACHED("huh?"); 1.354 +} 1.355 + 1.356 +/* static */ PromiseCallback* 1.357 +PromiseCallback::Factory(Promise* aNextPromise, JS::Handle<JSObject*> aGlobal, 1.358 + AnyCallback* aCallback, Task aTask) 1.359 +{ 1.360 + MOZ_ASSERT(aNextPromise); 1.361 + 1.362 + // If we have a callback and a next resolver, we have to exec the callback and 1.363 + // then propagate the return value to the next resolver->resolve(). 1.364 + if (aCallback) { 1.365 + return new WrapperPromiseCallback(aNextPromise, aGlobal, aCallback); 1.366 + } 1.367 + 1.368 + if (aTask == Resolve) { 1.369 + return new ResolvePromiseCallback(aNextPromise, aGlobal); 1.370 + } 1.371 + 1.372 + if (aTask == Reject) { 1.373 + return new RejectPromiseCallback(aNextPromise, aGlobal); 1.374 + } 1.375 + 1.376 + MOZ_ASSERT(false, "This should not happen"); 1.377 + return nullptr; 1.378 +} 1.379 + 1.380 +} // namespace dom 1.381 +} // namespace mozilla