promise.h 6.0 KB

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