promise_util.h 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  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 SHELL_COMMON_PROMISE_UTIL_H_
  5. #define SHELL_COMMON_PROMISE_UTIL_H_
  6. #include <string>
  7. #include <tuple>
  8. #include <type_traits>
  9. #include <utility>
  10. #include "base/strings/string_piece.h"
  11. #include "base/task/post_task.h"
  12. #include "content/public/browser/browser_task_traits.h"
  13. #include "content/public/browser/browser_thread.h"
  14. #include "native_mate/converter.h"
  15. #include "native_mate/microtasks_scope.h"
  16. #include "shell/common/gin_converters/std_converter.h"
  17. namespace electron {
  18. namespace util {
  19. // A wrapper around the v8::Promise.
  20. //
  21. // This is a move-only type that should always be `std::move`d when passed to
  22. // callbacks, and it should be destroyed on the same thread of creation.
  23. template <typename RT>
  24. class Promise {
  25. public:
  26. // Create a new promise.
  27. explicit Promise(v8::Isolate* isolate)
  28. : Promise(isolate,
  29. v8::Promise::Resolver::New(isolate->GetCurrentContext())
  30. .ToLocalChecked()) {}
  31. // Wrap an existing v8 promise.
  32. Promise(v8::Isolate* isolate, v8::Local<v8::Promise::Resolver> handle)
  33. : isolate_(isolate),
  34. context_(isolate, isolate->GetCurrentContext()),
  35. resolver_(isolate, handle) {}
  36. ~Promise() = default;
  37. // Support moving.
  38. Promise(Promise&&) = default;
  39. Promise& operator=(Promise&&) = default;
  40. v8::Isolate* isolate() const { return isolate_; }
  41. v8::Local<v8::Context> GetContext() {
  42. return v8::Local<v8::Context>::New(isolate_, context_);
  43. }
  44. // helpers for promise resolution and rejection
  45. static void ResolvePromise(Promise<RT> promise, RT result) {
  46. if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
  47. base::PostTask(
  48. FROM_HERE, {content::BrowserThread::UI},
  49. base::BindOnce([](Promise<RT> promise,
  50. RT result) { promise.ResolveWithGin(result); },
  51. std::move(promise), std::move(result)));
  52. } else {
  53. promise.ResolveWithGin(result);
  54. }
  55. }
  56. static void ResolveEmptyPromise(Promise<RT> promise) {
  57. if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
  58. base::PostTask(
  59. FROM_HERE, {content::BrowserThread::UI},
  60. base::BindOnce([](Promise<RT> promise) { promise.Resolve(); },
  61. std::move(promise)));
  62. } else {
  63. promise.Resolve();
  64. }
  65. }
  66. static void RejectPromise(Promise<RT> promise, base::StringPiece errmsg) {
  67. if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
  68. base::PostTask(FROM_HERE, {content::BrowserThread::UI},
  69. base::BindOnce(
  70. [](Promise<RT> promise, base::StringPiece err) {
  71. promise.RejectWithErrorMessage(err);
  72. },
  73. std::move(promise), std::move(errmsg)));
  74. } else {
  75. promise.RejectWithErrorMessage(errmsg);
  76. }
  77. }
  78. // Returns an already-resolved promise.
  79. static v8::Local<v8::Promise> ResolvedPromise(v8::Isolate* isolate,
  80. RT result) {
  81. Promise<RT> resolved(isolate);
  82. resolved.Resolve(result);
  83. return resolved.GetHandle();
  84. }
  85. static v8::Local<v8::Promise> ResolvedPromise(v8::Isolate* isolate) {
  86. Promise<void*> resolved(isolate);
  87. resolved.Resolve();
  88. return resolved.GetHandle();
  89. }
  90. v8::Local<v8::Promise> GetHandle() const { return GetInner()->GetPromise(); }
  91. v8::Maybe<bool> Resolve() {
  92. static_assert(std::is_same<void*, RT>(),
  93. "Can only resolve void* promises with no value");
  94. v8::HandleScope handle_scope(isolate());
  95. mate::MicrotasksScope microtasks_scope(isolate());
  96. v8::Context::Scope context_scope(
  97. v8::Local<v8::Context>::New(isolate(), GetContext()));
  98. return GetInner()->Resolve(GetContext(), v8::Undefined(isolate()));
  99. }
  100. v8::Maybe<bool> Reject() {
  101. v8::HandleScope handle_scope(isolate());
  102. mate::MicrotasksScope microtasks_scope(isolate());
  103. v8::Context::Scope context_scope(
  104. v8::Local<v8::Context>::New(isolate(), GetContext()));
  105. return GetInner()->Reject(GetContext(), v8::Undefined(isolate()));
  106. }
  107. v8::Maybe<bool> Reject(v8::Local<v8::Value> exception) {
  108. v8::HandleScope handle_scope(isolate());
  109. mate::MicrotasksScope microtasks_scope(isolate());
  110. v8::Context::Scope context_scope(
  111. v8::Local<v8::Context>::New(isolate(), GetContext()));
  112. return GetInner()->Reject(GetContext(), exception);
  113. }
  114. template <typename... ResolveType>
  115. v8::MaybeLocal<v8::Promise> Then(
  116. base::OnceCallback<void(ResolveType...)> cb) {
  117. static_assert(sizeof...(ResolveType) <= 1,
  118. "A promise's 'Then' callback should only receive at most one "
  119. "parameter");
  120. static_assert(
  121. std::is_same<RT, std::tuple_element_t<0, std::tuple<ResolveType...>>>(),
  122. "A promises's 'Then' callback must handle the same type as the "
  123. "promises resolve type");
  124. v8::HandleScope handle_scope(isolate());
  125. v8::Context::Scope context_scope(
  126. v8::Local<v8::Context>::New(isolate(), GetContext()));
  127. v8::Local<v8::Value> value = gin::ConvertToV8(isolate(), std::move(cb));
  128. v8::Local<v8::Function> handler = v8::Local<v8::Function>::Cast(value);
  129. return GetHandle()->Then(GetContext(), handler);
  130. }
  131. // Promise resolution is a microtask
  132. // We use the MicrotasksRunner to trigger the running of pending microtasks
  133. v8::Maybe<bool> Resolve(const RT& value) {
  134. static_assert(!std::is_same<void*, RT>(),
  135. "void* promises can not be resolved with a value");
  136. v8::HandleScope handle_scope(isolate());
  137. mate::MicrotasksScope microtasks_scope(isolate());
  138. v8::Context::Scope context_scope(
  139. v8::Local<v8::Context>::New(isolate(), GetContext()));
  140. return GetInner()->Resolve(GetContext(),
  141. mate::ConvertToV8(isolate(), value));
  142. }
  143. v8::Maybe<bool> ResolveWithGin(const RT& value) {
  144. static_assert(!std::is_same<void*, RT>(),
  145. "void* promises can not be resolved with a value");
  146. v8::HandleScope handle_scope(isolate());
  147. mate::MicrotasksScope microtasks_scope(isolate());
  148. v8::Context::Scope context_scope(
  149. v8::Local<v8::Context>::New(isolate(), GetContext()));
  150. return GetInner()->Resolve(GetContext(),
  151. gin::ConvertToV8(isolate(), value));
  152. }
  153. v8::Maybe<bool> RejectWithErrorMessage(base::StringPiece string) {
  154. v8::HandleScope handle_scope(isolate());
  155. mate::MicrotasksScope microtasks_scope(isolate());
  156. v8::Context::Scope context_scope(
  157. v8::Local<v8::Context>::New(isolate(), GetContext()));
  158. v8::Local<v8::Value> error =
  159. v8::Exception::Error(gin::StringToV8(isolate(), string));
  160. return GetInner()->Reject(GetContext(), (error));
  161. }
  162. private:
  163. v8::Local<v8::Promise::Resolver> GetInner() const {
  164. return resolver_.Get(isolate());
  165. }
  166. v8::Isolate* isolate_;
  167. v8::Global<v8::Context> context_;
  168. v8::Global<v8::Promise::Resolver> resolver_;
  169. DISALLOW_COPY_AND_ASSIGN(Promise);
  170. };
  171. } // namespace util
  172. } // namespace electron
  173. namespace mate {
  174. template <typename T>
  175. struct Converter<electron::util::Promise<T>> {
  176. static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
  177. const electron::util::Promise<T>& val);
  178. // TODO(MarshallOfSound): Implement FromV8 to allow promise chaining
  179. // in native land
  180. // static bool FromV8(v8::Isolate* isolate,
  181. // v8::Local<v8::Value> val,
  182. // Promise* out);
  183. };
  184. } // namespace mate
  185. #endif // SHELL_COMMON_PROMISE_UTIL_H_