Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(283)

Unified Diff: extensions/renderer/api_event_handler.cc

Issue 2469593002: [Extensions Bindings] Add Events support (Closed)
Patch Set: . Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « extensions/renderer/api_event_handler.h ('k') | extensions/renderer/api_event_handler_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: extensions/renderer/api_event_handler.cc
diff --git a/extensions/renderer/api_event_handler.cc b/extensions/renderer/api_event_handler.cc
new file mode 100644
index 0000000000000000000000000000000000000000..eb606654b7ad5a0fd82edcc17870c8423dd42b6f
--- /dev/null
+++ b/extensions/renderer/api_event_handler.cc
@@ -0,0 +1,222 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/renderer/api_event_handler.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "content/public/child/v8_value_converter.h"
+#include "gin/arguments.h"
+#include "gin/per_context_data.h"
+
+namespace extensions {
+
+namespace {
+
+const char kExtensionAPIEventPerContextKey[] = "extension_api_events";
+const char kEventListenersKey[] = "event_listeners";
+
+void ForwardToGinHandler(const v8::FunctionCallbackInfo<v8::Value>& info) {
+ gin::Arguments args(info);
+ v8::Local<v8::External> external;
+ CHECK(args.GetData(&external));
+ auto callback =
+ static_cast<APIEventHandler::HandlerCallback*>(external->Value());
+ callback->Run(info.This(), &args);
+}
+
+} // namespace
+
+APIEventHandler::APIEventPerContextData::APIEventPerContextData() = default;
+APIEventHandler::APIEventPerContextData::~APIEventPerContextData() = default;
+
+APIEventHandler::APIEventHandler(const binding::RunJSFunction& call_js)
+ : call_js_(call_js), weak_factory_(this) {}
+APIEventHandler::~APIEventHandler() {}
+
+v8::Local<v8::Object> APIEventHandler::CreateEventInstance(
jbroman 2016/11/01 18:59:24 High-level comment here: while we needed to get cl
jbroman 2016/11/01 19:14:33 One note here: a little more caution than I've imp
Devlin 2016/11/02 00:48:29 So, there's a few gotchas here and there with how
+ const std::string& event_name,
+ v8::Local<v8::Context> context) {
+ gin::PerContextData* per_context_data = gin::PerContextData::From(context);
+ DCHECK(per_context_data);
+ APIEventPerContextData* data = static_cast<APIEventPerContextData*>(
+ per_context_data->GetUserData(kExtensionAPIEventPerContextKey));
+ if (!data) {
+ auto api_data = base::MakeUnique<APIEventPerContextData>();
+ data = api_data.get();
+ per_context_data->SetUserData(kExtensionAPIEventPerContextKey,
+ api_data.release());
+ }
+
+ DCHECK(data->event_data.find(event_name) == data->event_data.end());
+ auto listeners = base::MakeUnique<EventListeners>();
+
+ if (event_template_.IsEmpty())
+ InitializeTemplate(context->GetIsolate(), data);
+
+ v8::Local<v8::ObjectTemplate> local_template =
+ event_template_.Get(context->GetIsolate());
+ v8::MaybeLocal<v8::Object> maybe_object =
+ local_template->NewInstance(context);
+ v8::Local<v8::Object> object;
+ // TODO(devlin): Could this fail?
+ CHECK(maybe_object.ToLocal(&object));
jbroman 2016/11/01 18:59:24 I believe this won't fail, because we know that si
Devlin 2016/11/02 00:48:29 Moot if we go with the latest patch set.
+
+ // We cache the EventListeners pointer on the event object for easy lookups
+ // when adding, removing, or querying listeners from JS.
+ v8::Maybe<bool> success = object->SetPrivate(
+ context, v8::Private::ForApi(context->GetIsolate(),
+ gin::StringToSymbol(context->GetIsolate(),
+ kEventListenersKey)),
+ v8::External::New(context->GetIsolate(), listeners.get()));
+ DCHECK(success.IsJust());
+ DCHECK(success.FromJust());
+
+ data->event_data.insert(std::make_pair(event_name, std::move(listeners)));
+
+ return object;
+}
+
+void APIEventHandler::InitializeTemplate(v8::Isolate* isolate,
+ APIEventPerContextData* data) {
+ v8::Local<v8::ObjectTemplate> event_template =
+ v8::ObjectTemplate::New(isolate);
+
+ {
+ struct Method {
+ const char* name;
+ void (APIEventHandler::*handler)(v8::Local<v8::Object> object,
+ gin::Arguments* args);
+ } methods[] = {
+ {"addListener", &APIEventHandler::AddListener},
+ {"removeListener", &APIEventHandler::RemoveListener},
+ {"hasListener", &APIEventHandler::HasListener},
+ {"hasListeners", &APIEventHandler::HasListeners},
+ };
+ for (const auto& method : methods) {
+ auto handler_callback = base::MakeUnique<HandlerCallback>(
+ base::Bind(method.handler, weak_factory_.GetWeakPtr()));
+ // TODO(devlin): FunctionTemplate docs say "There can only be one function
+ // created from a FunctionTemplate in a context." I'm assuming that means
+ // we can't change the template, rather than we can only use it to
+ // instantiate a single function object?
jbroman 2016/11/01 18:59:24 It means that if you call v8::FunctionTemplate::Ge
Devlin 2016/11/02 00:48:29 Ditto
+ v8::Local<v8::FunctionTemplate> function = v8::FunctionTemplate::New(
+ isolate, &ForwardToGinHandler,
+ v8::External::New(isolate, handler_callback.get()),
+ v8::Local<v8::Signature>(), 0, v8::ConstructorBehavior::kThrow);
jbroman 2016/11/01 18:59:24 If you use a signature, you can have V8 validate t
Devlin 2016/11/02 00:48:29 Ditto
+ data->callbacks.push_back(std::move(handler_callback));
+ event_template->Set(gin::StringToSymbol(isolate, method.name), function);
+ }
+ }
+
+ event_template_ = v8::Global<v8::ObjectTemplate>(isolate, event_template);
+}
+
+void APIEventHandler::FireEventInContext(const std::string& event_name,
+ v8::Local<v8::Context> context,
+ const base::ListValue& args) {
+ gin::PerContextData* per_context_data = gin::PerContextData::From(context);
+ DCHECK(per_context_data);
+ APIEventPerContextData* data = static_cast<APIEventPerContextData*>(
+ per_context_data->GetUserData(kExtensionAPIEventPerContextKey));
+ if (!data)
+ return;
+
+ auto iter = data->event_data.find(event_name);
+ if (iter == data->event_data.end())
+ return;
+
+ EventListeners* listeners = iter->second.get();
+
+ std::vector<v8::Local<v8::Value>> v8_args;
+ v8_args.reserve(args.GetSize());
+ std::unique_ptr<content::V8ValueConverter> converter(
+ content::V8ValueConverter::create());
+ for (const auto& arg : args)
+ v8_args.push_back(converter->ToV8Value(arg.get(), context));
+
+ for (auto& listener : *listeners) {
+ call_js_.Run(listener.Get(context->GetIsolate()), context, v8_args.size(),
+ v8_args.data());
+ }
+}
+
+const APIEventHandler::EventListeners&
+APIEventHandler::GetEventListenersForTesting(const std::string& event_name,
+ v8::Local<v8::Context> context) {
+ gin::PerContextData* per_context_data = gin::PerContextData::From(context);
+ DCHECK(per_context_data);
+ APIEventPerContextData* data = static_cast<APIEventPerContextData*>(
+ per_context_data->GetUserData(kExtensionAPIEventPerContextKey));
+ DCHECK(data);
+
+ auto iter = data->event_data.find(event_name);
+ DCHECK(iter != data->event_data.end());
+ return *iter->second;
+}
+
+APIEventHandler::EventListeners* APIEventHandler::GetListenersForObject(
+ v8::Local<v8::Object> object,
+ v8::Local<v8::Context> context) {
+ v8::MaybeLocal<v8::Value> maybe_value = object->GetPrivate(
+ context, v8::Private::ForApi(context->GetIsolate(),
+ gin::StringToSymbol(context->GetIsolate(),
+ kEventListenersKey)));
+ v8::Local<v8::Value> value;
+ CHECK(maybe_value.ToLocal(&value));
+ CHECK(value->IsExternal());
jbroman 2016/11/01 18:59:24 You can't rely on this working, at the moment. You
Devlin 2016/11/02 00:48:29 Also moot.
+ v8::Local<v8::External> external = v8::Local<v8::External>::Cast(value);
+ return static_cast<EventListeners*>(external->Value());
+}
+
+void APIEventHandler::AddListener(v8::Local<v8::Object> caller,
+ gin::Arguments* args) {
+ v8::Local<v8::Function> listener;
+ if (args->Length() != 1 || !args->GetNext(&listener))
+ return;
+ EventListeners* listeners =
+ GetListenersForObject(caller, args->isolate()->GetCurrentContext());
jbroman 2016/11/01 18:59:24 Didn't catch this before, but getting the context
Devlin 2016/11/02 00:48:29 And moot :)
+ if (std::find(listeners->begin(), listeners->end(), listener) !=
+ listeners->end())
+ return;
+ listeners->push_back(v8::Global<v8::Function>(args->isolate(), listener));
+}
+
+void APIEventHandler::RemoveListener(v8::Local<v8::Object> caller,
+ gin::Arguments* args) {
+ v8::Local<v8::Function> listener;
+ if (args->Length() != 1 || !args->GetNext(&listener))
+ return;
+ EventListeners* listeners =
+ GetListenersForObject(caller, args->isolate()->GetCurrentContext());
+ auto iter = std::find(listeners->begin(), listeners->end(), listener);
+ if (iter != listeners->end())
+ listeners->erase(iter);
+}
+
+void APIEventHandler::HasListener(v8::Local<v8::Object> caller,
+ gin::Arguments* args) {
+ v8::Local<v8::Function> listener;
+ if (args->Length() != 1 || !args->GetNext(&listener))
+ return;
+ EventListeners* listeners =
+ GetListenersForObject(caller, args->isolate()->GetCurrentContext());
+ auto iter = std::find(listeners->begin(), listeners->end(), listener);
+ args->Return(iter != listeners->end());
+}
+
+void APIEventHandler::HasListeners(v8::Local<v8::Object> caller,
+ gin::Arguments* args) {
+ if (args->Length() != 0)
+ return;
+ EventListeners* listeners =
+ GetListenersForObject(caller, args->isolate()->GetCurrentContext());
+ args->Return(!listeners->empty());
+}
+
+} // namespace extensions
« no previous file with comments | « extensions/renderer/api_event_handler.h ('k') | extensions/renderer/api_event_handler_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698