promise.h 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  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. // Promise resolution is a microtask
  93. // We use the MicrotasksRunner to trigger the running of pending microtasks
  94. v8::Maybe<bool> Resolve(const RT& value) {
  95. gin_helper::Locker locker(isolate());
  96. v8::HandleScope handle_scope(isolate());
  97. gin_helper::MicrotasksScope microtasks_scope(isolate());
  98. v8::Context::Scope context_scope(GetContext());
  99. return GetInner()->Resolve(GetContext(),
  100. gin::ConvertToV8(isolate(), value));
  101. }
  102. template <typename... ResolveType>
  103. v8::MaybeLocal<v8::Promise> Then(
  104. base::OnceCallback<void(ResolveType...)> cb) {
  105. static_assert(sizeof...(ResolveType) <= 1,
  106. "A promise's 'Then' callback should only receive at most one "
  107. "parameter");
  108. static_assert(
  109. std::is_same<RT, std::tuple_element_t<0, std::tuple<ResolveType...>>>(),
  110. "A promises's 'Then' callback must handle the same type as the "
  111. "promises resolve type");
  112. gin_helper::Locker locker(isolate());
  113. v8::HandleScope handle_scope(isolate());
  114. v8::Context::Scope context_scope(GetContext());
  115. v8::Local<v8::Value> value = gin::ConvertToV8(isolate(), std::move(cb));
  116. v8::Local<v8::Function> handler = v8::Local<v8::Function>::Cast(value);
  117. return GetHandle()->Then(GetContext(), handler);
  118. }
  119. };
  120. // Template implementation that returns nothing.
  121. template <>
  122. class Promise<void> : public PromiseBase {
  123. public:
  124. using PromiseBase::PromiseBase;
  125. // Helper for resolving the empty promise.
  126. static void ResolvePromise(Promise<void> promise);
  127. // Returns an already-resolved promise.
  128. static v8::Local<v8::Promise> ResolvedPromise(v8::Isolate* isolate);
  129. v8::Maybe<bool> Resolve();
  130. };
  131. } // namespace gin_helper
  132. namespace gin {
  133. template <typename T>
  134. struct Converter<gin_helper::Promise<T>> {
  135. static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
  136. const gin_helper::Promise<T>& val) {
  137. return val.GetHandle();
  138. }
  139. // TODO(MarshallOfSound): Implement FromV8 to allow promise chaining
  140. // in native land
  141. // static bool FromV8(v8::Isolate* isolate,
  142. // v8::Local<v8::Value> val,
  143. // Promise* out);
  144. };
  145. } // namespace gin
  146. #endif // SHELL_COMMON_GIN_HELPER_PROMISE_H_