promise.h 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  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 <tuple>
  8. #include <type_traits>
  9. #include <utility>
  10. #include "base/memory/raw_ptr.h"
  11. #include "base/strings/string_piece.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, base::StringPiece errmsg) {
  43. if (electron::IsBrowserProcess() &&
  44. !content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
  45. content::GetUIThreadTaskRunner({})->PostTask(
  46. FROM_HERE,
  47. base::BindOnce(
  48. // Note that this callback can not take StringPiece,
  49. // as StringPiece only references string internally and
  50. // will blow when a temporary string is passed.
  51. [](PromiseBase&& promise, std::string str) {
  52. promise.RejectWithErrorMessage(str);
  53. },
  54. std::move(promise), std::string(errmsg.data(), errmsg.size())));
  55. } else {
  56. promise.RejectWithErrorMessage(errmsg);
  57. }
  58. }
  59. v8::Maybe<bool> Reject();
  60. v8::Maybe<bool> Reject(v8::Local<v8::Value> except);
  61. v8::Maybe<bool> RejectWithErrorMessage(base::StringPiece message);
  62. v8::Local<v8::Context> GetContext() const;
  63. v8::Local<v8::Promise> GetHandle() const;
  64. v8::Isolate* isolate() const { return isolate_; }
  65. protected:
  66. v8::Local<v8::Promise::Resolver> GetInner() const;
  67. private:
  68. raw_ptr<v8::Isolate> isolate_;
  69. v8::Global<v8::Context> context_;
  70. v8::Global<v8::Promise::Resolver> resolver_;
  71. };
  72. // Template implementation that returns values.
  73. template <typename RT>
  74. class Promise : public PromiseBase {
  75. public:
  76. using PromiseBase::PromiseBase;
  77. // Helper for resolving the promise with |result|.
  78. static void ResolvePromise(Promise<RT> promise, RT result) {
  79. if (electron::IsBrowserProcess() &&
  80. !content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
  81. content::GetUIThreadTaskRunner({})->PostTask(
  82. FROM_HERE, base::BindOnce([](Promise<RT> promise,
  83. RT result) { promise.Resolve(result); },
  84. std::move(promise), std::move(result)));
  85. } else {
  86. promise.Resolve(result);
  87. }
  88. }
  89. // Returns an already-resolved promise.
  90. static v8::Local<v8::Promise> ResolvedPromise(v8::Isolate* isolate,
  91. RT result) {
  92. Promise<RT> resolved(isolate);
  93. resolved.Resolve(result);
  94. return resolved.GetHandle();
  95. }
  96. // Convert to another type.
  97. template <typename NT>
  98. Promise<NT> As() {
  99. return Promise<NT>(isolate(), GetInner());
  100. }
  101. // Promise resolution is a microtask
  102. // We use the MicrotasksRunner to trigger the running of pending microtasks
  103. v8::Maybe<bool> Resolve(const RT& value) {
  104. gin_helper::Locker locker(isolate());
  105. v8::HandleScope handle_scope(isolate());
  106. gin_helper::MicrotasksScope microtasks_scope(
  107. isolate(), GetContext()->GetMicrotaskQueue());
  108. v8::Context::Scope context_scope(GetContext());
  109. return GetInner()->Resolve(GetContext(),
  110. gin::ConvertToV8(isolate(), value));
  111. }
  112. template <typename... ResolveType>
  113. v8::MaybeLocal<v8::Promise> Then(
  114. base::OnceCallback<void(ResolveType...)> cb) {
  115. static_assert(sizeof...(ResolveType) <= 1,
  116. "A promise's 'Then' callback should only receive at most one "
  117. "parameter");
  118. static_assert(
  119. std::is_same<RT, std::tuple_element_t<0, std::tuple<ResolveType...>>>(),
  120. "A promises's 'Then' callback must handle the same type as the "
  121. "promises resolve type");
  122. gin_helper::Locker locker(isolate());
  123. v8::HandleScope handle_scope(isolate());
  124. v8::Context::Scope context_scope(GetContext());
  125. v8::Local<v8::Value> value = gin::ConvertToV8(isolate(), std::move(cb));
  126. v8::Local<v8::Function> handler = value.As<v8::Function>();
  127. return GetHandle()->Then(GetContext(), handler);
  128. }
  129. };
  130. // Template implementation that returns nothing.
  131. template <>
  132. class Promise<void> : public PromiseBase {
  133. public:
  134. using PromiseBase::PromiseBase;
  135. // Helper for resolving the empty promise.
  136. static void ResolvePromise(Promise<void> promise);
  137. // Returns an already-resolved promise.
  138. static v8::Local<v8::Promise> ResolvedPromise(v8::Isolate* isolate);
  139. v8::Maybe<bool> Resolve();
  140. };
  141. } // namespace gin_helper
  142. namespace gin {
  143. template <typename T>
  144. struct Converter<gin_helper::Promise<T>> {
  145. static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
  146. const gin_helper::Promise<T>& val) {
  147. return val.GetHandle();
  148. }
  149. // TODO(MarshallOfSound): Implement FromV8 to allow promise chaining
  150. // in native land
  151. // static bool FromV8(v8::Isolate* isolate,
  152. // v8::Local<v8::Value> val,
  153. // Promise* out);
  154. };
  155. } // namespace gin
  156. #endif // ELECTRON_SHELL_COMMON_GIN_HELPER_PROMISE_H_