blob: d9222cd19394edea8bb5d6e11ccbc0eb6682523b [file] [log] [blame]
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef EXTENSIONS_RENDERER_BINDINGS_API_EVENT_HANDLER_H_
#define EXTENSIONS_RENDERER_BINDINGS_API_EVENT_HANDLER_H_
#include <string>
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "extensions/common/mojom/event_dispatcher.mojom-forward.h"
#include "extensions/renderer/bindings/api_binding_types.h"
#include "extensions/renderer/bindings/api_event_listeners.h"
#include "extensions/renderer/bindings/event_emitter.h"
#include "extensions/renderer/bindings/listener_tracker.h"
#include "v8/include/v8.h"
namespace extensions {
class APIResponseValidator;
class ExceptionHandler;
// The object to handle API events. This includes vending v8::Objects for the
// event; handling adding, removing, and querying listeners; and firing events
// to subscribed listeners. Designed to be used across JS contexts, but on a
// single thread.
class APIEventHandler {
public:
// A callback to retrieve the owner of the context's identity. This allows us
// to associate multiple listeners from different v8::Contexts with the same
// owner (e.g., extension).
using ContextOwnerIdGetter =
base::RepeatingCallback<std::string(v8::Local<v8::Context>)>;
APIEventHandler(const APIEventListeners::ListenersUpdated& listeners_changed,
const ContextOwnerIdGetter& context_owner_id_getter,
ExceptionHandler* exception_handler);
APIEventHandler(const APIEventHandler&) = delete;
APIEventHandler& operator=(const APIEventHandler&) = delete;
~APIEventHandler();
// Sets the response validator to be used in verifying event arguments.
void SetResponseValidator(std::unique_ptr<APIResponseValidator> validator);
// Returns a new v8::Object for an event with the given `event_name`. If
// `notify_on_change` is true, notifies whenever listeners state is changed.
// TODO(devlin): Maybe worth creating a Params struct to hold the event
// information?
v8::Local<v8::Object> CreateEventInstance(const std::string& event_name,
bool supports_filters,
bool supports_lazy_listeners,
int max_listeners,
bool notify_on_change,
v8::Local<v8::Context> context);
// Creates a new event without any name. This is used by custom bindings when
// the entirety of the logic for the event is contained in the renderer. These
// events do not notify of new/removed listeners or allow for dispatching
// through FireEventInContext().
v8::Local<v8::Object> CreateAnonymousEventInstance(
v8::Local<v8::Context> context);
// Invalidates the given `event`.
void InvalidateCustomEvent(v8::Local<v8::Context> context,
v8::Local<v8::Object> event);
// Notifies all listeners of the event with the given `event_name` in the
// specified `context`, sending the included `arguments`.
// `on_dispatched_callback` allows the caller to specify a `v8::Function` to
// be called with the results of the listener event dispatch.
// `listener_error_callback` is an optional callback that is invoked
// immediately when a listener throws an exception. The exception is passed as
// the single argument to the callback.
// Warning: This runs arbitrary JS code, so the `context` may be invalidated
// after this!
void FireEventInContext(const std::string& event_name,
v8::Local<v8::Context> context,
const base::Value::List& arguments,
mojom::EventFilteringInfoPtr filter);
void FireEventInContext(const std::string& event_name,
v8::Local<v8::Context> context,
v8::LocalVector<v8::Value>* arguments,
mojom::EventFilteringInfoPtr filter,
v8::Local<v8::Function> on_dispatched_callback,
v8::Local<v8::Function> listener_error_callback);
// Registers a `function` to serve as an "argument massager" for the given
// `event_name`, mutating the original arguments.
// The function is called with two arguments: the array of original arguments
// being dispatched to the event, and the function to dispatch the event to
// listeners.
void RegisterArgumentMassager(v8::Local<v8::Context> context,
const std::string& event_name,
v8::Local<v8::Function> function);
// Returns true if there is a listener for the given `event_name` in the
// given `context`.
bool HasListenerForEvent(const std::string& event_name,
v8::Local<v8::Context> context);
// Invalidates listeners for the given `context`. It's a shame we have to
// have this separately (as opposed to hooking into e.g. a PerContextData
// destructor), but we need to do this before the context is fully removed
// (because the associated extension ScriptContext needs to be valid).
void InvalidateContext(v8::Local<v8::Context> context);
// Returns the number of event listeners for a given `event_name` and
// `context`.
size_t GetNumEventListenersForTesting(const std::string& event_name,
v8::Local<v8::Context> context);
private:
APIEventListeners::ListenersUpdated listeners_changed_;
ContextOwnerIdGetter context_owner_id_getter_;
// The shared ListenerTracker for all listeners in the system.
ListenerTracker listener_tracker_;
// The exception handler associated with the bindings system; guaranteed to
// outlive this object.
const raw_ptr<ExceptionHandler> exception_handler_;
// The response validator used to verify event arguments. Only non-null if
// validation is enabled.
std::unique_ptr<APIResponseValidator> api_response_validator_;
};
} // namespace extensions
#endif // EXTENSIONS_RENDERER_BINDINGS_API_EVENT_HANDLER_H_