| // Copyright (c) 2012 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. |
| |
| #ifndef EXTENSIONS_BROWSER_EVENT_ROUTER_H_ |
| #define EXTENSIONS_BROWSER_EVENT_ROUTER_H_ |
| |
| #include <set> |
| #include <string> |
| #include <unordered_map> |
| |
| #include "base/callback.h" |
| #include "base/compiler_specific.h" |
| #include "base/macros.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/observer_list.h" |
| #include "base/scoped_observer.h" |
| #include "base/values.h" |
| #include "components/keyed_service/core/keyed_service.h" |
| #include "content/public/browser/render_process_host_observer.h" |
| #include "extensions/browser/event_listener_map.h" |
| #include "extensions/browser/events/event_ack_data.h" |
| #include "extensions/browser/events/lazy_event_dispatch_util.h" |
| #include "extensions/browser/extension_event_histogram_value.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/browser/extension_registry_observer.h" |
| #include "extensions/browser/lazy_context_task_queue.h" |
| #include "extensions/common/constants.h" |
| #include "extensions/common/event_filtering_info.h" |
| #include "extensions/common/features/feature.h" |
| #include "ipc/ipc_sender.h" |
| #include "url/gurl.h" |
| |
| class GURL; |
| struct ServiceWorkerIdentifier; |
| |
| namespace content { |
| class BrowserContext; |
| class RenderProcessHost; |
| } |
| |
| namespace extensions { |
| class Extension; |
| class ExtensionPrefs; |
| |
| struct Event; |
| struct EventListenerInfo; |
| |
| // TODO(lazyboy): Document how extension events work, including how listeners |
| // are registered and how listeners are tracked in renderer and browser process. |
| class EventRouter : public KeyedService, |
| public ExtensionRegistryObserver, |
| public EventListenerMap::Delegate, |
| public content::RenderProcessHostObserver { |
| public: |
| // These constants convey the state of our knowledge of whether we're in |
| // a user-caused gesture as part of DispatchEvent. |
| enum UserGestureState { |
| USER_GESTURE_UNKNOWN = 0, |
| USER_GESTURE_ENABLED = 1, |
| USER_GESTURE_NOT_ENABLED = 2, |
| }; |
| |
| // The pref key for the list of event names for which an extension has |
| // registered from its lazy background page. |
| static const char kRegisteredLazyEvents[]; |
| // The pref key for the list of event names for which an extension has |
| // registered from its service worker. |
| static const char kRegisteredServiceWorkerEvents[]; |
| |
| // Observers register interest in events with a particular name and are |
| // notified when a listener is added or removed. Observers are matched by |
| // the base name of the event (e.g. adding an event listener for event name |
| // "foo.onBar/123" will trigger observers registered for "foo.onBar"). |
| class Observer { |
| public: |
| // Called when a listener is added. |
| virtual void OnListenerAdded(const EventListenerInfo& details) {} |
| // Called when a listener is removed. |
| virtual void OnListenerRemoved(const EventListenerInfo& details) {} |
| |
| protected: |
| virtual ~Observer() {} |
| }; |
| |
| // A test observer to monitor event dispatching. |
| class TestObserver { |
| public: |
| virtual ~TestObserver() = default; |
| virtual void OnWillDispatchEvent(const Event& event) = 0; |
| virtual void OnDidDispatchEventToProcess(const Event& event) = 0; |
| }; |
| |
| // Gets the EventRouter for |browser_context|. |
| static EventRouter* Get(content::BrowserContext* browser_context); |
| |
| // Converts event names like "foo.onBar/123" into "foo.onBar". Event names |
| // without a "/" are returned unchanged. |
| static std::string GetBaseEventName(const std::string& full_event_name); |
| |
| // Sends an event via ipc_sender to the given extension. Can be called on any |
| // thread. |
| // |
| // It is very rare to call this function directly. Instead use the instance |
| // methods BroadcastEvent or DispatchEventToExtension. |
| // Note that this method will dispatch the event with |
| // UserGestureState:USER_GESTURE_UNKNOWN. |
| static void DispatchEventToSender(IPC::Sender* ipc_sender, |
| content::BrowserContext* browser_context, |
| const std::string& extension_id, |
| events::HistogramValue histogram_value, |
| const std::string& event_name, |
| int render_process_id, |
| int worker_thread_id, |
| int64_t service_worker_version_id, |
| std::unique_ptr<base::ListValue> event_args, |
| const EventFilteringInfo& info); |
| |
| // Returns false when the event is scoped to a context and the listening |
| // extension does not have access to events from that context. |
| static bool CanDispatchEventToBrowserContext(content::BrowserContext* context, |
| const Extension* extension, |
| const Event& event); |
| |
| // An EventRouter is shared between |browser_context| and its associated |
| // incognito context. |extension_prefs| may be NULL in tests. |
| EventRouter(content::BrowserContext* browser_context, |
| ExtensionPrefs* extension_prefs); |
| ~EventRouter() override; |
| |
| // Add or remove an extension as an event listener for |event_name|. |
| // |
| // Note that multiple extensions can share a process due to process |
| // collapsing. Also, a single extension can have 2 processes if it is a split |
| // mode extension. |
| void AddEventListener(const std::string& event_name, |
| content::RenderProcessHost* process, |
| const ExtensionId& extension_id); |
| void AddServiceWorkerEventListener(const std::string& event_name, |
| content::RenderProcessHost* process, |
| const ExtensionId& extension_id, |
| const GURL& service_worker_scope, |
| int64_t service_worker_version_id, |
| int worker_thread_id); |
| void RemoveEventListener(const std::string& event_name, |
| content::RenderProcessHost* process, |
| const ExtensionId& extension_id); |
| void RemoveServiceWorkerEventListener(const std::string& event_name, |
| content::RenderProcessHost* process, |
| const ExtensionId& extension_id, |
| const GURL& service_worker_scope, |
| int64_t service_worker_version_id, |
| int worker_thread_id); |
| |
| // Add or remove a URL as an event listener for |event_name|. |
| void AddEventListenerForURL(const std::string& event_name, |
| content::RenderProcessHost* process, |
| const GURL& listener_url); |
| void RemoveEventListenerForURL(const std::string& event_name, |
| content::RenderProcessHost* process, |
| const GURL& listener_url); |
| |
| EventListenerMap& listeners() { return listeners_; } |
| |
| // Registers an observer to be notified when an event listener for |
| // |event_name| is added or removed. There can currently be only one observer |
| // for each distinct |event_name|. |
| void RegisterObserver(Observer* observer, const std::string& event_name); |
| |
| // Unregisters an observer from all events. |
| void UnregisterObserver(Observer* observer); |
| |
| // Adds/removes test observers. |
| void AddObserverForTesting(TestObserver* observer); |
| void RemoveObserverForTesting(TestObserver* observer); |
| |
| // Add or remove the extension as having a lazy background page that listens |
| // to the event. The difference from the above methods is that these will be |
| // remembered even after the process goes away. We use this list to decide |
| // which extension pages to load when dispatching an event. |
| void AddLazyEventListener(const std::string& event_name, |
| const ExtensionId& extension_id); |
| void RemoveLazyEventListener(const std::string& event_name, |
| const ExtensionId& extension_id); |
| // Similar to Add/RemoveLazyEventListener, but applies to extension service |
| // workers. |
| void AddLazyServiceWorkerEventListener(const std::string& event_name, |
| const ExtensionId& extension_id, |
| const GURL& service_worker_scope); |
| void RemoveLazyServiceWorkerEventListener(const std::string& event_name, |
| const ExtensionId& extension_id, |
| const GURL& service_worker_scope); |
| |
| // If |add_lazy_listener| is true also add the lazy version of this listener. |
| void AddFilteredEventListener( |
| const std::string& event_name, |
| content::RenderProcessHost* process, |
| const std::string& extension_id, |
| base::Optional<ServiceWorkerIdentifier> sw_identifier, |
| const base::DictionaryValue& filter, |
| bool add_lazy_listener); |
| |
| // If |remove_lazy_listener| is true also remove the lazy version of this |
| // listener. |
| void RemoveFilteredEventListener( |
| const std::string& event_name, |
| content::RenderProcessHost* process, |
| const std::string& extension_id, |
| base::Optional<ServiceWorkerIdentifier> sw_identifier, |
| const base::DictionaryValue& filter, |
| bool remove_lazy_listener); |
| |
| // Returns true if there is at least one listener for the given event. |
| bool HasEventListener(const std::string& event_name) const; |
| |
| // Returns true if the extension is listening to the given event. |
| // (virtual for testing only.) |
| virtual bool ExtensionHasEventListener(const std::string& extension_id, |
| const std::string& event_name) const; |
| |
| // Broadcasts an event to every listener registered for that event. |
| virtual void BroadcastEvent(std::unique_ptr<Event> event); |
| |
| // Dispatches an event to the given extension. |
| virtual void DispatchEventToExtension(const std::string& extension_id, |
| std::unique_ptr<Event> event); |
| |
| // Dispatches |event| to the given extension as if the extension has a lazy |
| // listener for it. NOTE: This should be used rarely, for dispatching events |
| // to extensions that haven't had a chance to add their own listeners yet, eg: |
| // newly installed extensions. |
| void DispatchEventWithLazyListener(const std::string& extension_id, |
| std::unique_ptr<Event> event); |
| |
| // Record the Event Ack from the renderer. (One less event in-flight.) |
| void OnEventAck(content::BrowserContext* context, |
| const std::string& extension_id, |
| const std::string& event_name); |
| |
| // Returns whether or not the given extension has any registered events. |
| bool HasRegisteredEvents(const ExtensionId& extension_id) const; |
| |
| // Clears registered events for testing purposes. |
| void ClearRegisteredEventsForTest(const ExtensionId& extension_id); |
| |
| // Reports UMA for an event dispatched to |extension| with histogram value |
| // |histogram_value|. Must be called on the UI thread. |
| // |
| // |did_enqueue| should be true if the event was queued waiting for a process |
| // to start, like an event page. |
| void ReportEvent(events::HistogramValue histogram_value, |
| const Extension* extension, |
| bool did_enqueue); |
| |
| LazyEventDispatchUtil* lazy_event_dispatch_util() { |
| return &lazy_event_dispatch_util_; |
| } |
| |
| EventAckData* event_ack_data() { return &event_ack_data_; } |
| |
| // Returns true if there is a registered lazy/non-lazy listener for the given |
| // |event_name|. |
| bool HasLazyEventListenerForTesting(const std::string& event_name); |
| bool HasNonLazyEventListenerForTesting(const std::string& event_name); |
| |
| private: |
| friend class EventRouterFilterTest; |
| friend class EventRouterTest; |
| |
| enum class RegisteredEventType { |
| kLazy, |
| kServiceWorker, |
| }; |
| |
| // TODO(gdk): Document this. |
| static void DispatchExtensionMessage( |
| IPC::Sender* ipc_sender, |
| int worker_thread_id, |
| content::BrowserContext* browser_context, |
| const std::string& extension_id, |
| int event_id, |
| const std::string& event_name, |
| base::ListValue* event_args, |
| UserGestureState user_gesture, |
| const extensions::EventFilteringInfo& info); |
| |
| // Returns or sets the list of events for which the given extension has |
| // registered. |
| std::set<std::string> GetRegisteredEvents(const std::string& extension_id, |
| RegisteredEventType type) const; |
| void SetRegisteredEvents(const std::string& extension_id, |
| const std::set<std::string>& events, |
| RegisteredEventType type); |
| |
| // ExtensionRegistryObserver implementation. |
| void OnExtensionLoaded(content::BrowserContext* browser_context, |
| const Extension* extension) override; |
| void OnExtensionUnloaded(content::BrowserContext* browser_context, |
| const Extension* extension, |
| UnloadedExtensionReason reason) override; |
| |
| void AddLazyEventListenerImpl(std::unique_ptr<EventListener> listener, |
| RegisteredEventType type); |
| void RemoveLazyEventListenerImpl(std::unique_ptr<EventListener> listener, |
| RegisteredEventType type); |
| |
| // Shared by all event dispatch methods. If |restrict_to_extension_id| is |
| // empty, the event is broadcast. An event that just came off the pending |
| // list may not be delayed again. |
| void DispatchEventImpl(const std::string& restrict_to_extension_id, |
| std::unique_ptr<Event> event); |
| |
| // Dispatches the event to the specified extension or URL running in |
| // |process|. |
| void DispatchEventToProcess(const std::string& extension_id, |
| const GURL& listener_url, |
| content::RenderProcessHost* process, |
| int64_t service_worker_version_id, |
| int worker_thread_id, |
| Event* event, |
| const base::DictionaryValue* listener_filter, |
| bool did_enqueue); |
| |
| // Adds a filter to an event. |
| void AddFilterToEvent(const std::string& event_name, |
| const std::string& extension_id, |
| bool is_for_service_worker, |
| const base::DictionaryValue* filter); |
| |
| // Removes a filter from an event. |
| void RemoveFilterFromEvent(const std::string& event_name, |
| const std::string& extension_id, |
| bool is_for_service_worker, |
| const base::DictionaryValue* filter); |
| |
| // Returns the dictionary of event filters that the given extension has |
| // registered. |
| const base::DictionaryValue* GetFilteredEvents( |
| const std::string& extension_id, |
| RegisteredEventType type); |
| |
| // Track the dispatched events that have not yet sent an ACK from the |
| // renderer. |
| void IncrementInFlightEvents(content::BrowserContext* context, |
| content::RenderProcessHost* process, |
| const Extension* extension, |
| int event_id, |
| const std::string& event_name, |
| int64_t service_worker_version_id); |
| |
| // static |
| static void DoDispatchEventToSenderBookkeeping( |
| content::BrowserContext* context, |
| const std::string& extension_id, |
| int event_id, |
| int render_process_id, |
| int64_t service_worker_version_id, |
| events::HistogramValue histogram_value, |
| const std::string& event_name); |
| |
| void DispatchPendingEvent( |
| std::unique_ptr<Event> event, |
| std::unique_ptr<LazyContextTaskQueue::ContextInfo> params); |
| |
| // Implementation of EventListenerMap::Delegate. |
| void OnListenerAdded(const EventListener* listener) override; |
| void OnListenerRemoved(const EventListener* listener) override; |
| |
| // RenderProcessHostObserver implementation. |
| void RenderProcessExited( |
| content::RenderProcessHost* host, |
| const content::ChildProcessTerminationInfo& info) override; |
| void RenderProcessHostDestroyed(content::RenderProcessHost* host) override; |
| |
| content::BrowserContext* const browser_context_; |
| |
| // The ExtensionPrefs associated with |browser_context_|. May be NULL in |
| // tests. |
| ExtensionPrefs* const extension_prefs_; |
| |
| ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver> |
| extension_registry_observer_{this}; |
| |
| EventListenerMap listeners_{this}; |
| |
| // Map from base event name to observer. |
| using ObserverMap = std::unordered_map<std::string, Observer*>; |
| ObserverMap observers_; |
| |
| base::ObserverList<TestObserver>::Unchecked test_observers_; |
| |
| std::set<content::RenderProcessHost*> observed_process_set_; |
| |
| LazyEventDispatchUtil lazy_event_dispatch_util_; |
| |
| EventAckData event_ack_data_; |
| |
| base::WeakPtrFactory<EventRouter> weak_factory_{this}; |
| |
| DISALLOW_COPY_AND_ASSIGN(EventRouter); |
| }; |
| |
| struct Event { |
| // This callback should return true if the event should be dispatched to the |
| // given context and extension, and false otherwise. |
| using WillDispatchCallback = |
| base::RepeatingCallback<bool(content::BrowserContext*, |
| Feature::Context, |
| const Extension*, |
| Event*, |
| const base::DictionaryValue*)>; |
| |
| // The identifier for the event, for histograms. In most cases this |
| // correlates 1:1 with |event_name|, in some cases events will generate |
| // their own names, but they cannot generate their own identifier. |
| const events::HistogramValue histogram_value; |
| |
| // The event to dispatch. |
| const std::string event_name; |
| |
| // Arguments to send to the event listener. |
| std::unique_ptr<base::ListValue> event_args; |
| |
| // If non-null, then the event will not be sent to other BrowserContexts |
| // unless the extension has permission (e.g. incognito tab update -> normal |
| // tab only works if extension is allowed incognito access). |
| content::BrowserContext* const restrict_to_browser_context; |
| |
| // If not empty, the event is only sent to extensions with host permissions |
| // for this url. |
| GURL event_url; |
| |
| // Whether a user gesture triggered the event. |
| EventRouter::UserGestureState user_gesture; |
| |
| // Extra information used to filter which events are sent to the listener. |
| EventFilteringInfo filter_info; |
| |
| // If specified, this is called before dispatching an event to each |
| // extension. The third argument is a mutable reference to event_args, |
| // allowing the caller to provide different arguments depending on the |
| // extension and profile. This is guaranteed to be called synchronously with |
| // DispatchEvent, so callers don't need to worry about lifetime. |
| // |
| // NOTE: the Extension argument to this may be NULL because it's possible for |
| // this event to be dispatched to non-extension processes, like WebUI. |
| WillDispatchCallback will_dispatch_callback; |
| |
| // TODO(lazyboy): This sets |restrict_to_browser_context| to nullptr, this |
| // will dispatch the event to unrelated profiles, not just incognito. Audit |
| // and limit usages of this constructor and introduce "include incognito" |
| // option to a constructor version for clients that need to disptach events to |
| // related browser_contexts. See https://crbug.com/726022. |
| Event(events::HistogramValue histogram_value, |
| const std::string& event_name, |
| std::unique_ptr<base::ListValue> event_args); |
| |
| Event(events::HistogramValue histogram_value, |
| const std::string& event_name, |
| std::unique_ptr<base::ListValue> event_args, |
| content::BrowserContext* restrict_to_browser_context); |
| |
| Event(events::HistogramValue histogram_value, |
| const std::string& event_name, |
| std::unique_ptr<base::ListValue> event_args, |
| content::BrowserContext* restrict_to_browser_context, |
| const GURL& event_url, |
| EventRouter::UserGestureState user_gesture, |
| const EventFilteringInfo& info); |
| |
| ~Event(); |
| |
| // Makes a deep copy of this instance. |
| std::unique_ptr<Event> DeepCopy() const; |
| }; |
| |
| struct EventListenerInfo { |
| // Constructor for a listener from a non-ServiceWorker context (background |
| // page, popup, tab, etc) |
| EventListenerInfo(const std::string& event_name, |
| const std::string& extension_id, |
| const GURL& listener_url, |
| content::BrowserContext* browser_context); |
| |
| // Constructor for a listener from a ServiceWorker context. |
| EventListenerInfo(const std::string& event_name, |
| const std::string& extension_id, |
| const GURL& listener_url, |
| content::BrowserContext* browser_context, |
| int worker_thread_id, |
| int64_t service_worker_version_id); |
| |
| // The event name including any sub-event, e.g. "runtime.onStartup" or |
| // "webRequest.onCompleted/123". |
| const std::string event_name; |
| const std::string extension_id; |
| const GURL listener_url; |
| content::BrowserContext* const browser_context; |
| const int worker_thread_id; |
| const int64_t service_worker_version_id; |
| }; |
| |
| } // namespace extensions |
| |
| #endif // EXTENSIONS_BROWSER_EVENT_ROUTER_H_ |