|
@@ -0,0 +1,121 @@
|
|
|
+// Copyright (c) 2017 GitHub, Inc.
|
|
|
+// Use of this source code is governed by the MIT license that can be
|
|
|
+// found in the LICENSE file.
|
|
|
+#include <string>
|
|
|
+
|
|
|
+#include "atom/browser/api/event_subscriber.h"
|
|
|
+#include "atom/common/native_mate_converters/callback.h"
|
|
|
+
|
|
|
+namespace {
|
|
|
+
|
|
|
+// A FunctionTemplate lifetime is bound to the v8 context, so it can be safely
|
|
|
+// stored as a global here since there's only one for the main process.
|
|
|
+v8::Global<v8::FunctionTemplate> g_cached_template;
|
|
|
+
|
|
|
+struct JSHandlerData {
|
|
|
+ JSHandlerData(v8::Isolate* isolate,
|
|
|
+ mate::internal::EventSubscriberBase* subscriber)
|
|
|
+ : handle_(isolate, v8::External::New(isolate, this)),
|
|
|
+ subscriber_(subscriber) {
|
|
|
+ handle_.SetWeak(this, GC, v8::WeakCallbackType::kFinalizer);
|
|
|
+ }
|
|
|
+
|
|
|
+ static void GC(const v8::WeakCallbackInfo<JSHandlerData>& data) {
|
|
|
+ delete data.GetParameter();
|
|
|
+ }
|
|
|
+
|
|
|
+ v8::Global<v8::External> handle_;
|
|
|
+ mate::internal::EventSubscriberBase* subscriber_;
|
|
|
+};
|
|
|
+
|
|
|
+void InvokeCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
|
|
|
+ v8::Locker locker(info.GetIsolate());
|
|
|
+ v8::HandleScope handle_scope(info.GetIsolate());
|
|
|
+ v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
|
|
|
+ v8::Context::Scope context_scope(context);
|
|
|
+ mate::Arguments args(info);
|
|
|
+ v8::Local<v8::Value> handler, event;
|
|
|
+ args.GetNext(&handler);
|
|
|
+ args.GetNext(&event);
|
|
|
+ DCHECK(handler->IsExternal());
|
|
|
+ DCHECK(event->IsString());
|
|
|
+ JSHandlerData* handler_data = static_cast<JSHandlerData*>(
|
|
|
+ v8::Local<v8::External>::Cast(handler)->Value());
|
|
|
+ handler_data->subscriber_->EventEmitted(mate::V8ToString(event), &args);
|
|
|
+}
|
|
|
+
|
|
|
+} // namespace
|
|
|
+
|
|
|
+namespace mate {
|
|
|
+
|
|
|
+namespace internal {
|
|
|
+
|
|
|
+EventSubscriberBase::EventSubscriberBase(v8::Isolate* isolate,
|
|
|
+ v8::Local<v8::Object> emitter)
|
|
|
+ : isolate_(isolate), emitter_(isolate, emitter) {
|
|
|
+ if (g_cached_template.IsEmpty()) {
|
|
|
+ g_cached_template = v8::Global<v8::FunctionTemplate>(
|
|
|
+ isolate_, v8::FunctionTemplate::New(isolate_, InvokeCallback));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+EventSubscriberBase::~EventSubscriberBase() {
|
|
|
+ if (!isolate_) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ RemoveAllListeners();
|
|
|
+ emitter_.Reset();
|
|
|
+ DCHECK_EQ(js_handlers_.size(), 0);
|
|
|
+}
|
|
|
+
|
|
|
+void EventSubscriberBase::On(const std::string& event_name) {
|
|
|
+ DCHECK(js_handlers_.find(event_name) == js_handlers_.end());
|
|
|
+ v8::Locker locker(isolate_);
|
|
|
+ v8::Isolate::Scope isolate_scope(isolate_);
|
|
|
+ v8::HandleScope handle_scope(isolate_);
|
|
|
+ auto fn_template = g_cached_template.Get(isolate_);
|
|
|
+ auto event = mate::StringToV8(isolate_, event_name);
|
|
|
+ auto js_handler_data = new JSHandlerData(isolate_, this);
|
|
|
+ v8::Local<v8::Value> fn = internal::BindFunctionWith(
|
|
|
+ isolate_, isolate_->GetCurrentContext(), fn_template->GetFunction(),
|
|
|
+ js_handler_data->handle_.Get(isolate_), event);
|
|
|
+ js_handlers_.insert(
|
|
|
+ std::make_pair(event_name, v8::Global<v8::Value>(isolate_, fn)));
|
|
|
+ internal::ValueVector converted_args = {event, fn};
|
|
|
+ internal::CallMethodWithArgs(isolate_, emitter_.Get(isolate_), "on",
|
|
|
+ &converted_args);
|
|
|
+}
|
|
|
+
|
|
|
+void EventSubscriberBase::Off(const std::string& event_name) {
|
|
|
+ v8::Locker locker(isolate_);
|
|
|
+ v8::Isolate::Scope isolate_scope(isolate_);
|
|
|
+ v8::HandleScope handle_scope(isolate_);
|
|
|
+ auto js_handler = js_handlers_.find(event_name);
|
|
|
+ DCHECK(js_handler != js_handlers_.end());
|
|
|
+ RemoveListener(js_handler);
|
|
|
+}
|
|
|
+
|
|
|
+void EventSubscriberBase::RemoveAllListeners() {
|
|
|
+ v8::Locker locker(isolate_);
|
|
|
+ v8::Isolate::Scope isolate_scope(isolate_);
|
|
|
+ v8::HandleScope handle_scope(isolate_);
|
|
|
+ while (!js_handlers_.empty()) {
|
|
|
+ RemoveListener(js_handlers_.begin());
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+std::map<std::string, v8::Global<v8::Value>>::iterator
|
|
|
+EventSubscriberBase::RemoveListener(
|
|
|
+ std::map<std::string, v8::Global<v8::Value>>::iterator it) {
|
|
|
+ internal::ValueVector args = {StringToV8(isolate_, it->first),
|
|
|
+ it->second.Get(isolate_)};
|
|
|
+ internal::CallMethodWithArgs(
|
|
|
+ isolate_, v8::Local<v8::Object>::Cast(emitter_.Get(isolate_)),
|
|
|
+ "removeListener", &args);
|
|
|
+ it->second.Reset();
|
|
|
+ return js_handlers_.erase(it);
|
|
|
+}
|
|
|
+
|
|
|
+} // namespace internal
|
|
|
+
|
|
|
+} // namespace mate
|