|  | // 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. | 
|  |  | 
|  | #include "extensions/browser/event_router.h" | 
|  |  | 
|  | #include <stddef.h> | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/atomic_sequence_num.h" | 
|  | #include "base/bind.h" | 
|  | #include "base/memory/ptr_util.h" | 
|  | #include "base/message_loop/message_loop.h" | 
|  | #include "base/metrics/histogram_macros.h" | 
|  | #include "base/stl_util.h" | 
|  | #include "base/values.h" | 
|  | #include "content/public/browser/render_process_host.h" | 
|  | #include "extensions/browser/api_activity_monitor.h" | 
|  | #include "extensions/browser/event_router_factory.h" | 
|  | #include "extensions/browser/events/lazy_event_dispatcher.h" | 
|  | #include "extensions/browser/extension_host.h" | 
|  | #include "extensions/browser/extension_prefs.h" | 
|  | #include "extensions/browser/extension_registry.h" | 
|  | #include "extensions/browser/extension_system.h" | 
|  | #include "extensions/browser/extensions_browser_client.h" | 
|  | #include "extensions/browser/process_manager.h" | 
|  | #include "extensions/browser/process_map.h" | 
|  | #include "extensions/common/constants.h" | 
|  | #include "extensions/common/extension.h" | 
|  | #include "extensions/common/extension_api.h" | 
|  | #include "extensions/common/extension_messages.h" | 
|  | #include "extensions/common/extension_urls.h" | 
|  | #include "extensions/common/features/feature.h" | 
|  | #include "extensions/common/features/feature_provider.h" | 
|  | #include "extensions/common/manifest_handlers/background_info.h" | 
|  | #include "extensions/common/manifest_handlers/incognito_info.h" | 
|  | #include "extensions/common/permissions/permissions_data.h" | 
|  |  | 
|  | using base::DictionaryValue; | 
|  | using base::ListValue; | 
|  | using content::BrowserContext; | 
|  | using content::BrowserThread; | 
|  |  | 
|  | namespace extensions { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // A dictionary of event names to lists of filters that this extension has | 
|  | // registered from its lazy background page. | 
|  | const char kFilteredEvents[] = "filtered_events"; | 
|  |  | 
|  | // Similar to |kFilteredEvents|, but applies to extension service worker events. | 
|  | const char kFilteredServiceWorkerEvents[] = "filtered_service_worker_events"; | 
|  |  | 
|  | // Sends a notification about an event to the API activity monitor and the | 
|  | // ExtensionHost for |extension_id| on the UI thread. Can be called from any | 
|  | // thread. | 
|  | void NotifyEventDispatched(void* browser_context_id, | 
|  | const std::string& extension_id, | 
|  | const std::string& event_name, | 
|  | const base::ListValue& args) { | 
|  | // Notify the ApiActivityMonitor about the event dispatch. | 
|  | BrowserContext* context = static_cast<BrowserContext*>(browser_context_id); | 
|  | activity_monitor::OnApiEventDispatched(context, extension_id, event_name, | 
|  | args); | 
|  | } | 
|  |  | 
|  | // A global identifier used to distinguish extension events. | 
|  | base::AtomicSequenceNumber g_extension_event_id; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | const char EventRouter::kRegisteredLazyEvents[] = "events"; | 
|  | const char EventRouter::kRegisteredServiceWorkerEvents[] = | 
|  | "serviceworkerevents"; | 
|  |  | 
|  | // static | 
|  | void EventRouter::DispatchExtensionMessage(IPC::Sender* ipc_sender, | 
|  | int worker_thread_id, | 
|  | void* browser_context_id, | 
|  | const std::string& extension_id, | 
|  | int event_id, | 
|  | const std::string& event_name, | 
|  | ListValue* event_args, | 
|  | UserGestureState user_gesture, | 
|  | const EventFilteringInfo& info) { | 
|  | NotifyEventDispatched(browser_context_id, extension_id, event_name, | 
|  | *event_args); | 
|  |  | 
|  | ExtensionMsg_DispatchEvent_Params params; | 
|  | params.worker_thread_id = worker_thread_id; | 
|  | params.extension_id = extension_id; | 
|  | params.event_name = event_name; | 
|  | params.event_id = event_id; | 
|  | params.is_user_gesture = user_gesture == USER_GESTURE_ENABLED; | 
|  | params.filtering_info = info; | 
|  |  | 
|  | ipc_sender->Send(new ExtensionMsg_DispatchEvent(params, *event_args)); | 
|  | } | 
|  |  | 
|  | // static | 
|  | EventRouter* EventRouter::Get(content::BrowserContext* browser_context) { | 
|  | return EventRouterFactory::GetForBrowserContext(browser_context); | 
|  | } | 
|  |  | 
|  | // static | 
|  | std::string EventRouter::GetBaseEventName(const std::string& full_event_name) { | 
|  | size_t slash_sep = full_event_name.find('/'); | 
|  | return full_event_name.substr(0, slash_sep); | 
|  | } | 
|  |  | 
|  | // static | 
|  | void EventRouter::DispatchEventToSender(IPC::Sender* ipc_sender, | 
|  | void* browser_context_id, | 
|  | const std::string& extension_id, | 
|  | events::HistogramValue histogram_value, | 
|  | const std::string& event_name, | 
|  | std::unique_ptr<ListValue> event_args, | 
|  | UserGestureState user_gesture, | 
|  | const EventFilteringInfo& info) { | 
|  | int event_id = g_extension_event_id.GetNext(); | 
|  |  | 
|  | if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { | 
|  | DoDispatchEventToSenderBookkeepingOnUI(browser_context_id, extension_id, | 
|  | event_id, histogram_value, | 
|  | event_name); | 
|  | } else { | 
|  | // This is called from WebRequest API. | 
|  | // TODO(lazyboy): Skip this entirely: http://crbug.com/488747. | 
|  | BrowserThread::PostTask( | 
|  | BrowserThread::UI, FROM_HERE, | 
|  | base::Bind(&EventRouter::DoDispatchEventToSenderBookkeepingOnUI, | 
|  | browser_context_id, extension_id, event_id, histogram_value, | 
|  | event_name)); | 
|  | } | 
|  |  | 
|  | DispatchExtensionMessage(ipc_sender, | 
|  | // TODO(lazyboy): |kMainThreadId| means these | 
|  | // will not work for extension SW. | 
|  | kMainThreadId, browser_context_id, extension_id, | 
|  | event_id, event_name, event_args.get(), user_gesture, | 
|  | info); | 
|  | } | 
|  |  | 
|  | // static. | 
|  | bool EventRouter::CanDispatchEventToBrowserContext(BrowserContext* context, | 
|  | const Extension* extension, | 
|  | const Event& event) { | 
|  | // Is this event from a different browser context than the renderer (ie, an | 
|  | // incognito tab event sent to a normal process, or vice versa). | 
|  | bool crosses_incognito = event.restrict_to_browser_context && | 
|  | context != event.restrict_to_browser_context; | 
|  | if (!crosses_incognito) | 
|  | return true; | 
|  | return ExtensionsBrowserClient::Get()->CanExtensionCrossIncognito(extension, | 
|  | context); | 
|  | } | 
|  |  | 
|  | EventRouter::EventRouter(BrowserContext* browser_context, | 
|  | ExtensionPrefs* extension_prefs) | 
|  | : browser_context_(browser_context), | 
|  | extension_prefs_(extension_prefs), | 
|  | extension_registry_observer_(this), | 
|  | listeners_(this), | 
|  | lazy_event_dispatch_util_(browser_context_), | 
|  | weak_factory_(this) { | 
|  | extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_)); | 
|  | } | 
|  |  | 
|  | EventRouter::~EventRouter() { | 
|  | for (auto* process : observed_process_set_) | 
|  | process->RemoveObserver(this); | 
|  | } | 
|  |  | 
|  | void EventRouter::AddEventListener(const std::string& event_name, | 
|  | content::RenderProcessHost* process, | 
|  | const std::string& extension_id) { | 
|  | listeners_.AddListener( | 
|  | EventListener::ForExtension(event_name, extension_id, process, nullptr)); | 
|  | } | 
|  |  | 
|  | void EventRouter::AddServiceWorkerEventListener( | 
|  | const std::string& event_name, | 
|  | content::RenderProcessHost* process, | 
|  | const ExtensionId& extension_id, | 
|  | const GURL& service_worker_scope, | 
|  | int worker_thread_id) { | 
|  | listeners_.AddListener(EventListener::ForExtensionServiceWorker( | 
|  | event_name, extension_id, process, service_worker_scope, worker_thread_id, | 
|  | nullptr)); | 
|  | } | 
|  |  | 
|  | void EventRouter::RemoveEventListener(const std::string& event_name, | 
|  | content::RenderProcessHost* process, | 
|  | const std::string& extension_id) { | 
|  | std::unique_ptr<EventListener> listener = | 
|  | EventListener::ForExtension(event_name, extension_id, process, nullptr); | 
|  | listeners_.RemoveListener(listener.get()); | 
|  | } | 
|  |  | 
|  | void EventRouter::RemoveServiceWorkerEventListener( | 
|  | const std::string& event_name, | 
|  | content::RenderProcessHost* process, | 
|  | const ExtensionId& extension_id, | 
|  | const GURL& service_worker_scope, | 
|  | int worker_thread_id) { | 
|  | std::unique_ptr<EventListener> listener = | 
|  | EventListener::ForExtensionServiceWorker(event_name, extension_id, | 
|  | process, service_worker_scope, | 
|  | worker_thread_id, nullptr); | 
|  | listeners_.RemoveListener(listener.get()); | 
|  | } | 
|  |  | 
|  | void EventRouter::AddEventListenerForURL(const std::string& event_name, | 
|  | content::RenderProcessHost* process, | 
|  | const GURL& listener_url) { | 
|  | listeners_.AddListener( | 
|  | EventListener::ForURL(event_name, listener_url, process, nullptr)); | 
|  | } | 
|  |  | 
|  | void EventRouter::RemoveEventListenerForURL(const std::string& event_name, | 
|  | content::RenderProcessHost* process, | 
|  | const GURL& listener_url) { | 
|  | std::unique_ptr<EventListener> listener = | 
|  | EventListener::ForURL(event_name, listener_url, process, nullptr); | 
|  | listeners_.RemoveListener(listener.get()); | 
|  | } | 
|  |  | 
|  | void EventRouter::RegisterObserver(Observer* observer, | 
|  | const std::string& event_name) { | 
|  | // Observing sub-event names like "foo.onBar/123" is not allowed. | 
|  | DCHECK(event_name.find('/') == std::string::npos); | 
|  | observers_[event_name] = observer; | 
|  | } | 
|  |  | 
|  | void EventRouter::UnregisterObserver(Observer* observer) { | 
|  | for (ObserverMap::iterator it = observers_.begin(); it != observers_.end();) { | 
|  | if (it->second == observer) | 
|  | it = observers_.erase(it); | 
|  | else | 
|  | ++it; | 
|  | } | 
|  | } | 
|  |  | 
|  | void EventRouter::OnListenerAdded(const EventListener* listener) { | 
|  | const EventListenerInfo details(listener->event_name(), | 
|  | listener->extension_id(), | 
|  | listener->listener_url(), | 
|  | listener->GetBrowserContext()); | 
|  | std::string base_event_name = GetBaseEventName(listener->event_name()); | 
|  | ObserverMap::iterator observer = observers_.find(base_event_name); | 
|  | if (observer != observers_.end()) | 
|  | observer->second->OnListenerAdded(details); | 
|  |  | 
|  | content::RenderProcessHost* process = listener->process(); | 
|  | if (process) { | 
|  | bool inserted = observed_process_set_.insert(process).second; | 
|  | if (inserted) | 
|  | process->AddObserver(this); | 
|  | } | 
|  | } | 
|  |  | 
|  | void EventRouter::OnListenerRemoved(const EventListener* listener) { | 
|  | const EventListenerInfo details(listener->event_name(), | 
|  | listener->extension_id(), | 
|  | listener->listener_url(), | 
|  | listener->GetBrowserContext()); | 
|  | std::string base_event_name = GetBaseEventName(listener->event_name()); | 
|  | ObserverMap::iterator observer = observers_.find(base_event_name); | 
|  | if (observer != observers_.end()) | 
|  | observer->second->OnListenerRemoved(details); | 
|  | } | 
|  |  | 
|  | void EventRouter::RenderProcessExited(content::RenderProcessHost* host, | 
|  | base::TerminationStatus status, | 
|  | int exit_code) { | 
|  | listeners_.RemoveListenersForProcess(host); | 
|  | observed_process_set_.erase(host); | 
|  | host->RemoveObserver(this); | 
|  | } | 
|  |  | 
|  | void EventRouter::RenderProcessHostDestroyed(content::RenderProcessHost* host) { | 
|  | listeners_.RemoveListenersForProcess(host); | 
|  | observed_process_set_.erase(host); | 
|  | host->RemoveObserver(this); | 
|  | } | 
|  |  | 
|  | void EventRouter::AddLazyEventListener(const std::string& event_name, | 
|  | const ExtensionId& extension_id) { | 
|  | AddLazyEventListenerImpl( | 
|  | EventListener::ForExtension(event_name, extension_id, nullptr, nullptr), | 
|  | RegisteredEventType::kLazy); | 
|  | } | 
|  |  | 
|  | void EventRouter::RemoveLazyEventListener(const std::string& event_name, | 
|  | const ExtensionId& extension_id) { | 
|  | RemoveLazyEventListenerImpl( | 
|  | EventListener::ForExtension(event_name, extension_id, nullptr, nullptr), | 
|  | RegisteredEventType::kLazy); | 
|  | } | 
|  |  | 
|  | void EventRouter::AddLazyServiceWorkerEventListener( | 
|  | const std::string& event_name, | 
|  | const ExtensionId& extension_id, | 
|  | const GURL& service_worker_scope) { | 
|  | std::unique_ptr<EventListener> listener = | 
|  | EventListener::ForExtensionServiceWorker( | 
|  | event_name, extension_id, nullptr, service_worker_scope, | 
|  | kMainThreadId,  // Lazy, without worker thread id. | 
|  | nullptr); | 
|  | AddLazyEventListenerImpl(std::move(listener), | 
|  | RegisteredEventType::kServiceWorker); | 
|  | } | 
|  |  | 
|  | void EventRouter::RemoveLazyServiceWorkerEventListener( | 
|  | const std::string& event_name, | 
|  | const ExtensionId& extension_id, | 
|  | const GURL& service_worker_scope) { | 
|  | std::unique_ptr<EventListener> listener = | 
|  | EventListener::ForExtensionServiceWorker( | 
|  | event_name, extension_id, nullptr, service_worker_scope, | 
|  | kMainThreadId,  // Lazy, without worker thread id. | 
|  | nullptr); | 
|  | RemoveLazyEventListenerImpl(std::move(listener), | 
|  | RegisteredEventType::kServiceWorker); | 
|  | } | 
|  |  | 
|  | void EventRouter::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) { | 
|  | const bool is_for_service_worker = sw_identifier.has_value(); | 
|  | listeners_.AddListener( | 
|  | is_for_service_worker | 
|  | ? EventListener::ForExtensionServiceWorker( | 
|  | event_name, extension_id, process, sw_identifier->scope, | 
|  | sw_identifier->thread_id, filter.CreateDeepCopy()) | 
|  | : EventListener::ForExtension(event_name, extension_id, process, | 
|  | filter.CreateDeepCopy())); | 
|  |  | 
|  | if (!add_lazy_listener) | 
|  | return; | 
|  |  | 
|  | bool added = listeners_.AddListener( | 
|  | is_for_service_worker | 
|  | ? EventListener::ForExtensionServiceWorker( | 
|  | event_name, extension_id, nullptr, sw_identifier->scope, | 
|  | kMainThreadId,  // Lazy, without worker thread id. | 
|  | filter.CreateDeepCopy()) | 
|  | : EventListener::ForExtension(event_name, extension_id, | 
|  | nullptr,  // Lazy, without process. | 
|  | filter.CreateDeepCopy())); | 
|  | if (added) | 
|  | AddFilterToEvent(event_name, extension_id, is_for_service_worker, &filter); | 
|  | } | 
|  |  | 
|  | void EventRouter::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) { | 
|  | const bool is_for_service_worker = sw_identifier.has_value(); | 
|  | std::unique_ptr<EventListener> listener = | 
|  | is_for_service_worker | 
|  | ? EventListener::ForExtensionServiceWorker( | 
|  | event_name, extension_id, process, sw_identifier->scope, | 
|  | sw_identifier->thread_id, filter.CreateDeepCopy()) | 
|  | : EventListener::ForExtension(event_name, extension_id, process, | 
|  | filter.CreateDeepCopy()); | 
|  |  | 
|  | listeners_.RemoveListener(listener.get()); | 
|  |  | 
|  | if (remove_lazy_listener) { | 
|  | listener->MakeLazy(); | 
|  | bool removed = listeners_.RemoveListener(listener.get()); | 
|  |  | 
|  | if (removed) { | 
|  | RemoveFilterFromEvent(event_name, extension_id, is_for_service_worker, | 
|  | &filter); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | bool EventRouter::HasEventListener(const std::string& event_name) const { | 
|  | return listeners_.HasListenerForEvent(event_name); | 
|  | } | 
|  |  | 
|  | bool EventRouter::ExtensionHasEventListener( | 
|  | const std::string& extension_id, | 
|  | const std::string& event_name) const { | 
|  | return listeners_.HasListenerForExtension(extension_id, event_name); | 
|  | } | 
|  |  | 
|  | std::set<std::string> EventRouter::GetRegisteredEvents( | 
|  | const std::string& extension_id, | 
|  | RegisteredEventType type) const { | 
|  | std::set<std::string> events; | 
|  | const ListValue* events_value = NULL; | 
|  |  | 
|  | const char* pref_key = type == RegisteredEventType::kLazy | 
|  | ? kRegisteredLazyEvents | 
|  | : kRegisteredServiceWorkerEvents; | 
|  | if (!extension_prefs_ || !extension_prefs_->ReadPrefAsList( | 
|  | extension_id, pref_key, &events_value)) { | 
|  | return events; | 
|  | } | 
|  |  | 
|  | for (size_t i = 0; i < events_value->GetSize(); ++i) { | 
|  | std::string event; | 
|  | if (events_value->GetString(i, &event)) | 
|  | events.insert(event); | 
|  | } | 
|  | return events; | 
|  | } | 
|  |  | 
|  | void EventRouter::ClearRegisteredEventsForTest( | 
|  | const ExtensionId& extension_id) { | 
|  | SetRegisteredEvents(extension_id, std::set<std::string>(), | 
|  | RegisteredEventType::kLazy); | 
|  | SetRegisteredEvents(extension_id, std::set<std::string>(), | 
|  | RegisteredEventType::kServiceWorker); | 
|  | } | 
|  |  | 
|  | bool EventRouter::HasLazyEventListenerForTesting( | 
|  | const std::string& event_name) { | 
|  | const EventListenerMap::ListenerList& listeners = | 
|  | listeners_.GetEventListenersByName(event_name); | 
|  | return std::any_of(listeners.begin(), listeners.end(), | 
|  | [](const std::unique_ptr<EventListener>& listener) { | 
|  | return listener->IsLazy(); | 
|  | }); | 
|  | } | 
|  |  | 
|  | bool EventRouter::HasNonLazyEventListenerForTesting( | 
|  | const std::string& event_name) { | 
|  | const EventListenerMap::ListenerList& listeners = | 
|  | listeners_.GetEventListenersByName(event_name); | 
|  | return std::any_of(listeners.begin(), listeners.end(), | 
|  | [](const std::unique_ptr<EventListener>& listener) { | 
|  | return !listener->IsLazy(); | 
|  | }); | 
|  | } | 
|  |  | 
|  | void EventRouter::RemoveFilterFromEvent(const std::string& event_name, | 
|  | const std::string& extension_id, | 
|  | bool is_for_service_worker, | 
|  | const DictionaryValue* filter) { | 
|  | ExtensionPrefs::ScopedDictionaryUpdate update( | 
|  | extension_prefs_, extension_id, | 
|  | is_for_service_worker ? kFilteredServiceWorkerEvents : kFilteredEvents); | 
|  | auto filtered_events = update.Create(); | 
|  | ListValue* filter_list = NULL; | 
|  | if (!filtered_events || | 
|  | !filtered_events->GetListWithoutPathExpansion(event_name, &filter_list)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | for (size_t i = 0; i < filter_list->GetSize(); i++) { | 
|  | DictionaryValue* filter_value = nullptr; | 
|  | CHECK(filter_list->GetDictionary(i, &filter_value)); | 
|  | if (filter_value->Equals(filter)) { | 
|  | filter_list->Remove(i, nullptr); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | const DictionaryValue* EventRouter::GetFilteredEvents( | 
|  | const std::string& extension_id, | 
|  | RegisteredEventType type) { | 
|  | const DictionaryValue* events = nullptr; | 
|  | const char* pref_key = type == RegisteredEventType::kLazy | 
|  | ? kFilteredEvents | 
|  | : kFilteredServiceWorkerEvents; | 
|  | extension_prefs_->ReadPrefAsDictionary(extension_id, pref_key, &events); | 
|  | return events; | 
|  | } | 
|  |  | 
|  | void EventRouter::BroadcastEvent(std::unique_ptr<Event> event) { | 
|  | DispatchEventImpl(std::string(), linked_ptr<Event>(event.release())); | 
|  | } | 
|  |  | 
|  | void EventRouter::DispatchEventToExtension(const std::string& extension_id, | 
|  | std::unique_ptr<Event> event) { | 
|  | DCHECK(!extension_id.empty()); | 
|  | DispatchEventImpl(extension_id, linked_ptr<Event>(event.release())); | 
|  | } | 
|  |  | 
|  | void EventRouter::DispatchEventWithLazyListener(const std::string& extension_id, | 
|  | std::unique_ptr<Event> event) { | 
|  | DCHECK(!extension_id.empty()); | 
|  | std::string event_name = event->event_name; | 
|  | bool has_listener = ExtensionHasEventListener(extension_id, event_name); | 
|  | if (!has_listener) | 
|  | AddLazyEventListener(event_name, extension_id); | 
|  | DispatchEventToExtension(extension_id, std::move(event)); | 
|  | if (!has_listener) | 
|  | RemoveLazyEventListener(event_name, extension_id); | 
|  | } | 
|  |  | 
|  | void EventRouter::DispatchEventImpl(const std::string& restrict_to_extension_id, | 
|  | const linked_ptr<Event>& event) { | 
|  | // We don't expect to get events from a completely different browser context. | 
|  | DCHECK(!event->restrict_to_browser_context || | 
|  | ExtensionsBrowserClient::Get()->IsSameContext( | 
|  | browser_context_, event->restrict_to_browser_context)); | 
|  | std::set<const EventListener*> listeners( | 
|  | listeners_.GetEventListeners(*event)); | 
|  |  | 
|  | LazyEventDispatcher lazy_event_dispatcher( | 
|  | browser_context_, event, | 
|  | base::Bind(&EventRouter::DispatchPendingEvent, | 
|  | weak_factory_.GetWeakPtr())); | 
|  |  | 
|  | // We dispatch events for lazy background pages first because attempting to do | 
|  | // so will cause those that are being suspended to cancel that suspension. | 
|  | // As canceling a suspension entails sending an event to the affected | 
|  | // background page, and as that event needs to be delivered before we dispatch | 
|  | // the event we are dispatching here, we dispatch to the lazy listeners here | 
|  | // first. | 
|  | for (const EventListener* listener : listeners) { | 
|  | if (!restrict_to_extension_id.empty() && | 
|  | restrict_to_extension_id != listener->extension_id()) { | 
|  | continue; | 
|  | } | 
|  | if (listener->IsLazy()) { | 
|  | if (listener->is_for_service_worker()) { | 
|  | lazy_event_dispatcher.DispatchToServiceWorker(listener->extension_id(), | 
|  | listener->listener_url(), | 
|  | listener->filter()); | 
|  | } else { | 
|  | lazy_event_dispatcher.DispatchToEventPage(listener->extension_id(), | 
|  | listener->filter()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | for (const EventListener* listener : listeners) { | 
|  | if (!restrict_to_extension_id.empty() && | 
|  | restrict_to_extension_id != listener->extension_id()) { | 
|  | continue; | 
|  | } | 
|  | if (!listener->process()) | 
|  | continue; | 
|  | if (lazy_event_dispatcher.HasAlreadyDispatched( | 
|  | listener->process()->GetBrowserContext(), listener)) { | 
|  | continue; | 
|  | } | 
|  | DispatchEventToProcess(listener->extension_id(), listener->listener_url(), | 
|  | listener->process(), listener->worker_thread_id(), | 
|  | event, listener->filter(), false /* did_enqueue */); | 
|  | } | 
|  | } | 
|  |  | 
|  | void EventRouter::DispatchEventToProcess( | 
|  | const std::string& extension_id, | 
|  | const GURL& listener_url, | 
|  | content::RenderProcessHost* process, | 
|  | int worker_thread_id, | 
|  | const linked_ptr<Event>& event, | 
|  | const base::DictionaryValue* listener_filter, | 
|  | bool did_enqueue) { | 
|  | BrowserContext* listener_context = process->GetBrowserContext(); | 
|  | ProcessMap* process_map = ProcessMap::Get(listener_context); | 
|  |  | 
|  | // NOTE: |extension| being NULL does not necessarily imply that this event | 
|  | // shouldn't be dispatched. Events can be dispatched to WebUI and webviews as | 
|  | // well.  It all depends on what GetMostLikelyContextType returns. | 
|  | const Extension* extension = | 
|  | ExtensionRegistry::Get(browser_context_)->enabled_extensions().GetByID( | 
|  | extension_id); | 
|  |  | 
|  | if (!extension && !extension_id.empty()) { | 
|  | // Trying to dispatch an event to an extension that doesn't exist. The | 
|  | // extension could have been removed, but we do not unregister it until the | 
|  | // extension process is unloaded. | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (extension) { | 
|  | // Extension-specific checks. | 
|  | // Firstly, if the event is for a URL, the Extension must have permission | 
|  | // to access that URL. | 
|  | if (!event->event_url.is_empty() && | 
|  | event->event_url.host() != extension->id() &&  // event for self is ok | 
|  | !extension->permissions_data() | 
|  | ->active_permissions() | 
|  | .HasEffectiveAccessToURL(event->event_url)) { | 
|  | return; | 
|  | } | 
|  | // Secondly, if the event is for incognito mode, the Extension must be | 
|  | // enabled in incognito mode. | 
|  | if (!CanDispatchEventToBrowserContext(listener_context, extension, | 
|  | *event)) { | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | Feature::Context target_context = | 
|  | process_map->GetMostLikelyContextType(extension, process->GetID()); | 
|  |  | 
|  | // We shouldn't be dispatching an event to a webpage, since all such events | 
|  | // (e.g.  messaging) don't go through EventRouter. | 
|  | DCHECK_NE(Feature::WEB_PAGE_CONTEXT, target_context) | 
|  | << "Trying to dispatch event " << event->event_name << " to a webpage," | 
|  | << " but this shouldn't be possible"; | 
|  |  | 
|  | Feature::Availability availability = | 
|  | ExtensionAPI::GetSharedInstance()->IsAvailable( | 
|  | event->event_name, extension, target_context, listener_url, | 
|  | CheckAliasStatus::ALLOWED); | 
|  | if (!availability.is_available()) { | 
|  | // It shouldn't be possible to reach here, because access is checked on | 
|  | // registration. However, for paranoia, check on dispatch as well. | 
|  | NOTREACHED() << "Trying to dispatch event " << event->event_name | 
|  | << " which the target does not have access to: " | 
|  | << availability.message(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!event->will_dispatch_callback.is_null() && | 
|  | !event->will_dispatch_callback.Run(listener_context, extension, | 
|  | event.get(), listener_filter)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | int event_id = g_extension_event_id.GetNext(); | 
|  | DispatchExtensionMessage(process, worker_thread_id, listener_context, | 
|  | extension_id, event_id, event->event_name, | 
|  | event->event_args.get(), event->user_gesture, | 
|  | event->filter_info); | 
|  |  | 
|  | // TODO(lazyboy): This is wrong for extensions SW events. We need to: | 
|  | // 1. Increment worker ref count | 
|  | // 2. Add EventAck IPC to decrement that ref count. | 
|  | if (extension) { | 
|  | ReportEvent(event->histogram_value, extension, did_enqueue); | 
|  | IncrementInFlightEvents(listener_context, extension, event_id, | 
|  | event->event_name); | 
|  | } | 
|  | } | 
|  |  | 
|  | // static | 
|  | void EventRouter::DoDispatchEventToSenderBookkeepingOnUI( | 
|  | void* browser_context_id, | 
|  | const std::string& extension_id, | 
|  | int event_id, | 
|  | events::HistogramValue histogram_value, | 
|  | const std::string& event_name) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  | BrowserContext* browser_context = | 
|  | reinterpret_cast<BrowserContext*>(browser_context_id); | 
|  | if (!ExtensionsBrowserClient::Get()->IsValidContext(browser_context)) | 
|  | return; | 
|  | const Extension* extension = | 
|  | ExtensionRegistry::Get(browser_context)->enabled_extensions().GetByID( | 
|  | extension_id); | 
|  | if (!extension) | 
|  | return; | 
|  | EventRouter* event_router = EventRouter::Get(browser_context); | 
|  | event_router->IncrementInFlightEvents(browser_context, extension, event_id, | 
|  | event_name); | 
|  | event_router->ReportEvent(histogram_value, extension, | 
|  | false /* did_enqueue */); | 
|  | } | 
|  |  | 
|  | void EventRouter::IncrementInFlightEvents(BrowserContext* context, | 
|  | const Extension* extension, | 
|  | int event_id, | 
|  | const std::string& event_name) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  |  | 
|  | // Only increment in-flight events if the lazy background page is active, | 
|  | // because that's the only time we'll get an ACK. | 
|  | if (BackgroundInfo::HasLazyBackgroundPage(extension)) { | 
|  | ProcessManager* pm = ProcessManager::Get(context); | 
|  | ExtensionHost* host = pm->GetBackgroundHostForExtension(extension->id()); | 
|  | if (host) { | 
|  | pm->IncrementLazyKeepaliveCount(extension); | 
|  | host->OnBackgroundEventDispatched(event_name, event_id); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void EventRouter::OnEventAck(BrowserContext* context, | 
|  | const std::string& extension_id) { | 
|  | ProcessManager* pm = ProcessManager::Get(context); | 
|  | ExtensionHost* host = pm->GetBackgroundHostForExtension(extension_id); | 
|  | // The event ACK is routed to the background host, so this should never be | 
|  | // NULL. | 
|  | CHECK(host); | 
|  | // TODO(mpcomplete): We should never get this message unless | 
|  | // HasLazyBackgroundPage is true. Find out why we're getting it anyway. | 
|  | if (host->extension() && | 
|  | BackgroundInfo::HasLazyBackgroundPage(host->extension())) | 
|  | pm->DecrementLazyKeepaliveCount(host->extension()); | 
|  | } | 
|  |  | 
|  | bool EventRouter::HasRegisteredEvents(const ExtensionId& extension_id) const { | 
|  | return !GetRegisteredEvents(extension_id, RegisteredEventType::kLazy) | 
|  | .empty() || | 
|  | !GetRegisteredEvents(extension_id, RegisteredEventType::kServiceWorker) | 
|  | .empty(); | 
|  | } | 
|  |  | 
|  | void EventRouter::ReportEvent(events::HistogramValue histogram_value, | 
|  | const Extension* extension, | 
|  | bool did_enqueue) { | 
|  | DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 
|  |  | 
|  | // Record every event fired. | 
|  | UMA_HISTOGRAM_ENUMERATION("Extensions.Events.Dispatch", histogram_value, | 
|  | events::ENUM_BOUNDARY); | 
|  |  | 
|  | bool is_component = Manifest::IsComponentLocation(extension->location()); | 
|  |  | 
|  | // Record events for component extensions. These should be kept to a minimum, | 
|  | // especially if they wake its event page. Component extensions should use | 
|  | // declarative APIs as much as possible. | 
|  | if (is_component) { | 
|  | UMA_HISTOGRAM_ENUMERATION("Extensions.Events.DispatchToComponent", | 
|  | histogram_value, events::ENUM_BOUNDARY); | 
|  | } | 
|  |  | 
|  | // Record events for background pages, if any. The most important statistic | 
|  | // is DispatchWithSuspendedEventPage. Events reported there woke an event | 
|  | // page. Implementing either filtered or declarative versions of these events | 
|  | // should be prioritised. | 
|  | // | 
|  | // Note: all we know is that the extension *has* a persistent or event page, | 
|  | // not that the event is being dispatched *to* such a page. However, this is | 
|  | // academic, since extensions with any background page have that background | 
|  | // page running (or in the case of suspended event pages, must be started) | 
|  | // regardless of where the event is being dispatched. Events are dispatched | 
|  | // to a *process* not a *frame*. | 
|  | if (BackgroundInfo::HasPersistentBackgroundPage(extension)) { | 
|  | UMA_HISTOGRAM_ENUMERATION( | 
|  | "Extensions.Events.DispatchWithPersistentBackgroundPage", | 
|  | histogram_value, events::ENUM_BOUNDARY); | 
|  | } else if (BackgroundInfo::HasLazyBackgroundPage(extension)) { | 
|  | if (did_enqueue) { | 
|  | UMA_HISTOGRAM_ENUMERATION( | 
|  | "Extensions.Events.DispatchWithSuspendedEventPage", histogram_value, | 
|  | events::ENUM_BOUNDARY); | 
|  | if (is_component) { | 
|  | UMA_HISTOGRAM_ENUMERATION( | 
|  | "Extensions.Events.DispatchToComponentWithSuspendedEventPage", | 
|  | histogram_value, events::ENUM_BOUNDARY); | 
|  | } | 
|  | } else { | 
|  | UMA_HISTOGRAM_ENUMERATION( | 
|  | "Extensions.Events.DispatchWithRunningEventPage", histogram_value, | 
|  | events::ENUM_BOUNDARY); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void EventRouter::DispatchPendingEvent( | 
|  | const linked_ptr<Event>& event, | 
|  | std::unique_ptr<LazyContextTaskQueue::ContextInfo> params) { | 
|  | if (!params) | 
|  | return; | 
|  |  | 
|  | if (listeners_.HasProcessListener(params->render_process_host, | 
|  | params->worker_thread_id, | 
|  | params->extension_id)) { | 
|  | DispatchEventToProcess( | 
|  | params->extension_id, params->url, params->render_process_host, | 
|  | params->worker_thread_id, event, nullptr, true /* did_enqueue */); | 
|  | } | 
|  | } | 
|  |  | 
|  | void EventRouter::SetRegisteredEvents(const std::string& extension_id, | 
|  | const std::set<std::string>& events, | 
|  | RegisteredEventType type) { | 
|  | auto events_value = std::make_unique<base::ListValue>(); | 
|  | for (std::set<std::string>::const_iterator iter = events.begin(); | 
|  | iter != events.end(); ++iter) { | 
|  | events_value->AppendString(*iter); | 
|  | } | 
|  | const char* pref_key = type == RegisteredEventType::kLazy | 
|  | ? kRegisteredLazyEvents | 
|  | : kRegisteredServiceWorkerEvents; | 
|  | extension_prefs_->UpdateExtensionPref(extension_id, pref_key, | 
|  | std::move(events_value)); | 
|  | } | 
|  |  | 
|  | void EventRouter::AddFilterToEvent(const std::string& event_name, | 
|  | const std::string& extension_id, | 
|  | bool is_for_service_worker, | 
|  | const DictionaryValue* filter) { | 
|  | ExtensionPrefs::ScopedDictionaryUpdate update( | 
|  | extension_prefs_, extension_id, | 
|  | is_for_service_worker ? kFilteredServiceWorkerEvents : kFilteredEvents); | 
|  | auto filtered_events = update.Create(); | 
|  |  | 
|  | ListValue* filter_list = nullptr; | 
|  | if (!filtered_events->GetListWithoutPathExpansion(event_name, &filter_list)) { | 
|  | filtered_events->SetWithoutPathExpansion( | 
|  | event_name, std::make_unique<base::ListValue>()); | 
|  | filtered_events->GetListWithoutPathExpansion(event_name, &filter_list); | 
|  | } | 
|  |  | 
|  | filter_list->Append(filter->CreateDeepCopy()); | 
|  | } | 
|  |  | 
|  | void EventRouter::OnExtensionLoaded(content::BrowserContext* browser_context, | 
|  | const Extension* extension) { | 
|  | // Add all registered lazy listeners to our cache. | 
|  | std::set<std::string> registered_events = | 
|  | GetRegisteredEvents(extension->id(), RegisteredEventType::kLazy); | 
|  | listeners_.LoadUnfilteredLazyListeners(extension->id(), registered_events); | 
|  |  | 
|  | std::set<std::string> registered_worker_events = | 
|  | GetRegisteredEvents(extension->id(), RegisteredEventType::kServiceWorker); | 
|  | listeners_.LoadUnfilteredWorkerListeners(extension->id(), | 
|  | registered_worker_events); | 
|  |  | 
|  | const DictionaryValue* filtered_events = | 
|  | GetFilteredEvents(extension->id(), RegisteredEventType::kLazy); | 
|  | if (filtered_events) | 
|  | listeners_.LoadFilteredLazyListeners( | 
|  | extension->id(), false /* is_for_service_worker */, *filtered_events); | 
|  |  | 
|  | const DictionaryValue* filtered_worker_events = | 
|  | GetFilteredEvents(extension->id(), RegisteredEventType::kServiceWorker); | 
|  | if (filtered_worker_events) | 
|  | listeners_.LoadFilteredLazyListeners(extension->id(), | 
|  | true /* is_for_service_worker */, | 
|  | *filtered_worker_events); | 
|  | } | 
|  |  | 
|  | void EventRouter::OnExtensionUnloaded(content::BrowserContext* browser_context, | 
|  | const Extension* extension, | 
|  | UnloadedExtensionReason reason) { | 
|  | // Remove all registered listeners from our cache. | 
|  | listeners_.RemoveListenersForExtension(extension->id()); | 
|  | } | 
|  |  | 
|  | void EventRouter::AddLazyEventListenerImpl( | 
|  | std::unique_ptr<EventListener> listener, | 
|  | RegisteredEventType type) { | 
|  | const ExtensionId extension_id = listener->extension_id(); | 
|  | const std::string event_name = listener->event_name(); | 
|  | bool is_new = listeners_.AddListener(std::move(listener)); | 
|  | if (is_new) { | 
|  | std::set<std::string> events = GetRegisteredEvents(extension_id, type); | 
|  | bool prefs_is_new = events.insert(event_name).second; | 
|  | if (prefs_is_new) | 
|  | SetRegisteredEvents(extension_id, events, type); | 
|  | } | 
|  | } | 
|  |  | 
|  | void EventRouter::RemoveLazyEventListenerImpl( | 
|  | std::unique_ptr<EventListener> listener, | 
|  | RegisteredEventType type) { | 
|  | const ExtensionId extension_id = listener->extension_id(); | 
|  | const std::string event_name = listener->event_name(); | 
|  | bool did_exist = listeners_.RemoveListener(listener.get()); | 
|  | if (did_exist) { | 
|  | std::set<std::string> events = GetRegisteredEvents(extension_id, type); | 
|  | bool prefs_did_exist = events.erase(event_name) > 0; | 
|  | DCHECK(prefs_did_exist); | 
|  | SetRegisteredEvents(extension_id, events, type); | 
|  | } | 
|  | } | 
|  |  | 
|  | Event::Event(events::HistogramValue histogram_value, | 
|  | const std::string& event_name, | 
|  | std::unique_ptr<base::ListValue> event_args) | 
|  | : Event(histogram_value, event_name, std::move(event_args), nullptr) {} | 
|  |  | 
|  | Event::Event(events::HistogramValue histogram_value, | 
|  | const std::string& event_name, | 
|  | std::unique_ptr<base::ListValue> event_args, | 
|  | BrowserContext* restrict_to_browser_context) | 
|  | : Event(histogram_value, | 
|  | event_name, | 
|  | std::move(event_args), | 
|  | restrict_to_browser_context, | 
|  | GURL(), | 
|  | EventRouter::USER_GESTURE_UNKNOWN, | 
|  | EventFilteringInfo()) {} | 
|  |  | 
|  | Event::Event(events::HistogramValue histogram_value, | 
|  | const std::string& event_name, | 
|  | std::unique_ptr<ListValue> event_args_tmp, | 
|  | BrowserContext* restrict_to_browser_context, | 
|  | const GURL& event_url, | 
|  | EventRouter::UserGestureState user_gesture, | 
|  | const EventFilteringInfo& filter_info) | 
|  | : histogram_value(histogram_value), | 
|  | event_name(event_name), | 
|  | event_args(std::move(event_args_tmp)), | 
|  | restrict_to_browser_context(restrict_to_browser_context), | 
|  | event_url(event_url), | 
|  | user_gesture(user_gesture), | 
|  | filter_info(filter_info) { | 
|  | DCHECK(event_args); | 
|  | DCHECK_NE(events::UNKNOWN, histogram_value) | 
|  | << "events::UNKNOWN cannot be used as a histogram value.\n" | 
|  | << "If this is a test, use events::FOR_TEST.\n" | 
|  | << "If this is production code, it is important that you use a realistic " | 
|  | << "value so that we can accurately track event usage. " | 
|  | << "See extension_event_histogram_value.h for inspiration."; | 
|  | } | 
|  |  | 
|  | Event::~Event() {} | 
|  |  | 
|  | Event* Event::DeepCopy() { | 
|  | Event* copy = new Event( | 
|  | histogram_value, event_name, | 
|  | std::unique_ptr<base::ListValue>(event_args->DeepCopy()), | 
|  | restrict_to_browser_context, event_url, user_gesture, filter_info); | 
|  | copy->will_dispatch_callback = will_dispatch_callback; | 
|  | return copy; | 
|  | } | 
|  |  | 
|  | EventListenerInfo::EventListenerInfo(const std::string& event_name, | 
|  | const std::string& extension_id, | 
|  | const GURL& listener_url, | 
|  | content::BrowserContext* browser_context) | 
|  | : event_name(event_name), | 
|  | extension_id(extension_id), | 
|  | listener_url(listener_url), | 
|  | browser_context(browser_context) { | 
|  | } | 
|  |  | 
|  | }  // namespace extensions |