dom/promise/PromiseCallback.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
michael@0 2 /* vim: set ts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "PromiseCallback.h"
michael@0 8 #include "mozilla/dom/Promise.h"
michael@0 9 #include "mozilla/dom/PromiseNativeHandler.h"
michael@0 10
michael@0 11 #include "js/OldDebugAPI.h"
michael@0 12
michael@0 13 namespace mozilla {
michael@0 14 namespace dom {
michael@0 15
michael@0 16 NS_IMPL_CYCLE_COLLECTING_ADDREF(PromiseCallback)
michael@0 17 NS_IMPL_CYCLE_COLLECTING_RELEASE(PromiseCallback)
michael@0 18
michael@0 19 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PromiseCallback)
michael@0 20 NS_INTERFACE_MAP_ENTRY(nsISupports)
michael@0 21 NS_INTERFACE_MAP_END
michael@0 22
michael@0 23 NS_IMPL_CYCLE_COLLECTION_CLASS(PromiseCallback)
michael@0 24
michael@0 25 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(PromiseCallback)
michael@0 26 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
michael@0 27
michael@0 28 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(PromiseCallback)
michael@0 29 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
michael@0 30
michael@0 31 PromiseCallback::PromiseCallback()
michael@0 32 {
michael@0 33 MOZ_COUNT_CTOR(PromiseCallback);
michael@0 34 }
michael@0 35
michael@0 36 PromiseCallback::~PromiseCallback()
michael@0 37 {
michael@0 38 MOZ_COUNT_DTOR(PromiseCallback);
michael@0 39 }
michael@0 40
michael@0 41 // ResolvePromiseCallback
michael@0 42
michael@0 43 NS_IMPL_CYCLE_COLLECTION_CLASS(ResolvePromiseCallback)
michael@0 44
michael@0 45 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ResolvePromiseCallback,
michael@0 46 PromiseCallback)
michael@0 47 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
michael@0 48 tmp->mGlobal = nullptr;
michael@0 49 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
michael@0 50
michael@0 51 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ResolvePromiseCallback,
michael@0 52 PromiseCallback)
michael@0 53 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise)
michael@0 54 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
michael@0 55 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
michael@0 56
michael@0 57 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ResolvePromiseCallback)
michael@0 58 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal)
michael@0 59 NS_IMPL_CYCLE_COLLECTION_TRACE_END
michael@0 60
michael@0 61 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ResolvePromiseCallback)
michael@0 62 NS_INTERFACE_MAP_END_INHERITING(PromiseCallback)
michael@0 63
michael@0 64 NS_IMPL_ADDREF_INHERITED(ResolvePromiseCallback, PromiseCallback)
michael@0 65 NS_IMPL_RELEASE_INHERITED(ResolvePromiseCallback, PromiseCallback)
michael@0 66
michael@0 67 ResolvePromiseCallback::ResolvePromiseCallback(Promise* aPromise,
michael@0 68 JS::Handle<JSObject*> aGlobal)
michael@0 69 : mPromise(aPromise)
michael@0 70 , mGlobal(aGlobal)
michael@0 71 {
michael@0 72 MOZ_ASSERT(aPromise);
michael@0 73 MOZ_ASSERT(aGlobal);
michael@0 74 MOZ_COUNT_CTOR(ResolvePromiseCallback);
michael@0 75 HoldJSObjects(this);
michael@0 76 }
michael@0 77
michael@0 78 ResolvePromiseCallback::~ResolvePromiseCallback()
michael@0 79 {
michael@0 80 MOZ_COUNT_DTOR(ResolvePromiseCallback);
michael@0 81 DropJSObjects(this);
michael@0 82 }
michael@0 83
michael@0 84 void
michael@0 85 ResolvePromiseCallback::Call(JS::Handle<JS::Value> aValue)
michael@0 86 {
michael@0 87 // Run resolver's algorithm with value and the synchronous flag set.
michael@0 88 ThreadsafeAutoSafeJSContext cx;
michael@0 89
michael@0 90 JSAutoCompartment ac(cx, mGlobal);
michael@0 91 JS::Rooted<JS::Value> value(cx, aValue);
michael@0 92 if (!JS_WrapValue(cx, &value)) {
michael@0 93 NS_WARNING("Failed to wrap value into the right compartment.");
michael@0 94 return;
michael@0 95 }
michael@0 96
michael@0 97 mPromise->ResolveInternal(cx, value, Promise::SyncTask);
michael@0 98 }
michael@0 99
michael@0 100 // RejectPromiseCallback
michael@0 101
michael@0 102 NS_IMPL_CYCLE_COLLECTION_CLASS(RejectPromiseCallback)
michael@0 103
michael@0 104 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(RejectPromiseCallback,
michael@0 105 PromiseCallback)
michael@0 106 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
michael@0 107 tmp->mGlobal = nullptr;
michael@0 108 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
michael@0 109
michael@0 110 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(RejectPromiseCallback,
michael@0 111 PromiseCallback)
michael@0 112 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise)
michael@0 113 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
michael@0 114 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
michael@0 115
michael@0 116 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(RejectPromiseCallback)
michael@0 117 NS_INTERFACE_MAP_END_INHERITING(PromiseCallback)
michael@0 118
michael@0 119 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(RejectPromiseCallback,
michael@0 120 PromiseCallback)
michael@0 121 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal)
michael@0 122 NS_IMPL_CYCLE_COLLECTION_TRACE_END
michael@0 123
michael@0 124 NS_IMPL_ADDREF_INHERITED(RejectPromiseCallback, PromiseCallback)
michael@0 125 NS_IMPL_RELEASE_INHERITED(RejectPromiseCallback, PromiseCallback)
michael@0 126
michael@0 127 RejectPromiseCallback::RejectPromiseCallback(Promise* aPromise,
michael@0 128 JS::Handle<JSObject*> aGlobal)
michael@0 129 : mPromise(aPromise)
michael@0 130 , mGlobal(aGlobal)
michael@0 131 {
michael@0 132 MOZ_ASSERT(aPromise);
michael@0 133 MOZ_ASSERT(mGlobal);
michael@0 134 MOZ_COUNT_CTOR(RejectPromiseCallback);
michael@0 135 HoldJSObjects(this);
michael@0 136 }
michael@0 137
michael@0 138 RejectPromiseCallback::~RejectPromiseCallback()
michael@0 139 {
michael@0 140 MOZ_COUNT_DTOR(RejectPromiseCallback);
michael@0 141 DropJSObjects(this);
michael@0 142 }
michael@0 143
michael@0 144 void
michael@0 145 RejectPromiseCallback::Call(JS::Handle<JS::Value> aValue)
michael@0 146 {
michael@0 147 // Run resolver's algorithm with value and the synchronous flag set.
michael@0 148 ThreadsafeAutoSafeJSContext cx;
michael@0 149
michael@0 150 JSAutoCompartment ac(cx, mGlobal);
michael@0 151 JS::Rooted<JS::Value> value(cx, aValue);
michael@0 152 if (!JS_WrapValue(cx, &value)) {
michael@0 153 NS_WARNING("Failed to wrap value into the right compartment.");
michael@0 154 return;
michael@0 155 }
michael@0 156
michael@0 157
michael@0 158 mPromise->RejectInternal(cx, value, Promise::SyncTask);
michael@0 159 }
michael@0 160
michael@0 161 // WrapperPromiseCallback
michael@0 162 NS_IMPL_CYCLE_COLLECTION_CLASS(WrapperPromiseCallback)
michael@0 163
michael@0 164 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WrapperPromiseCallback,
michael@0 165 PromiseCallback)
michael@0 166 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNextPromise)
michael@0 167 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
michael@0 168 tmp->mGlobal = nullptr;
michael@0 169 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
michael@0 170
michael@0 171 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WrapperPromiseCallback,
michael@0 172 PromiseCallback)
michael@0 173 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNextPromise)
michael@0 174 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
michael@0 175 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
michael@0 176 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
michael@0 177
michael@0 178 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(WrapperPromiseCallback)
michael@0 179 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal)
michael@0 180 NS_IMPL_CYCLE_COLLECTION_TRACE_END
michael@0 181
michael@0 182 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(WrapperPromiseCallback)
michael@0 183 NS_INTERFACE_MAP_END_INHERITING(PromiseCallback)
michael@0 184
michael@0 185 NS_IMPL_ADDREF_INHERITED(WrapperPromiseCallback, PromiseCallback)
michael@0 186 NS_IMPL_RELEASE_INHERITED(WrapperPromiseCallback, PromiseCallback)
michael@0 187
michael@0 188 WrapperPromiseCallback::WrapperPromiseCallback(Promise* aNextPromise,
michael@0 189 JS::Handle<JSObject*> aGlobal,
michael@0 190 AnyCallback* aCallback)
michael@0 191 : mNextPromise(aNextPromise)
michael@0 192 , mGlobal(aGlobal)
michael@0 193 , mCallback(aCallback)
michael@0 194 {
michael@0 195 MOZ_ASSERT(aNextPromise);
michael@0 196 MOZ_ASSERT(aGlobal);
michael@0 197 MOZ_COUNT_CTOR(WrapperPromiseCallback);
michael@0 198 HoldJSObjects(this);
michael@0 199 }
michael@0 200
michael@0 201 WrapperPromiseCallback::~WrapperPromiseCallback()
michael@0 202 {
michael@0 203 MOZ_COUNT_DTOR(WrapperPromiseCallback);
michael@0 204 DropJSObjects(this);
michael@0 205 }
michael@0 206
michael@0 207 void
michael@0 208 WrapperPromiseCallback::Call(JS::Handle<JS::Value> aValue)
michael@0 209 {
michael@0 210 ThreadsafeAutoSafeJSContext cx;
michael@0 211
michael@0 212 JSAutoCompartment ac(cx, mGlobal);
michael@0 213 JS::Rooted<JS::Value> value(cx, aValue);
michael@0 214 if (!JS_WrapValue(cx, &value)) {
michael@0 215 NS_WARNING("Failed to wrap value into the right compartment.");
michael@0 216 return;
michael@0 217 }
michael@0 218
michael@0 219 ErrorResult rv;
michael@0 220
michael@0 221 // If invoking callback threw an exception, run resolver's reject with the
michael@0 222 // thrown exception as argument and the synchronous flag set.
michael@0 223 JS::Rooted<JS::Value> retValue(cx);
michael@0 224 mCallback->Call(value, &retValue, rv, CallbackObject::eRethrowExceptions);
michael@0 225
michael@0 226 rv.WouldReportJSException();
michael@0 227
michael@0 228 if (rv.Failed() && rv.IsJSException()) {
michael@0 229 JS::Rooted<JS::Value> value(cx);
michael@0 230 rv.StealJSException(cx, &value);
michael@0 231
michael@0 232 if (!JS_WrapValue(cx, &value)) {
michael@0 233 NS_WARNING("Failed to wrap value into the right compartment.");
michael@0 234 return;
michael@0 235 }
michael@0 236
michael@0 237 mNextPromise->RejectInternal(cx, value, Promise::SyncTask);
michael@0 238 return;
michael@0 239 }
michael@0 240
michael@0 241 // If the return value is the same as the promise itself, throw TypeError.
michael@0 242 if (retValue.isObject()) {
michael@0 243 JS::Rooted<JSObject*> valueObj(cx, &retValue.toObject());
michael@0 244 Promise* returnedPromise;
michael@0 245 nsresult r = UNWRAP_OBJECT(Promise, valueObj, returnedPromise);
michael@0 246
michael@0 247 if (NS_SUCCEEDED(r) && returnedPromise == mNextPromise) {
michael@0 248 const char* fileName = nullptr;
michael@0 249 uint32_t lineNumber = 0;
michael@0 250
michael@0 251 // Try to get some information about the callback to report a sane error,
michael@0 252 // but don't try too hard (only deals with scripted functions).
michael@0 253 JS::Rooted<JSObject*> unwrapped(cx,
michael@0 254 js::CheckedUnwrap(mCallback->Callback()));
michael@0 255
michael@0 256 if (unwrapped) {
michael@0 257 JSAutoCompartment ac(cx, unwrapped);
michael@0 258 if (JS_ObjectIsFunction(cx, unwrapped)) {
michael@0 259 JS::Rooted<JS::Value> asValue(cx, JS::ObjectValue(*unwrapped));
michael@0 260 JS::Rooted<JSFunction*> func(cx, JS_ValueToFunction(cx, asValue));
michael@0 261
michael@0 262 MOZ_ASSERT(func);
michael@0 263 JSScript* script = JS_GetFunctionScript(cx, func);
michael@0 264 if (script) {
michael@0 265 fileName = JS_GetScriptFilename(script);
michael@0 266 lineNumber = JS_GetScriptBaseLineNumber(cx, script);
michael@0 267 }
michael@0 268 }
michael@0 269 }
michael@0 270
michael@0 271 // We're back in aValue's compartment here.
michael@0 272 JS::Rooted<JSString*> stack(cx, JS_GetEmptyString(JS_GetRuntime(cx)));
michael@0 273 JS::Rooted<JSString*> fn(cx, JS_NewStringCopyZ(cx, fileName));
michael@0 274 if (!fn) {
michael@0 275 // Out of memory. Promise will stay unresolved.
michael@0 276 JS_ClearPendingException(cx);
michael@0 277 return;
michael@0 278 }
michael@0 279
michael@0 280 JS::Rooted<JSString*> message(cx,
michael@0 281 JS_NewStringCopyZ(cx,
michael@0 282 "then() cannot return same Promise that it resolves."));
michael@0 283 if (!message) {
michael@0 284 // Out of memory. Promise will stay unresolved.
michael@0 285 JS_ClearPendingException(cx);
michael@0 286 return;
michael@0 287 }
michael@0 288
michael@0 289 JS::Rooted<JS::Value> typeError(cx);
michael@0 290 if (!JS::CreateTypeError(cx, stack, fn, lineNumber, 0,
michael@0 291 nullptr, message, &typeError)) {
michael@0 292 // Out of memory. Promise will stay unresolved.
michael@0 293 JS_ClearPendingException(cx);
michael@0 294 return;
michael@0 295 }
michael@0 296
michael@0 297 mNextPromise->RejectInternal(cx, typeError, Promise::SyncTask);
michael@0 298 return;
michael@0 299 }
michael@0 300 }
michael@0 301
michael@0 302 // Otherwise, run resolver's resolve with value and the synchronous flag
michael@0 303 // set.
michael@0 304 if (!JS_WrapValue(cx, &retValue)) {
michael@0 305 NS_WARNING("Failed to wrap value into the right compartment.");
michael@0 306 return;
michael@0 307 }
michael@0 308
michael@0 309 mNextPromise->ResolveInternal(cx, retValue, Promise::SyncTask);
michael@0 310 }
michael@0 311
michael@0 312 // NativePromiseCallback
michael@0 313
michael@0 314 NS_IMPL_CYCLE_COLLECTION_INHERITED(NativePromiseCallback,
michael@0 315 PromiseCallback, mHandler)
michael@0 316
michael@0 317 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(NativePromiseCallback)
michael@0 318 NS_INTERFACE_MAP_END_INHERITING(PromiseCallback)
michael@0 319
michael@0 320 NS_IMPL_ADDREF_INHERITED(NativePromiseCallback, PromiseCallback)
michael@0 321 NS_IMPL_RELEASE_INHERITED(NativePromiseCallback, PromiseCallback)
michael@0 322
michael@0 323 NativePromiseCallback::NativePromiseCallback(PromiseNativeHandler* aHandler,
michael@0 324 Promise::PromiseState aState)
michael@0 325 : mHandler(aHandler)
michael@0 326 , mState(aState)
michael@0 327 {
michael@0 328 MOZ_ASSERT(aHandler);
michael@0 329 MOZ_COUNT_CTOR(NativePromiseCallback);
michael@0 330 }
michael@0 331
michael@0 332 NativePromiseCallback::~NativePromiseCallback()
michael@0 333 {
michael@0 334 MOZ_COUNT_DTOR(NativePromiseCallback);
michael@0 335 }
michael@0 336
michael@0 337 void
michael@0 338 NativePromiseCallback::Call(JS::Handle<JS::Value> aValue)
michael@0 339 {
michael@0 340 if (mState == Promise::Resolved) {
michael@0 341 mHandler->ResolvedCallback(aValue);
michael@0 342 return;
michael@0 343 }
michael@0 344
michael@0 345 if (mState == Promise::Rejected) {
michael@0 346 mHandler->RejectedCallback(aValue);
michael@0 347 return;
michael@0 348 }
michael@0 349
michael@0 350 NS_NOTREACHED("huh?");
michael@0 351 }
michael@0 352
michael@0 353 /* static */ PromiseCallback*
michael@0 354 PromiseCallback::Factory(Promise* aNextPromise, JS::Handle<JSObject*> aGlobal,
michael@0 355 AnyCallback* aCallback, Task aTask)
michael@0 356 {
michael@0 357 MOZ_ASSERT(aNextPromise);
michael@0 358
michael@0 359 // If we have a callback and a next resolver, we have to exec the callback and
michael@0 360 // then propagate the return value to the next resolver->resolve().
michael@0 361 if (aCallback) {
michael@0 362 return new WrapperPromiseCallback(aNextPromise, aGlobal, aCallback);
michael@0 363 }
michael@0 364
michael@0 365 if (aTask == Resolve) {
michael@0 366 return new ResolvePromiseCallback(aNextPromise, aGlobal);
michael@0 367 }
michael@0 368
michael@0 369 if (aTask == Reject) {
michael@0 370 return new RejectPromiseCallback(aNextPromise, aGlobal);
michael@0 371 }
michael@0 372
michael@0 373 MOZ_ASSERT(false, "This should not happen");
michael@0 374 return nullptr;
michael@0 375 }
michael@0 376
michael@0 377 } // namespace dom
michael@0 378 } // namespace mozilla

mercurial