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.

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

mercurial