-
Notifications
You must be signed in to change notification settings - Fork 15k
/
callback.cc
156 lines (125 loc) · 4.92 KB
/
callback.cc
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
// Copyright (c) 2015 GitHub, Inc. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/native_mate_converters/callback.h"
#include "content/public/browser/browser_thread.h"
#include "native_mate/dictionary.h"
using content::BrowserThread;
namespace mate {
namespace internal {
namespace {
struct TranslaterHolder {
explicit TranslaterHolder(v8::Isolate* isolate)
: handle(isolate, v8::External::New(isolate, this)) {
handle.SetWeak(this, &GC, v8::WeakCallbackType::kFinalizer);
}
~TranslaterHolder() {
if (!handle.IsEmpty()) {
handle.ClearWeak();
handle.Reset();
}
}
static void GC(const v8::WeakCallbackInfo<TranslaterHolder>& data) {
delete data.GetParameter();
}
v8::Global<v8::External> handle;
Translater translater;
};
// Cached JavaScript version of |CallTranslater|.
v8::Persistent<v8::FunctionTemplate> g_call_translater;
void CallTranslater(v8::Local<v8::External> external,
v8::Local<v8::Object> state,
mate::Arguments* args) {
// Whether the callback should only be called for once.
v8::Isolate* isolate = args->isolate();
bool one_time = state->Has(mate::StringToSymbol(isolate, "oneTime"));
// Check if the callback has already been called.
if (one_time) {
auto called_symbol = mate::StringToSymbol(isolate, "called");
if (state->Has(called_symbol)) {
args->ThrowError("callback can only be called for once");
return;
} else {
state->Set(called_symbol, v8::Boolean::New(isolate, true));
}
}
TranslaterHolder* holder = static_cast<TranslaterHolder*>(external->Value());
holder->translater.Run(args);
// Free immediately for one-time callback.
if (one_time)
delete holder;
}
} // namespace
// Destroy the class on UI thread when possible.
struct DeleteOnUIThread {
template <typename T>
static void Destruct(const T* x) {
if (Locker::IsBrowserProcess() &&
!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, x);
} else {
delete x;
}
}
};
// Like v8::Global, but ref-counted.
template <typename T>
class RefCountedGlobal
: public base::RefCountedThreadSafe<RefCountedGlobal<T>, DeleteOnUIThread> {
public:
RefCountedGlobal(v8::Isolate* isolate, v8::Local<v8::Value> value)
: handle_(isolate, v8::Local<T>::Cast(value)) {}
bool IsAlive() const { return !handle_.IsEmpty(); }
v8::Local<T> NewHandle(v8::Isolate* isolate) const {
return v8::Local<T>::New(isolate, handle_);
}
private:
v8::Global<T> handle_;
DISALLOW_COPY_AND_ASSIGN(RefCountedGlobal);
};
SafeV8Function::SafeV8Function(v8::Isolate* isolate, v8::Local<v8::Value> value)
: v8_function_(new RefCountedGlobal<v8::Function>(isolate, value)) {}
SafeV8Function::SafeV8Function(const SafeV8Function& other)
: v8_function_(other.v8_function_) {}
SafeV8Function::~SafeV8Function() {}
bool SafeV8Function::IsAlive() const {
return v8_function_.get() && v8_function_->IsAlive();
}
v8::Local<v8::Function> SafeV8Function::NewHandle(v8::Isolate* isolate) const {
return v8_function_->NewHandle(isolate);
}
v8::Local<v8::Value> CreateFunctionFromTranslater(v8::Isolate* isolate,
const Translater& translater,
bool one_time) {
// The FunctionTemplate is cached.
if (g_call_translater.IsEmpty())
g_call_translater.Reset(isolate, mate::CreateFunctionTemplate(
isolate, base::Bind(&CallTranslater)));
v8::Local<v8::FunctionTemplate> call_translater =
v8::Local<v8::FunctionTemplate>::New(isolate, g_call_translater);
auto* holder = new TranslaterHolder(isolate);
holder->translater = translater;
Dictionary state = mate::Dictionary::CreateEmpty(isolate);
if (one_time)
state.Set("oneTime", true);
return BindFunctionWith(isolate, isolate->GetCurrentContext(),
call_translater->GetFunction(),
holder->handle.Get(isolate), state.GetHandle());
}
// func.bind(func, arg1).
// NB(zcbenz): Using C++11 version crashes VS.
v8::Local<v8::Value> BindFunctionWith(v8::Isolate* isolate,
v8::Local<v8::Context> context,
v8::Local<v8::Function> func,
v8::Local<v8::Value> arg1,
v8::Local<v8::Value> arg2) {
v8::MaybeLocal<v8::Value> bind = func->Get(mate::StringToV8(isolate, "bind"));
CHECK(!bind.IsEmpty());
v8::Local<v8::Function> bind_func =
v8::Local<v8::Function>::Cast(bind.ToLocalChecked());
v8::Local<v8::Value> converted[] = {func, arg1, arg2};
return bind_func->Call(context, func, arraysize(converted), converted)
.ToLocalChecked();
}
} // namespace internal
} // namespace mate