// 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/event_emitter.h" #include #include "extensions/renderer/api_event_listeners.h" #include "gin/object_template_builder.h" #include "gin/per_context_data.h" namespace extensions { gin::WrapperInfo EventEmitter::kWrapperInfo = {gin::kEmbedderNativeGin}; EventEmitter::EventEmitter(bool supports_filters, std::unique_ptr listeners, const binding::RunJSFunction& run_js) : supports_filters_(supports_filters), listeners_(std::move(listeners)), run_js_(run_js) {} EventEmitter::~EventEmitter() {} gin::ObjectTemplateBuilder EventEmitter::GetObjectTemplateBuilder( v8::Isolate* isolate) { return Wrappable::GetObjectTemplateBuilder(isolate) .SetMethod("addListener", &EventEmitter::AddListener) .SetMethod("removeListener", &EventEmitter::RemoveListener) .SetMethod("hasListener", &EventEmitter::HasListener) .SetMethod("hasListeners", &EventEmitter::HasListeners) // The following methods aren't part of the public API, but are used // by our custom bindings and exposed on the public event object. :( // TODO(devlin): Once we convert all custom bindings that use these, // they can be removed. .SetMethod("dispatch", &EventEmitter::Dispatch); } void EventEmitter::Fire(v8::Local context, std::vector>* args, const EventFilteringInfo* filter) { // Note that |listeners_| can be modified during handling. std::vector> listeners = listeners_->GetListeners(filter, context); for (const auto& listener : listeners) { v8::TryCatch try_catch(context->GetIsolate()); // SetVerbose() means the error will still get logged, which is what we // want. We don't let it bubble up any further to prevent it from being // surfaced in e.g. JS code that triggered the event. try_catch.SetVerbose(true); run_js_.Run(listener, context, args->size(), args->data()); } } void EventEmitter::Invalidate(v8::Local context) { valid_ = false; listeners_->Invalidate(context); } size_t EventEmitter::GetNumListeners() const { return listeners_->GetNumListeners(); } void EventEmitter::AddListener(gin::Arguments* arguments) { // If script from another context maintains a reference to this object, it's // possible that functions can be called after this object's owning context // is torn down and released by blink. We don't support this behavior, but // we need to make sure nothing crashes, so early out of methods. if (!valid_) return; v8::Local listener; // TODO(devlin): For some reason, we don't throw an error when someone calls // add/removeListener with no argument. We probably should. For now, keep // the status quo, but we should revisit this. if (!arguments->GetNext(&listener)) return; if (!arguments->PeekNext().IsEmpty() && !supports_filters_) { arguments->ThrowTypeError("This event does not support filters"); return; } v8::Local filter; if (!arguments->PeekNext().IsEmpty() && !arguments->GetNext(&filter)) { arguments->ThrowTypeError("Invalid invocation"); return; } v8::Local context = arguments->GetHolderCreationContext(); if (!gin::PerContextData::From(context)) return; std::string error; if (!listeners_->AddListener(listener, filter, context, &error) && !error.empty()) { arguments->ThrowTypeError(error); } } void EventEmitter::RemoveListener(gin::Arguments* arguments) { // See comment in AddListener(). if (!valid_) return; v8::Local listener; // See comment in AddListener(). if (!arguments->GetNext(&listener)) return; listeners_->RemoveListener(listener, arguments->GetHolderCreationContext()); } bool EventEmitter::HasListener(v8::Local listener) { return listeners_->HasListener(listener); } bool EventEmitter::HasListeners() { return listeners_->GetNumListeners() != 0; } void EventEmitter::Dispatch(gin::Arguments* arguments) { if (!valid_) return; if (listeners_->GetNumListeners() == 0) return; v8::HandleScope handle_scope(arguments->isolate()); v8::Local context = arguments->isolate()->GetCurrentContext(); std::vector> v8_args = arguments->GetAll(); Fire(context, &v8_args, nullptr); } } // namespace extensions