promise_util.h 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  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 <utility>
  8. #include "base/task/post_task.h"
  9. #include "content/public/browser/browser_task_traits.h"
  10. #include "content/public/browser/browser_thread.h"
  11. #include "native_mate/converter.h"
  12. #include "shell/common/api/locker.h"
  13. #include "shell/common/native_mate_converters/callback.h"
  14. #include "shell/common/native_mate_converters/once_callback.h"
  15. namespace electron {
  16. namespace util {
  17. // A wrapper around the v8::Promise.
  18. //
  19. // This is a move-only type that should always be `std::move`d when passed to
  20. // callbacks, and it should be destroyed on the same thread of creation.
  21. class Promise {
  22. public:
  23. // Create a new promise.
  24. explicit Promise(v8::Isolate* isolate);
  25. // Wrap an existing v8 promise.
  26. Promise(v8::Isolate* isolate, v8::Local<v8::Promise::Resolver> handle);
  27. ~Promise();
  28. // Support moving.
  29. Promise(Promise&&);
  30. Promise& operator=(Promise&&);
  31. v8::Isolate* isolate() const { return isolate_; }
  32. v8::Local<v8::Context> GetContext() {
  33. return v8::Local<v8::Context>::New(isolate_, context_);
  34. }
  35. // helpers for promise resolution and rejection
  36. template <typename T>
  37. static void ResolvePromise(Promise promise, T result) {
  38. if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
  39. base::PostTaskWithTraits(
  40. FROM_HERE, {content::BrowserThread::UI},
  41. base::BindOnce(
  42. [](Promise promise, T result) { promise.Resolve(result); },
  43. std::move(promise), std::move(result)));
  44. } else {
  45. promise.Resolve(result);
  46. }
  47. }
  48. static void ResolveEmptyPromise(Promise promise) {
  49. if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
  50. base::PostTaskWithTraits(
  51. FROM_HERE, {content::BrowserThread::UI},
  52. base::BindOnce([](Promise promise) { promise.Resolve(); },
  53. std::move(promise)));
  54. } else {
  55. promise.Resolve();
  56. }
  57. }
  58. static void RejectPromise(Promise promise, std::string errmsg) {
  59. if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
  60. base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI},
  61. base::BindOnce(
  62. [](Promise promise, std::string errmsg) {
  63. promise.RejectWithErrorMessage(errmsg);
  64. },
  65. std::move(promise), std::move(errmsg)));
  66. } else {
  67. promise.RejectWithErrorMessage(errmsg);
  68. }
  69. }
  70. // Returns an already-resolved promise.
  71. template <typename T>
  72. static v8::Local<v8::Promise> ResolvedPromise(v8::Isolate* isolate,
  73. T result) {
  74. Promise resolved(isolate);
  75. resolved.Resolve(result);
  76. return resolved.GetHandle();
  77. }
  78. static v8::Local<v8::Promise> ResolvedPromise(v8::Isolate* isolate) {
  79. Promise resolved(isolate);
  80. resolved.Resolve();
  81. return resolved.GetHandle();
  82. }
  83. v8::Local<v8::Promise> GetHandle() const;
  84. v8::Maybe<bool> Resolve() {
  85. v8::HandleScope handle_scope(isolate());
  86. v8::MicrotasksScope script_scope(isolate(),
  87. v8::MicrotasksScope::kRunMicrotasks);
  88. v8::Context::Scope context_scope(
  89. v8::Local<v8::Context>::New(isolate(), GetContext()));
  90. return GetInner()->Resolve(GetContext(), v8::Undefined(isolate()));
  91. }
  92. v8::Maybe<bool> Reject() {
  93. v8::HandleScope handle_scope(isolate());
  94. v8::MicrotasksScope script_scope(isolate(),
  95. v8::MicrotasksScope::kRunMicrotasks);
  96. v8::Context::Scope context_scope(
  97. v8::Local<v8::Context>::New(isolate(), GetContext()));
  98. return GetInner()->Reject(GetContext(), v8::Undefined(isolate()));
  99. }
  100. v8::Maybe<bool> Reject(v8::Local<v8::Value> exception) {
  101. v8::HandleScope handle_scope(isolate());
  102. v8::MicrotasksScope script_scope(isolate(),
  103. v8::MicrotasksScope::kRunMicrotasks);
  104. v8::Context::Scope context_scope(
  105. v8::Local<v8::Context>::New(isolate(), GetContext()));
  106. return GetInner()->Reject(GetContext(), exception);
  107. }
  108. // Please note that using Then is effectively the same as calling .then
  109. // in javascript. This means (a) it is not type safe and (b) please note
  110. // it is NOT type safe.
  111. // If the base::Callback you provide here is of type void(boolean) and you
  112. // resolve the promise with a string, Electron will compile successfully and
  113. // then that promise will be rejected as soon as you try to use it as the
  114. // mate converters doing work behind the scenes will throw an error for you.
  115. // This can be really hard to trace so until either
  116. // * This helper becomes typesafe (by templating the class instead of each
  117. // method)
  118. // * or the world goes mad
  119. // Please try your hardest not to use this method
  120. // The world thanks you
  121. template <typename... ResolveType>
  122. v8::MaybeLocal<v8::Promise> Then(
  123. base::OnceCallback<void(ResolveType...)> cb) {
  124. static_assert(sizeof...(ResolveType) <= 1,
  125. "A promise's 'Then' callback should only receive at most one "
  126. "parameter");
  127. v8::HandleScope handle_scope(isolate());
  128. v8::Context::Scope context_scope(
  129. v8::Local<v8::Context>::New(isolate(), GetContext()));
  130. v8::Local<v8::Value> value = mate::ConvertToV8(isolate(), cb);
  131. v8::Local<v8::Function> handler = v8::Local<v8::Function>::Cast(value);
  132. return GetHandle()->Then(GetContext(), handler);
  133. }
  134. // Promise resolution is a microtask
  135. // We use the MicrotasksRunner to trigger the running of pending microtasks
  136. template <typename T>
  137. v8::Maybe<bool> Resolve(const T& value) {
  138. v8::HandleScope handle_scope(isolate());
  139. v8::MicrotasksScope script_scope(isolate(),
  140. v8::MicrotasksScope::kRunMicrotasks);
  141. v8::Context::Scope context_scope(
  142. v8::Local<v8::Context>::New(isolate(), GetContext()));
  143. return GetInner()->Resolve(GetContext(),
  144. mate::ConvertToV8(isolate(), value));
  145. }
  146. template <typename T>
  147. v8::Maybe<bool> Reject(const T& value) {
  148. v8::HandleScope handle_scope(isolate());
  149. v8::MicrotasksScope script_scope(isolate(),
  150. v8::MicrotasksScope::kRunMicrotasks);
  151. v8::Context::Scope context_scope(
  152. v8::Local<v8::Context>::New(isolate(), GetContext()));
  153. return GetInner()->Reject(GetContext(),
  154. mate::ConvertToV8(isolate(), value));
  155. }
  156. v8::Maybe<bool> RejectWithErrorMessage(const std::string& error);
  157. private:
  158. friend class CopyablePromise;
  159. v8::Local<v8::Promise::Resolver> GetInner() const {
  160. return resolver_.Get(isolate());
  161. }
  162. v8::Isolate* isolate_;
  163. v8::Global<v8::Context> context_;
  164. v8::Global<v8::Promise::Resolver> resolver_;
  165. DISALLOW_COPY_AND_ASSIGN(Promise);
  166. };
  167. // A wrapper of Promise that can be copied.
  168. //
  169. // This class should only be used when we have to pass Promise to a Chromium API
  170. // that does not take OnceCallback.
  171. class CopyablePromise {
  172. public:
  173. explicit CopyablePromise(const Promise& promise);
  174. CopyablePromise(const CopyablePromise&);
  175. ~CopyablePromise();
  176. template <typename T>
  177. static void ResolveCopyablePromise(const CopyablePromise& promise, T result) {
  178. if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
  179. base::PostTaskWithTraits(
  180. FROM_HERE, {content::BrowserThread::UI},
  181. base::BindOnce(Promise::ResolvePromise<T>, promise.GetPromise(),
  182. std::move(result)));
  183. } else {
  184. promise.GetPromise().Resolve(result);
  185. }
  186. }
  187. static void ResolveEmptyCopyablePromise(const CopyablePromise& promise) {
  188. if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
  189. base::PostTaskWithTraits(
  190. FROM_HERE, {content::BrowserThread::UI},
  191. base::BindOnce(Promise::ResolveEmptyPromise, promise.GetPromise()));
  192. } else {
  193. promise.GetPromise().Resolve();
  194. }
  195. }
  196. static void RejectCopyablePromise(const CopyablePromise& promise,
  197. std::string errmsg) {
  198. if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
  199. base::PostTaskWithTraits(
  200. FROM_HERE, {content::BrowserThread::UI},
  201. base::BindOnce(Promise::RejectPromise, promise.GetPromise(),
  202. std::move(errmsg)));
  203. } else {
  204. promise.GetPromise().RejectWithErrorMessage(errmsg);
  205. }
  206. }
  207. Promise GetPromise() const;
  208. private:
  209. using CopyablePersistent =
  210. v8::CopyablePersistentTraits<v8::Promise::Resolver>::CopyablePersistent;
  211. v8::Isolate* isolate_;
  212. CopyablePersistent handle_;
  213. };
  214. } // namespace util
  215. } // namespace electron
  216. namespace mate {
  217. template <>
  218. struct Converter<electron::util::Promise> {
  219. static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
  220. const electron::util::Promise& val);
  221. // TODO(MarshallOfSound): Implement FromV8 to allow promise chaining
  222. // in native land
  223. // static bool FromV8(v8::Isolate* isolate,
  224. // v8::Local<v8::Value> val,
  225. // Promise* out);
  226. };
  227. } // namespace mate
  228. #endif // SHELL_COMMON_PROMISE_UTIL_H_