promise.h 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. // Copyright (c) 2018 GitHub, Inc.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #ifndef ELECTRON_SHELL_COMMON_GIN_HELPER_PROMISE_H_
  5. #define ELECTRON_SHELL_COMMON_GIN_HELPER_PROMISE_H_
  6. #include <string>
  7. #include <string_view>
  8. #include <tuple>
  9. #include <type_traits>
  10. #include <utility>
  11. #include "base/memory/raw_ptr.h"
  12. #include "content/public/browser/browser_task_traits.h"
  13. #include "content/public/browser/browser_thread.h"
  14. #include "shell/common/gin_converters/std_converter.h"
  15. #include "shell/common/gin_helper/locker.h"
  16. #include "shell/common/gin_helper/microtasks_scope.h"
  17. #include "shell/common/process_util.h"
  18. namespace gin_helper {
  19. // A wrapper around the v8::Promise.
  20. //
  21. // This is the non-template base class to share code between templates
  22. // instances.
  23. //
  24. // This is a move-only type that should always be `std::move`d when passed to
  25. // callbacks, and it should be destroyed on the same thread of creation.
  26. class PromiseBase {
  27. public:
  28. explicit PromiseBase(v8::Isolate* isolate);
  29. PromiseBase(v8::Isolate* isolate, v8::Local<v8::Promise::Resolver> handle);
  30. PromiseBase();
  31. ~PromiseBase();
  32. // disable copy
  33. PromiseBase(const PromiseBase&) = delete;
  34. PromiseBase& operator=(const PromiseBase&) = delete;
  35. // Support moving.
  36. PromiseBase(PromiseBase&&);
  37. PromiseBase& operator=(PromiseBase&&);
  38. // Helper for rejecting promise with error message.
  39. //
  40. // Note: The parameter type is PromiseBase&& so it can take the instances of
  41. // Promise<T> type.
  42. static void RejectPromise(PromiseBase&& promise,
  43. const std::string_view errmsg) {
  44. if (electron::IsBrowserProcess() &&
  45. !content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
  46. content::GetUIThreadTaskRunner({})->PostTask(
  47. FROM_HERE,
  48. base::BindOnce(
  49. // Note that this callback can not take std::string_view,
  50. // as StringPiece only references string internally and
  51. // will blow when a temporary string is passed.
  52. [](PromiseBase&& promise, std::string str) {
  53. promise.RejectWithErrorMessage(str);
  54. },
  55. std::move(promise), std::string{errmsg}));
  56. } else {
  57. promise.RejectWithErrorMessage(errmsg);
  58. }
  59. }
  60. v8::Maybe<bool> Reject();
  61. v8::Maybe<bool> Reject(v8::Local<v8::Value> except);
  62. v8::Maybe<bool> RejectWithErrorMessage(std::string_view message);
  63. v8::Local<v8::Context> GetContext() const;
  64. v8::Local<v8::Promise> GetHandle() const;
  65. v8::Isolate* isolate() const { return isolate_; }
  66. protected:
  67. v8::Local<v8::Promise::Resolver> GetInner() const;
  68. private:
  69. raw_ptr<v8::Isolate> isolate_;
  70. v8::Global<v8::Context> context_;
  71. v8::Global<v8::Promise::Resolver> resolver_;
  72. };
  73. // Template implementation that returns values.
  74. template <typename RT>
  75. class Promise : public PromiseBase {
  76. public:
  77. using PromiseBase::PromiseBase;
  78. // Helper for resolving the promise with |result|.
  79. static void ResolvePromise(Promise<RT> promise, RT result) {
  80. if (electron::IsBrowserProcess() &&
  81. !content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
  82. content::GetUIThreadTaskRunner({})->PostTask(
  83. FROM_HERE, base::BindOnce([](Promise<RT> promise,
  84. RT result) { promise.Resolve(result); },
  85. std::move(promise), std::move(result)));
  86. } else {
  87. promise.Resolve(result);
  88. }
  89. }
  90. // Returns an already-resolved promise.
  91. static v8::Local<v8::Promise> ResolvedPromise(v8::Isolate* isolate,
  92. RT result) {
  93. Promise<RT> resolved(isolate);
  94. resolved.Resolve(result);
  95. return resolved.GetHandle();
  96. }
  97. // Convert to another type.
  98. template <typename NT>
  99. Promise<NT> As() {
  100. return Promise<NT>(isolate(), GetInner());
  101. }
  102. // Promise resolution is a microtask
  103. // We use the MicrotasksRunner to trigger the running of pending microtasks
  104. v8::Maybe<bool> Resolve(const RT& value) {
  105. gin_helper::Locker locker(isolate());
  106. v8::HandleScope handle_scope(isolate());
  107. gin_helper::MicrotasksScope microtasks_scope(
  108. isolate(), GetContext()->GetMicrotaskQueue());
  109. v8::Context::Scope context_scope(GetContext());
  110. return GetInner()->Resolve(GetContext(),
  111. gin::ConvertToV8(isolate(), value));
  112. }
  113. template <typename... ResolveType>
  114. v8::MaybeLocal<v8::Promise> Then(
  115. base::OnceCallback<void(ResolveType...)> cb) {
  116. static_assert(sizeof...(ResolveType) <= 1,
  117. "A promise's 'Then' callback should only receive at most one "
  118. "parameter");
  119. static_assert(
  120. std::is_same<RT, std::tuple_element_t<0, std::tuple<ResolveType...>>>(),
  121. "A promises's 'Then' callback must handle the same type as the "
  122. "promises resolve type");
  123. gin_helper::Locker locker(isolate());
  124. v8::HandleScope handle_scope(isolate());
  125. v8::Context::Scope context_scope(GetContext());
  126. v8::Local<v8::Value> value = gin::ConvertToV8(isolate(), std::move(cb));
  127. v8::Local<v8::Function> handler = value.As<v8::Function>();
  128. return GetHandle()->Then(GetContext(), handler);
  129. }
  130. };
  131. // Template implementation that returns nothing.
  132. template <>
  133. class Promise<void> : public PromiseBase {
  134. public:
  135. using PromiseBase::PromiseBase;
  136. // Helper for resolving the empty promise.
  137. static void ResolvePromise(Promise<void> promise);
  138. // Returns an already-resolved promise.
  139. static v8::Local<v8::Promise> ResolvedPromise(v8::Isolate* isolate);
  140. v8::Maybe<bool> Resolve();
  141. };
  142. } // namespace gin_helper
  143. namespace gin {
  144. template <typename T>
  145. struct Converter<gin_helper::Promise<T>> {
  146. static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
  147. const gin_helper::Promise<T>& val) {
  148. return val.GetHandle();
  149. }
  150. // TODO(MarshallOfSound): Implement FromV8 to allow promise chaining
  151. // in native land
  152. // static bool FromV8(v8::Isolate* isolate,
  153. // v8::Local<v8::Value> val,
  154. // Promise* out);
  155. };
  156. } // namespace gin
  157. #endif // ELECTRON_SHELL_COMMON_GIN_HELPER_PROMISE_H_