summaryrefslogtreecommitdiffstats
path: root/dom/promise/Promise-inl.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/promise/Promise-inl.h167
1 files changed, 167 insertions, 0 deletions
diff --git a/dom/promise/Promise-inl.h b/dom/promise/Promise-inl.h
new file mode 100644
index 0000000000..4ff32585ba
--- /dev/null
+++ b/dom/promise/Promise-inl.h
@@ -0,0 +1,167 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_dom_Promise_inl_h
+#define mozilla_dom_Promise_inl_h
+
+#include <type_traits>
+#include <utility>
+
+#include "mozilla/TupleCycleCollection.h"
+#include "mozilla/ResultExtensions.h"
+#include "mozilla/dom/PromiseNativeHandler.h"
+
+namespace mozilla {
+namespace dom {
+
+class PromiseNativeThenHandlerBase : public PromiseNativeHandler {
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_CLASS(PromiseNativeThenHandlerBase)
+
+ PromiseNativeThenHandlerBase(Promise& aPromise) : mPromise(&aPromise) {}
+
+ void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
+
+ void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
+
+ protected:
+ virtual ~PromiseNativeThenHandlerBase() = default;
+
+ virtual already_AddRefed<Promise> CallResolveCallback(
+ JSContext* aCx, JS::Handle<JS::Value> aValue) = 0;
+
+ virtual void Traverse(nsCycleCollectionTraversalCallback&) = 0;
+ virtual void Unlink() = 0;
+
+ RefPtr<Promise> mPromise;
+};
+
+namespace {
+
+template <typename T, bool = IsRefcounted<std::remove_pointer_t<T>>::value,
+ bool = (std::is_convertible_v<T, nsISupports*> ||
+ std::is_convertible_v<T*, nsISupports*>)>
+struct StorageTypeHelper {
+ using Type = T;
+};
+
+template <typename T>
+struct StorageTypeHelper<T, true, true> {
+ using Type = nsCOMPtr<T>;
+};
+
+template <typename T>
+struct StorageTypeHelper<nsCOMPtr<T>, true, true> {
+ using Type = nsCOMPtr<T>;
+};
+
+template <typename T>
+struct StorageTypeHelper<T*, true, false> {
+ using Type = RefPtr<T>;
+};
+
+template <template <typename> class SmartPtr, typename T>
+struct StorageTypeHelper<SmartPtr<T>, true, false>
+ : std::enable_if<std::is_convertible_v<SmartPtr<T>, T*>, RefPtr<T>> {
+ using Type = typename StorageTypeHelper::enable_if::type;
+};
+
+template <typename T>
+using StorageType = typename StorageTypeHelper<std::decay_t<T>>::Type;
+
+// Helpers to choose the correct argument type based on the storage type. Smart
+// pointers are converted to the corresponding raw pointer type. Everything else
+// is passed by move reference.
+//
+// Note: We can't just use std::forward for this because the input type may be a
+// raw pointer which does not match the argument type, and while the
+// spec-compliant behavior there should still give us the expected results, MSVC
+// considers it an illegal use of std::forward.
+template <template <typename> class SmartPtr, typename T>
+decltype(std::declval<SmartPtr<T>>().get()) ArgType(SmartPtr<T>& aVal) {
+ return aVal.get();
+}
+
+template <typename T>
+T&& ArgType(T& aVal) {
+ return std::move(aVal);
+}
+
+using ::ImplCycleCollectionUnlink;
+
+template <typename Callback, typename... Args>
+class NativeThenHandler final : public PromiseNativeThenHandlerBase {
+ public:
+ NativeThenHandler(Promise& aPromise, Callback&& aOnResolve, Args&&... aArgs)
+ : PromiseNativeThenHandlerBase(aPromise),
+ mOnResolve(std::forward<Callback>(aOnResolve)),
+ mArgs(std::forward<Args>(aArgs)...) {}
+
+ protected:
+ void Traverse(nsCycleCollectionTraversalCallback& cb) override {
+ auto* tmp = this;
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArgs)
+ }
+
+ void Unlink() override {
+ auto* tmp = this;
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mArgs)
+ }
+
+ already_AddRefed<Promise> CallResolveCallback(
+ JSContext* aCx, JS::Handle<JS::Value> aValue) override {
+ return CallCallback(aCx, mOnResolve, aValue);
+ }
+
+ template <size_t... Indices>
+ already_AddRefed<Promise> CallCallback(JSContext* aCx,
+ const Callback& aHandler,
+ JS::Handle<JS::Value> aValue,
+ std::index_sequence<Indices...>) {
+ return mOnResolve(aCx, aValue, ArgType(Get<Indices>(mArgs))...);
+ }
+
+ already_AddRefed<Promise> CallCallback(JSContext* aCx,
+ const Callback& aHandler,
+ JS::Handle<JS::Value> aValue) {
+ return CallCallback(aCx, aHandler, aValue,
+ std::index_sequence_for<Args...>{});
+ }
+
+ Callback mOnResolve;
+
+ Tuple<StorageType<Args>...> mArgs;
+};
+
+} // anonymous namespace
+
+template <typename Callback, typename... Args>
+Promise::ThenResult<Callback, Args...> Promise::ThenWithCycleCollectedArgs(
+ Callback&& aOnResolve, Args&&... aArgs) {
+ using HandlerType = NativeThenHandler<Callback, Args...>;
+
+ ErrorResult rv;
+ RefPtr<Promise> promise = Promise::Create(GetParentObject(), rv);
+ if (rv.Failed()) {
+ return Err(rv.StealNSResult());
+ }
+
+ auto* handler =
+ new (fallible) HandlerType(*promise, std::forward<Callback>(aOnResolve),
+ std::forward<Args>(aArgs)...);
+
+ if (!handler) {
+ return Err(NS_ERROR_OUT_OF_MEMORY);
+ }
+
+ AppendNativeHandler(handler);
+ return std::move(promise);
+}
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_Promise_inl_h