// Copyright (c) 2018 GitHub, Inc. // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. #ifndef ELECTRON_SHELL_COMMON_GIN_HELPER_PROMISE_H_ #define ELECTRON_SHELL_COMMON_GIN_HELPER_PROMISE_H_ #include #include #include #include #include #include "base/memory/raw_ptr.h" #include "base/memory/scoped_refptr.h" #include "base/task/task_runner.h" #include "shell/common/gin_converters/std_converter.h" #include "shell/common/gin_helper/microtasks_scope.h" #include "v8/include/v8-context.h" namespace gin_helper { // A wrapper around the v8::Promise. // // This is the non-template base class to share code between templates // instances. // // This is a move-only type that should always be `std::move`d when passed to // callbacks, and it should be destroyed on the same thread of creation. class PromiseBase { public: explicit PromiseBase(v8::Isolate* isolate); PromiseBase(v8::Isolate* isolate, v8::Local handle); PromiseBase(); ~PromiseBase(); // disable copy PromiseBase(const PromiseBase&) = delete; PromiseBase& operator=(const PromiseBase&) = delete; // Support moving. PromiseBase(PromiseBase&&); PromiseBase& operator=(PromiseBase&&); // Helper for rejecting promise with error message. static void RejectPromise(PromiseBase&& promise, std::string_view errmsg); v8::Maybe Reject(); v8::Maybe Reject(v8::Local except); v8::Maybe RejectWithErrorMessage(std::string_view message); v8::Local GetContext() const; v8::Local GetHandle() const; v8::Isolate* isolate() const { return isolate_; } protected: struct SettleScope { explicit SettleScope(const PromiseBase& base); ~SettleScope(); v8::HandleScope handle_scope_; v8::Local context_; gin_helper::MicrotasksScope microtasks_scope_; v8::Context::Scope context_scope_; }; v8::Local GetInner() const; static scoped_refptr GetTaskRunner(); private: raw_ptr isolate_; v8::Global context_; v8::Global resolver_; }; // Template implementation that returns values. template class Promise : public PromiseBase { public: using PromiseBase::PromiseBase; // Helper for resolving the promise with |result|. static void ResolvePromise(Promise promise, RT result) { if (auto task_runner = GetTaskRunner()) { task_runner->PostTask( FROM_HERE, base::BindOnce([](Promise promise, RT result) { promise.Resolve(result); }, std::move(promise), std::move(result))); } else { promise.Resolve(result); } } // Returns an already-resolved promise. static v8::Local ResolvedPromise(v8::Isolate* isolate, RT result) { Promise resolved(isolate); resolved.Resolve(result); return resolved.GetHandle(); } // Convert to another type. template Promise As() { return Promise{isolate(), GetInner()}; } v8::Maybe Resolve(const RT& value) { SettleScope settle_scope{*this}; return GetInner()->Resolve(settle_scope.context_, gin::ConvertToV8(isolate(), value)); } template v8::MaybeLocal Then( base::OnceCallback cb) { static_assert(sizeof...(ResolveType) <= 1, "A promise's 'Then' callback should only receive at most one " "parameter"); static_assert( std::is_same>>(), "A promises's 'Then' callback must handle the same type as the " "promises resolve type"); SettleScope settle_scope{*this}; v8::Local value = gin::ConvertToV8(isolate(), std::move(cb)); v8::Local handler = value.As(); return GetHandle()->Then(settle_scope.context_, handler); } }; // Template implementation that returns nothing. template <> class Promise : public PromiseBase { public: using PromiseBase::PromiseBase; // Helper for resolving the empty promise. static void ResolvePromise(Promise promise); // Returns an already-resolved promise. static v8::Local ResolvedPromise(v8::Isolate* isolate); v8::Maybe Resolve(); }; } // namespace gin_helper namespace gin { template struct Converter> { static v8::Local ToV8(v8::Isolate* isolate, const gin_helper::Promise& val) { return val.GetHandle(); } // TODO(MarshallOfSound): Implement FromV8 to allow promise chaining // in native land // static bool FromV8(v8::Isolate* isolate, // v8::Local val, // Promise* out); }; } // namespace gin #endif // ELECTRON_SHELL_COMMON_GIN_HELPER_PROMISE_H_