tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Promise-inl.h (13423B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=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/. */
      6 
      7 #ifndef mozilla_dom_Promise_inl_h
      8 #define mozilla_dom_Promise_inl_h
      9 
     10 #include <type_traits>
     11 #include <utility>
     12 
     13 #include "mozilla/AlreadyAddRefed.h"
     14 #include "mozilla/dom/BindingUtils.h"
     15 #include "mozilla/dom/Promise.h"
     16 #include "mozilla/dom/PromiseNativeHandler.h"
     17 #include "nsCycleCollectionParticipant.h"
     18 
     19 namespace mozilla::dom {
     20 
     21 class PromiseNativeThenHandlerBase : public PromiseNativeHandler {
     22  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     23  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PromiseNativeThenHandlerBase)
     24 
     25  PromiseNativeThenHandlerBase(Promise* aPromise) : mPromise(aPromise) {}
     26 
     27  virtual bool HasResolvedCallback() = 0;
     28  virtual bool HasRejectedCallback() = 0;
     29 
     30  MOZ_CAN_RUN_SCRIPT void ResolvedCallback(JSContext* aCx,
     31                                           JS::Handle<JS::Value> aValue,
     32                                           ErrorResult& aRv) override;
     33 
     34  MOZ_CAN_RUN_SCRIPT void RejectedCallback(JSContext* aCx,
     35                                           JS::Handle<JS::Value> aValue,
     36                                           ErrorResult& aRv) override;
     37 
     38 protected:
     39  virtual ~PromiseNativeThenHandlerBase() = default;
     40 
     41  MOZ_CAN_RUN_SCRIPT virtual already_AddRefed<Promise> CallResolveCallback(
     42      JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv) = 0;
     43  MOZ_CAN_RUN_SCRIPT virtual already_AddRefed<Promise> CallRejectCallback(
     44      JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv) = 0;
     45 
     46  virtual void Traverse(nsCycleCollectionTraversalCallback&) = 0;
     47  virtual void Unlink() = 0;
     48  virtual void Trace(const TraceCallbacks& aCallbacks, void* aClosure) = 0;
     49 
     50  RefPtr<Promise> mPromise;
     51 };
     52 
     53 namespace {
     54 
     55 template <typename T, bool = IsRefcounted<std::remove_pointer_t<T>>::value,
     56          bool = (std::is_convertible_v<T, nsISupports*> ||
     57                  std::is_convertible_v<T*, nsISupports*>)>
     58 struct StorageTypeHelper {
     59  using Type = T;
     60 };
     61 
     62 template <typename T>
     63 struct StorageTypeHelper<T, true, true> {
     64  using Type = nsCOMPtr<T>;
     65 };
     66 
     67 template <typename T>
     68 struct StorageTypeHelper<nsCOMPtr<T>, true, true> {
     69  using Type = nsCOMPtr<T>;
     70 };
     71 
     72 template <typename T>
     73 struct StorageTypeHelper<T*, true, false> {
     74  using Type = RefPtr<T>;
     75 };
     76 
     77 template <typename T>
     78 struct StorageTypeHelper<JS::Handle<T>, false, false> {
     79  using Type = JS::Heap<T>;
     80 };
     81 
     82 template <template <typename> class SmartPtr, typename T>
     83 struct StorageTypeHelper<SmartPtr<T>, true, false>
     84    : std::enable_if<std::is_convertible_v<SmartPtr<T>, T*>, RefPtr<T>> {
     85  using Type = typename StorageTypeHelper::enable_if::type;
     86 };
     87 
     88 template <typename T>
     89 using StorageType = typename StorageTypeHelper<std::decay_t<T>>::Type;
     90 
     91 // Helpers to choose the correct argument type based on the storage type. Smart
     92 // pointers are converted to the corresponding raw pointer type. Everything else
     93 // is passed by move reference.
     94 //
     95 // Note: We can't just use std::forward for this because the input type may be a
     96 // raw pointer which does not match the argument type, and while the
     97 // spec-compliant behavior there should still give us the expected results, MSVC
     98 // considers it an illegal use of std::forward.
     99 template <template <typename> class SmartPtr, typename T>
    100 decltype(std::declval<SmartPtr<T>>().get()) ArgType(SmartPtr<T>& aVal) {
    101  return aVal.get();
    102 }
    103 
    104 template <typename T>
    105 T&& ArgType(T& aVal) {
    106  return std::move(aVal);
    107 }
    108 
    109 using ::ImplCycleCollectionUnlink;
    110 
    111 template <typename ResolveCallback, typename RejectCallback, typename ArgsTuple,
    112          typename JSArgsTuple>
    113 class NativeThenHandler;
    114 
    115 template <typename ResolveCallback, typename RejectCallback, typename... Args,
    116          typename... JSArgs>
    117 class NativeThenHandler<ResolveCallback, RejectCallback, std::tuple<Args...>,
    118                        std::tuple<JSArgs...>>
    119    final : public PromiseNativeThenHandlerBase {
    120 public:
    121  /**
    122   * @param aPromise A promise that will be settled by the result of the
    123   * callbacks. Any thrown value to ErrorResult passed to those callbacks will
    124   * be used to reject the promise, otherwise the promise will be resolved with
    125   * the return value.
    126   * @param aOnResolve A resolve callback
    127   * @param aOnReject A reject callback
    128   * @param aArgs The custom arguments to be passed to the both callbacks. The
    129   * handler class will grab them to make them live long enough and to allow
    130   * cycle collection.
    131   * @param aJSArgs The JS arguments to be passed to the both callbacks, after
    132   * native arguments. The handler will also grab them and allow garbage
    133   * collection.
    134   *
    135   * XXX(krosylight): ideally there should be two signatures, with or without a
    136   * promise parameter. Unfortunately doing so confuses the compiler and errors
    137   * out, because nothing prevents promise from being ResolveCallback.
    138   */
    139  NativeThenHandler(Promise* aPromise, Maybe<ResolveCallback>&& aOnResolve,
    140                    Maybe<RejectCallback>&& aOnReject,
    141                    std::tuple<std::remove_reference_t<Args>...>&& aArgs,
    142                    std::tuple<std::remove_reference_t<JSArgs>...>&& aJSArgs)
    143      : PromiseNativeThenHandlerBase(aPromise),
    144        mOnResolve(std::forward<Maybe<ResolveCallback>>(aOnResolve)),
    145        mOnReject(std::forward<Maybe<RejectCallback>>(aOnReject)),
    146        mArgs(std::forward<decltype(aArgs)>(aArgs)),
    147        mJSArgs(std::forward<decltype(aJSArgs)>(aJSArgs)) {
    148    if constexpr (std::tuple_size<decltype(mJSArgs)>::value > 0) {
    149      mozilla::HoldJSObjects(this);
    150    }
    151  }
    152 
    153 protected:
    154  ~NativeThenHandler() override {
    155    if constexpr (std::tuple_size<decltype(mJSArgs)>::value > 0) {
    156      mozilla::DropJSObjects(this);
    157    }
    158  }
    159 
    160  void Traverse(nsCycleCollectionTraversalCallback& cb) override {
    161    auto* tmp = this;
    162    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArgs)
    163  }
    164 
    165  void Unlink() override {
    166    auto* tmp = this;
    167    NS_IMPL_CYCLE_COLLECTION_UNLINK(mArgs)
    168    NS_IMPL_CYCLE_COLLECTION_UNLINK(mJSArgs)
    169  }
    170 
    171  void Trace(const TraceCallbacks& aCallbacks, void* aClosure) override {
    172    std::apply(
    173        [&aCallbacks, aClosure](auto&&... aArgs) {
    174          (aCallbacks.Trace(&aArgs, "mJSArgs[]", aClosure), ...);
    175        },
    176        mJSArgs);
    177  }
    178 
    179  bool HasResolvedCallback() override { return mOnResolve.isSome(); }
    180  bool HasRejectedCallback() override { return mOnReject.isSome(); }
    181 
    182  MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> CallResolveCallback(
    183      JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv) override {
    184    return CallCallback(aCx, *mOnResolve, aValue, aRv);
    185  }
    186  MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> CallRejectCallback(
    187      JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv) override {
    188    return CallCallback(aCx, *mOnReject, aValue, aRv);
    189  }
    190 
    191  // mJSArgs are marked with Trace() above, so they can be safely converted to
    192  // Handles. But we should not circumvent the read barrier, so call
    193  // exposeToActiveJS explicitly.
    194  template <typename T>
    195  static JS::Handle<T> GetJSArgHandleForCall(JS::Heap<T>& aArg) {
    196    aArg.exposeToActiveJS();
    197    return JS::Handle<T>::fromMarkedLocation(aArg.unsafeAddress());
    198  }
    199 
    200  template <typename TCallback, size_t... Indices, size_t... JSIndices>
    201  MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> CallCallback(
    202      JSContext* aCx, const TCallback& aHandler, JS::Handle<JS::Value> aValue,
    203      ErrorResult& aRv, std::index_sequence<Indices...>,
    204      std::index_sequence<JSIndices...>) {
    205    return aHandler(aCx, aValue, aRv, ArgType(std::get<Indices>(mArgs))...,
    206                    GetJSArgHandleForCall(std::get<JSIndices>(mJSArgs))...);
    207  }
    208 
    209  template <typename TCallback>
    210  MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> CallCallback(
    211      JSContext* aCx, const TCallback& aHandler, JS::Handle<JS::Value> aValue,
    212      ErrorResult& aRv) {
    213    return CallCallback(aCx, aHandler, aValue, aRv,
    214                        std::index_sequence_for<Args...>{},
    215                        std::index_sequence_for<JSArgs...>{});
    216  }
    217 
    218  Maybe<ResolveCallback> mOnResolve;
    219  Maybe<RejectCallback> mOnReject;
    220 
    221  std::tuple<StorageType<Args>...> mArgs;
    222  std::tuple<StorageType<JSArgs>...> mJSArgs;
    223 };
    224 
    225 }  // anonymous namespace
    226 
    227 template <typename ResolveCallback, typename RejectCallback, typename... Args,
    228          typename... JSArgs>
    229 Result<RefPtr<Promise>, nsresult>
    230 Promise::ThenCatchWithCycleCollectedArgsJSImpl(
    231    Maybe<ResolveCallback>&& aOnResolve, Maybe<RejectCallback>&& aOnReject,
    232    std::tuple<Args...>&& aArgs, std::tuple<JSArgs...>&& aJSArgs) {
    233  using HandlerType =
    234      NativeThenHandler<ResolveCallback, RejectCallback, std::tuple<Args...>,
    235                        std::tuple<JSArgs...>>;
    236 
    237  ErrorResult rv;
    238  RefPtr<Promise> promise = Promise::Create(GetParentObject(), rv);
    239  if (rv.Failed()) {
    240    return Err(rv.StealNSResult());
    241  }
    242 
    243  auto* handler = new (fallible)
    244      HandlerType(promise, std::forward<Maybe<ResolveCallback>>(aOnResolve),
    245                  std::forward<Maybe<RejectCallback>>(aOnReject),
    246                  std::forward<std::tuple<Args...>>(aArgs),
    247                  std::forward<std::tuple<JSArgs...>>(aJSArgs));
    248 
    249  if (!handler) {
    250    return Err(NS_ERROR_OUT_OF_MEMORY);
    251  }
    252 
    253  AppendNativeHandler(handler);
    254  return std::move(promise);
    255 }
    256 
    257 template <typename ResolveCallback, typename RejectCallback, typename... Args>
    258 Promise::ThenResult<ResolveCallback, Args...>
    259 Promise::ThenCatchWithCycleCollectedArgsImpl(
    260    Maybe<ResolveCallback>&& aOnResolve, Maybe<RejectCallback>&& aOnReject,
    261    Args&&... aArgs) {
    262  return ThenCatchWithCycleCollectedArgsJSImpl(
    263      std::forward<Maybe<ResolveCallback>>(aOnResolve),
    264      std::forward<Maybe<RejectCallback>>(aOnReject),
    265      std::make_tuple(std::forward<Args>(aArgs)...), std::make_tuple());
    266 }
    267 
    268 template <typename ResolveCallback, typename RejectCallback, typename... Args>
    269 Promise::ThenResult<ResolveCallback, Args...>
    270 Promise::ThenCatchWithCycleCollectedArgs(ResolveCallback&& aOnResolve,
    271                                         RejectCallback&& aOnReject,
    272                                         Args&&... aArgs) {
    273  return ThenCatchWithCycleCollectedArgsImpl(Some(aOnResolve), Some(aOnReject),
    274                                             std::forward<Args>(aArgs)...);
    275 }
    276 
    277 template <typename Callback, typename... Args>
    278 Promise::ThenResult<Callback, Args...> Promise::ThenWithCycleCollectedArgs(
    279    Callback&& aOnResolve, Args&&... aArgs) {
    280  return ThenCatchWithCycleCollectedArgsImpl(Some(aOnResolve),
    281                                             Maybe<Callback>(Nothing()),
    282                                             std::forward<Args>(aArgs)...);
    283 }
    284 
    285 template <typename Callback, typename... Args>
    286 Promise::ThenResult<Callback, Args...> Promise::CatchWithCycleCollectedArgs(
    287    Callback&& aOnReject, Args&&... aArgs) {
    288  return ThenCatchWithCycleCollectedArgsImpl(Maybe<Callback>(Nothing()),
    289                                             Some(aOnReject),
    290                                             std::forward<Args>(aArgs)...);
    291 }
    292 
    293 template <typename ResolveCallback, typename RejectCallback, typename ArgsTuple,
    294          typename JSArgsTuple>
    295 Result<RefPtr<Promise>, nsresult> Promise::ThenCatchWithCycleCollectedArgsJS(
    296    ResolveCallback&& aOnResolve, RejectCallback&& aOnReject, ArgsTuple&& aArgs,
    297    JSArgsTuple&& aJSArgs) {
    298  return ThenCatchWithCycleCollectedArgsJSImpl(
    299      Some(aOnResolve), Some(aOnReject), std::forward<ArgsTuple>(aArgs),
    300      std::forward<JSArgsTuple>(aJSArgs));
    301 }
    302 
    303 template <typename Callback, typename ArgsTuple, typename JSArgsTuple>
    304 Result<RefPtr<Promise>, nsresult> Promise::ThenWithCycleCollectedArgsJS(
    305    Callback&& aOnResolve, ArgsTuple&& aArgs, JSArgsTuple&& aJSArgs) {
    306  return ThenCatchWithCycleCollectedArgsJSImpl(
    307      Some(aOnResolve), Maybe<Callback>(Nothing()),
    308      std::forward<ArgsTuple>(aArgs), std::forward<JSArgsTuple>(aJSArgs));
    309 }
    310 
    311 template <typename ResolveCallback, typename RejectCallback, typename... Args>
    312 void Promise::AddCallbacksWithCycleCollectedArgs(ResolveCallback&& aOnResolve,
    313                                                 RejectCallback&& aOnReject,
    314                                                 Args&&... aArgs) {
    315  auto onResolve =
    316      [aOnResolve](JSContext* aCx, JS::Handle<JS::Value> value,
    317                   ErrorResult& aRv,
    318                   StorageType<Args>&&... aArgs) -> already_AddRefed<Promise> {
    319    aOnResolve(aCx, value, aRv, aArgs...);
    320    return nullptr;
    321  };
    322  auto onReject =
    323      [aOnReject](JSContext* aCx, JS::Handle<JS::Value> value, ErrorResult& aRv,
    324                  StorageType<Args>&&... aArgs) -> already_AddRefed<Promise> {
    325    aOnReject(aCx, value, aRv, aArgs...);
    326    return nullptr;
    327  };
    328 
    329  // Note: explicit template parameters for clang<7/gcc<8 without "Template
    330  // argument deduction for class templates" support
    331  AppendNativeHandler(
    332      new NativeThenHandler<decltype(onResolve), decltype(onReject),
    333                            std::tuple<Args...>, std::tuple<>>(
    334          nullptr, Some(onResolve), Some(onReject),
    335          std::make_tuple(std::forward<Args>(aArgs)...), std::make_tuple()));
    336 }
    337 
    338 }  // namespace mozilla::dom
    339 
    340 #endif  // mozilla_dom_Promise_inl_h