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