diff options
Diffstat (limited to 'extensions/browser/event_router.cc')
-rw-r--r-- | extensions/browser/event_router.cc | 783 |
1 files changed, 783 insertions, 0 deletions
diff --git a/extensions/browser/event_router.cc b/extensions/browser/event_router.cc new file mode 100644 index 0000000..6105572 --- /dev/null +++ b/extensions/browser/event_router.cc @@ -0,0 +1,783 @@ +// 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 <utility> + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/message_loop/message_loop.h" +#include "base/stl_util.h" +#include "base/values.h" +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/extensions/extension_host.h" +#include "chrome/browser/extensions/extension_prefs.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/extension_util.h" +#include "chrome/common/extensions/extension_messages.h" +#include "content/public/browser/notification_service.h" +#include "content/public/browser/render_process_host.h" +#include "extensions/browser/extensions_browser_client.h" +#include "extensions/browser/lazy_background_task_queue.h" +#include "extensions/browser/process_manager.h" +#include "extensions/browser/process_map.h" +#include "extensions/common/extension.h" +#include "extensions/common/extension_api.h" +#include "extensions/common/extension_urls.h" +#include "extensions/common/manifest_handlers/background_info.h" +#include "extensions/common/manifest_handlers/incognito_info.h" + +using base::DictionaryValue; +using base::ListValue; +using content::BrowserContext; +using content::BrowserThread; + +namespace extensions { + +namespace { + +void DoNothing(ExtensionHost* host) {} + +// A dictionary of event names to lists of filters that this extension has +// registered from its lazy background page. +const char kFilteredEvents[] = "filtered_events"; + +} // namespace + +const char EventRouter::kRegisteredEvents[] = "events"; + +struct EventRouter::ListenerProcess { + content::RenderProcessHost* process; + std::string extension_id; + + ListenerProcess(content::RenderProcessHost* process, + const std::string& extension_id) + : process(process), extension_id(extension_id) {} + + bool operator<(const ListenerProcess& that) const { + if (process < that.process) + return true; + if (process == that.process && extension_id < that.extension_id) + return true; + return false; + } +}; + +// static +void EventRouter::NotifyExtensionDispatchObserverOnUIThread( + void* browser_context_id, + scoped_ptr<EventDispatchInfo> details) { + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&NotifyExtensionDispatchObserverOnUIThread, + browser_context_id, base::Passed(&details))); + } else { + BrowserContext* context = + reinterpret_cast<BrowserContext*>(browser_context_id); + if (!ExtensionsBrowserClient::Get()->IsValidContext(context)) + return; + ExtensionSystem* extension_system = + ExtensionSystem::GetForBrowserContext(context); + EventRouter* event_router = extension_system->event_router(); + if (!event_router) + return; + if (event_router->event_dispatch_observer_) { + event_router->event_dispatch_observer_->OnWillDispatchEvent( + details.Pass()); + } + } +} + +// static +void EventRouter::DispatchExtensionMessage(IPC::Sender* ipc_sender, + void* browser_context_id, + const std::string& extension_id, + const std::string& event_name, + ListValue* event_args, + UserGestureState user_gesture, + const EventFilteringInfo& info) { + NotifyExtensionDispatchObserverOnUIThread( + browser_context_id, + make_scoped_ptr(new EventDispatchInfo( + extension_id, + event_name, + make_scoped_ptr(event_args->DeepCopy())))); + + ListValue args; + args.Set(0, new base::StringValue(event_name)); + args.Set(1, event_args); + args.Set(2, info.AsValue().release()); + ipc_sender->Send(new ExtensionMsg_MessageInvoke( + MSG_ROUTING_CONTROL, + extension_id, + kEventBindings, + "dispatchEvent", + args, + user_gesture == USER_GESTURE_ENABLED)); + + // DispatchExtensionMessage does _not_ take ownership of event_args, so we + // must ensure that the destruction of args does not attempt to free it. + scoped_ptr<Value> removed_event_args; + args.Remove(1, &removed_event_args); + ignore_result(removed_event_args.release()); +} + +// 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::DispatchEvent(IPC::Sender* ipc_sender, + void* browser_context_id, + const std::string& extension_id, + const std::string& event_name, + scoped_ptr<ListValue> event_args, + UserGestureState user_gesture, + const EventFilteringInfo& info) { + DispatchExtensionMessage(ipc_sender, + browser_context_id, + extension_id, + event_name, + event_args.get(), + user_gesture, + info); + + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&EventRouter::IncrementInFlightEventsOnUI, + browser_context_id, + extension_id)); +} + +EventRouter::EventRouter(BrowserContext* browser_context, + ExtensionPrefs* extension_prefs) + : browser_context_(browser_context), + extension_prefs_(extension_prefs), + listeners_(this), + event_dispatch_observer_(NULL) { + registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, + content::NotificationService::AllSources()); + registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, + content::NotificationService::AllSources()); + registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_ENABLED, + content::Source<BrowserContext>(browser_context_)); + registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, + content::Source<BrowserContext>(browser_context_)); + registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, + content::Source<BrowserContext>(browser_context_)); +} + +EventRouter::~EventRouter() {} + +void EventRouter::AddEventListener(const std::string& event_name, + content::RenderProcessHost* process, + const std::string& extension_id) { + listeners_.AddListener(scoped_ptr<EventListener>(new EventListener( + event_name, extension_id, process, scoped_ptr<DictionaryValue>()))); +} + +void EventRouter::RemoveEventListener(const std::string& event_name, + content::RenderProcessHost* process, + const std::string& extension_id) { + EventListener listener(event_name, extension_id, process, + scoped_ptr<DictionaryValue>()); + listeners_.RemoveListener(&listener); +} + +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) { + std::vector<ObserverMap::iterator> iters_to_remove; + for (ObserverMap::iterator iter = observers_.begin(); + iter != observers_.end(); ++iter) { + if (iter->second == observer) + iters_to_remove.push_back(iter); + } + for (size_t i = 0; i < iters_to_remove.size(); ++i) + observers_.erase(iters_to_remove[i]); +} + +void EventRouter::SetEventDispatchObserver(EventDispatchObserver* observer) { + CHECK(!event_dispatch_observer_); + event_dispatch_observer_ = observer; +} + +void EventRouter::OnListenerAdded(const EventListener* listener) { + const EventListenerInfo details( + listener->event_name, + listener->extension_id, + listener->process ? listener->process->GetBrowserContext() : NULL); + 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); +} + +void EventRouter::OnListenerRemoved(const EventListener* listener) { + const EventListenerInfo details( + listener->event_name, + listener->extension_id, + listener->process ? listener->process->GetBrowserContext() : NULL); + 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::AddLazyEventListener(const std::string& event_name, + const std::string& extension_id) { + scoped_ptr<EventListener> listener(new EventListener( + event_name, extension_id, NULL, scoped_ptr<DictionaryValue>())); + bool is_new = listeners_.AddListener(listener.Pass()); + + if (is_new) { + std::set<std::string> events = GetRegisteredEvents(extension_id); + bool prefs_is_new = events.insert(event_name).second; + if (prefs_is_new) + SetRegisteredEvents(extension_id, events); + } +} + +void EventRouter::RemoveLazyEventListener(const std::string& event_name, + const std::string& extension_id) { + EventListener listener(event_name, extension_id, NULL, + scoped_ptr<DictionaryValue>()); + bool did_exist = listeners_.RemoveListener(&listener); + + if (did_exist) { + std::set<std::string> events = GetRegisteredEvents(extension_id); + bool prefs_did_exist = events.erase(event_name) > 0; + DCHECK(prefs_did_exist); + SetRegisteredEvents(extension_id, events); + } +} + +void EventRouter::AddFilteredEventListener(const std::string& event_name, + content::RenderProcessHost* process, + const std::string& extension_id, + const base::DictionaryValue& filter, + bool add_lazy_listener) { + listeners_.AddListener(scoped_ptr<EventListener>(new EventListener( + event_name, extension_id, process, + scoped_ptr<DictionaryValue>(filter.DeepCopy())))); + + if (add_lazy_listener) { + bool added = listeners_.AddListener(scoped_ptr<EventListener>( + new EventListener(event_name, extension_id, NULL, + scoped_ptr<DictionaryValue>(filter.DeepCopy())))); + + if (added) + AddFilterToEvent(event_name, extension_id, &filter); + } +} + +void EventRouter::RemoveFilteredEventListener( + const std::string& event_name, + content::RenderProcessHost* process, + const std::string& extension_id, + const base::DictionaryValue& filter, + bool remove_lazy_listener) { + EventListener listener(event_name, extension_id, process, + scoped_ptr<DictionaryValue>(filter.DeepCopy())); + + listeners_.RemoveListener(&listener); + + if (remove_lazy_listener) { + listener.process = NULL; + bool removed = listeners_.RemoveListener(&listener); + + if (removed) + RemoveFilterFromEvent(event_name, extension_id, &filter); + } +} + +bool EventRouter::HasEventListener(const std::string& event_name) { + return listeners_.HasListenerForEvent(event_name); +} + +bool EventRouter::ExtensionHasEventListener(const std::string& extension_id, + const std::string& event_name) { + return listeners_.HasListenerForExtension(extension_id, event_name); +} + +bool EventRouter::HasEventListenerImpl(const ListenerMap& listener_map, + const std::string& extension_id, + const std::string& event_name) { + ListenerMap::const_iterator it = listener_map.find(event_name); + if (it == listener_map.end()) + return false; + + const std::set<ListenerProcess>& listeners = it->second; + if (extension_id.empty()) + return !listeners.empty(); + + for (std::set<ListenerProcess>::const_iterator listener = listeners.begin(); + listener != listeners.end(); ++listener) { + if (listener->extension_id == extension_id) + return true; + } + return false; +} + +std::set<std::string> EventRouter::GetRegisteredEvents( + const std::string& extension_id) { + std::set<std::string> events; + const ListValue* events_value = NULL; + + if (!extension_prefs_ || + !extension_prefs_->ReadPrefAsList( + extension_id, kRegisteredEvents, &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::SetRegisteredEvents(const std::string& extension_id, + const std::set<std::string>& events) { + ListValue* events_value = new ListValue; + for (std::set<std::string>::const_iterator iter = events.begin(); + iter != events.end(); ++iter) { + events_value->Append(new StringValue(*iter)); + } + extension_prefs_->UpdateExtensionPref( + extension_id, kRegisteredEvents, events_value); +} + +void EventRouter::AddFilterToEvent(const std::string& event_name, + const std::string& extension_id, + const DictionaryValue* filter) { + ExtensionPrefs::ScopedDictionaryUpdate update( + extension_prefs_, extension_id, kFilteredEvents); + DictionaryValue* filtered_events = update.Get(); + if (!filtered_events) + filtered_events = update.Create(); + + ListValue* filter_list = NULL; + if (!filtered_events->GetList(event_name, &filter_list)) { + filter_list = new ListValue; + filtered_events->SetWithoutPathExpansion(event_name, filter_list); + } + + filter_list->Append(filter->DeepCopy()); +} + +void EventRouter::RemoveFilterFromEvent(const std::string& event_name, + const std::string& extension_id, + const DictionaryValue* filter) { + ExtensionPrefs::ScopedDictionaryUpdate update( + extension_prefs_, extension_id, kFilteredEvents); + DictionaryValue* filtered_events = update.Get(); + 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 = NULL; + CHECK(filter_list->GetDictionary(i, &filter)); + if (filter->Equals(filter)) { + filter_list->Remove(i, NULL); + break; + } + } +} + +const DictionaryValue* EventRouter::GetFilteredEvents( + const std::string& extension_id) { + const DictionaryValue* events = NULL; + extension_prefs_->ReadPrefAsDictionary( + extension_id, kFilteredEvents, &events); + return events; +} + +void EventRouter::BroadcastEvent(scoped_ptr<Event> event) { + DispatchEventImpl(std::string(), linked_ptr<Event>(event.release())); +} + +void EventRouter::DispatchEventToExtension(const std::string& extension_id, + scoped_ptr<Event> event) { + DCHECK(!extension_id.empty()); + DispatchEventImpl(extension_id, linked_ptr<Event>(event.release())); +} + +void EventRouter::DispatchEventWithLazyListener(const std::string& extension_id, + scoped_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, event.Pass()); + 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)); + + std::set<EventDispatchIdentifier> already_dispatched; + + // 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 (std::set<const EventListener*>::iterator it = listeners.begin(); + it != listeners.end(); it++) { + const EventListener* listener = *it; + if (restrict_to_extension_id.empty() || + restrict_to_extension_id == listener->extension_id) { + if (!listener->process) { + DispatchLazyEvent(listener->extension_id, event, &already_dispatched); + } + } + } + + for (std::set<const EventListener*>::iterator it = listeners.begin(); + it != listeners.end(); it++) { + const EventListener* listener = *it; + if (restrict_to_extension_id.empty() || + restrict_to_extension_id == listener->extension_id) { + if (listener->process) { + EventDispatchIdentifier dispatch_id( + listener->process->GetBrowserContext(), listener->extension_id); + if (!ContainsKey(already_dispatched, dispatch_id)) { + DispatchEventToProcess(listener->extension_id, listener->process, + event); + } + } + } + } +} + +void EventRouter::DispatchLazyEvent( + const std::string& extension_id, + const linked_ptr<Event>& event, + std::set<EventDispatchIdentifier>* already_dispatched) { + ExtensionService* service = ExtensionSystem::GetForBrowserContext( + browser_context_)->extension_service(); + // Check both the original and the incognito browser context to see if we + // should load a lazy bg page to handle the event. The latter case + // occurs in the case of split-mode extensions. + const Extension* extension = service->extensions()->GetByID(extension_id); + if (!extension) + return; + + if (MaybeLoadLazyBackgroundPageToDispatchEvent( + browser_context_, extension, event)) { + already_dispatched->insert(std::make_pair(browser_context_, extension_id)); + } + + ExtensionsBrowserClient* browser_client = ExtensionsBrowserClient::Get(); + if (browser_client->HasOffTheRecordContext(browser_context_) && + IncognitoInfo::IsSplitMode(extension)) { + BrowserContext* incognito_context = + browser_client->GetOffTheRecordContext(browser_context_); + if (MaybeLoadLazyBackgroundPageToDispatchEvent( + incognito_context, extension, event)) { + already_dispatched->insert( + std::make_pair(incognito_context, extension_id)); + } + } +} + +void EventRouter::DispatchEventToProcess(const std::string& extension_id, + content::RenderProcessHost* process, + const linked_ptr<Event>& event) { + ExtensionService* service = ExtensionSystem::GetForBrowserContext( + browser_context_)->extension_service(); + const Extension* extension = service->extensions()->GetByID(extension_id); + + // The extension could have been removed, but we do not unregister it until + // the extension process is unloaded. + if (!extension) + return; + + BrowserContext* listener_context = process->GetBrowserContext(); + ProcessMap* process_map = + ExtensionSystem::GetForBrowserContext(listener_context) + ->extension_service() + ->process_map(); + // If the event is privileged, only send to extension processes. Otherwise, + // it's OK to send to normal renderers (e.g., for content scripts). + if (ExtensionAPI::GetSharedInstance()->IsPrivileged(event->event_name) && + !process_map->Contains(extension->id(), process->GetID())) { + return; + } + + // If the event is restricted to a URL, only dispatch if the extension has + // permission for it (or if the event originated from itself). + if (!event->event_url.is_empty() && + event->event_url.host() != extension->id() && + !extension->GetActivePermissions()->HasEffectiveAccessToURL( + event->event_url)) { + return; + } + + if (!CanDispatchEventToBrowserContext(listener_context, extension, event)) + return; + + if (!event->will_dispatch_callback.is_null()) { + event->will_dispatch_callback.Run(listener_context, extension, + event->event_args.get()); + } + + DispatchExtensionMessage(process, listener_context, extension->id(), + event->event_name, event->event_args.get(), + event->user_gesture, event->filter_info); + IncrementInFlightEvents(listener_context, extension); +} + +bool EventRouter::CanDispatchEventToBrowserContext( + BrowserContext* context, + const Extension* extension, + const linked_ptr<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 cross_incognito = event->restrict_to_browser_context && + context != event->restrict_to_browser_context; + if (!cross_incognito) + return true; + ExtensionService* service = + ExtensionSystem::GetForBrowserContext(context)->extension_service(); + return extension_util::CanCrossIncognito(extension, service); +} + +bool EventRouter::MaybeLoadLazyBackgroundPageToDispatchEvent( + BrowserContext* context, + const Extension* extension, + const linked_ptr<Event>& event) { + if (!CanDispatchEventToBrowserContext(context, extension, event)) + return false; + + LazyBackgroundTaskQueue* queue = ExtensionSystem::GetForBrowserContext( + context)->lazy_background_task_queue(); + if (queue->ShouldEnqueueTask(context, extension)) { + linked_ptr<Event> dispatched_event(event); + + // If there's a dispatch callback, call it now (rather than dispatch time) + // to avoid lifetime issues. Use a separate copy of the event args, so they + // last until the event is dispatched. + if (!event->will_dispatch_callback.is_null()) { + dispatched_event.reset(event->DeepCopy()); + dispatched_event->will_dispatch_callback.Run( + context, extension, dispatched_event->event_args.get()); + // Ensure we don't call it again at dispatch time. + dispatched_event->will_dispatch_callback.Reset(); + } + + queue->AddPendingTask(context, extension->id(), + base::Bind(&EventRouter::DispatchPendingEvent, + base::Unretained(this), dispatched_event)); + return true; + } + + return false; +} + +// static +void EventRouter::IncrementInFlightEventsOnUI( + void* browser_context_id, + const std::string& extension_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + BrowserContext* browser_context = + reinterpret_cast<BrowserContext*>(browser_context_id); + if (!ExtensionsBrowserClient::Get()->IsValidContext(browser_context)) + return; + ExtensionSystem* extension_system = + ExtensionSystem::GetForBrowserContext(browser_context); + EventRouter* event_router = extension_system->event_router(); + if (!event_router) + return; + ExtensionService* extension_service = extension_system->extension_service(); + const Extension* extension = + extension_service->extensions()->GetByID(extension_id); + if (!extension) + return; + event_router->IncrementInFlightEvents(browser_context, extension); +} + +void EventRouter::IncrementInFlightEvents(BrowserContext* context, + const Extension* extension) { + // 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 = + ExtensionSystem::GetForBrowserContext(context)->process_manager(); + ExtensionHost* host = pm->GetBackgroundHostForExtension(extension->id()); + if (host) + pm->IncrementLazyKeepaliveCount(extension); + } +} + +void EventRouter::OnEventAck(BrowserContext* context, + const std::string& extension_id) { + ProcessManager* pm = + ExtensionSystem::GetForBrowserContext(context)->process_manager(); + 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()); +} + +void EventRouter::DispatchPendingEvent(const linked_ptr<Event>& event, + ExtensionHost* host) { + if (!host) + return; + + if (listeners_.HasProcessListener(host->render_process_host(), + host->extension()->id())) { + DispatchEventToProcess(host->extension()->id(), + host->render_process_host(), event); + } +} + +void EventRouter::Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + switch (type) { + case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: + case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: { + content::RenderProcessHost* renderer = + content::Source<content::RenderProcessHost>(source).ptr(); + // Remove all event listeners associated with this renderer. + listeners_.RemoveListenersForProcess(renderer); + break; + } + case chrome::NOTIFICATION_EXTENSION_ENABLED: { + // If the extension has a lazy background page, make sure it gets loaded + // to register the events the extension is interested in. + const Extension* extension = + content::Details<const Extension>(details).ptr(); + if (BackgroundInfo::HasLazyBackgroundPage(extension)) { + LazyBackgroundTaskQueue* queue = ExtensionSystem::GetForBrowserContext( + browser_context_)->lazy_background_task_queue(); + queue->AddPendingTask(browser_context_, extension->id(), + base::Bind(&DoNothing)); + } + break; + } + case chrome::NOTIFICATION_EXTENSION_LOADED: { + // Add all registered lazy listeners to our cache. + const Extension* extension = + content::Details<const Extension>(details).ptr(); + std::set<std::string> registered_events = + GetRegisteredEvents(extension->id()); + listeners_.LoadUnfilteredLazyListeners(extension->id(), + registered_events); + const DictionaryValue* filtered_events = + GetFilteredEvents(extension->id()); + if (filtered_events) + listeners_.LoadFilteredLazyListeners(extension->id(), *filtered_events); + break; + } + case chrome::NOTIFICATION_EXTENSION_UNLOADED: { + // Remove all registered lazy listeners from our cache. + UnloadedExtensionInfo* unloaded = + content::Details<UnloadedExtensionInfo>(details).ptr(); + listeners_.RemoveLazyListenersForExtension(unloaded->extension->id()); + break; + } + default: + NOTREACHED(); + return; + } +} + +Event::Event(const std::string& event_name, + scoped_ptr<base::ListValue> event_args) + : event_name(event_name), + event_args(event_args.Pass()), + restrict_to_browser_context(NULL), + user_gesture(EventRouter::USER_GESTURE_UNKNOWN) { + DCHECK(this->event_args.get()); +} + +Event::Event(const std::string& event_name, + scoped_ptr<base::ListValue> event_args, + BrowserContext* restrict_to_browser_context) + : event_name(event_name), + event_args(event_args.Pass()), + restrict_to_browser_context(restrict_to_browser_context), + user_gesture(EventRouter::USER_GESTURE_UNKNOWN) { + DCHECK(this->event_args.get()); +} + +Event::Event(const std::string& event_name, + scoped_ptr<ListValue> event_args, + BrowserContext* restrict_to_browser_context, + const GURL& event_url, + EventRouter::UserGestureState user_gesture, + const EventFilteringInfo& filter_info) + : event_name(event_name), + event_args(event_args.Pass()), + restrict_to_browser_context(restrict_to_browser_context), + event_url(event_url), + user_gesture(user_gesture), + filter_info(filter_info) { + DCHECK(this->event_args.get()); +} + +Event::~Event() {} + +Event* Event::DeepCopy() { + Event* copy = new Event(event_name, + scoped_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, + content::BrowserContext* browser_context) + : event_name(event_name), + extension_id(extension_id), + browser_context(browser_context) {} + +EventDispatchInfo::EventDispatchInfo(const std::string& extension_id, + const std::string& event_name, + scoped_ptr<ListValue> event_args) + : extension_id(extension_id), + event_name(event_name), + event_args(event_args.Pass()) {} + +EventDispatchInfo::~EventDispatchInfo() {} + +} // namespace extensions |