Check for captive portal correctly in OAuth2TokenFetcher.
[chromium-blink-merge.git] / extensions / browser / event_router.cc
blobd660b8d71544666ed80019381fc92aa99452bdd1
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "extensions/browser/event_router.h"
7 #include <utility>
9 #include "base/atomic_sequence_num.h"
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/lazy_instance.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/metrics/histogram_macros.h"
15 #include "base/stl_util.h"
16 #include "base/values.h"
17 #include "content/public/browser/notification_service.h"
18 #include "content/public/browser/render_process_host.h"
19 #include "extensions/browser/api_activity_monitor.h"
20 #include "extensions/browser/event_router_factory.h"
21 #include "extensions/browser/extension_host.h"
22 #include "extensions/browser/extension_prefs.h"
23 #include "extensions/browser/extension_registry.h"
24 #include "extensions/browser/extension_system.h"
25 #include "extensions/browser/extensions_browser_client.h"
26 #include "extensions/browser/lazy_background_task_queue.h"
27 #include "extensions/browser/notification_types.h"
28 #include "extensions/browser/process_manager.h"
29 #include "extensions/browser/process_map.h"
30 #include "extensions/common/constants.h"
31 #include "extensions/common/extension.h"
32 #include "extensions/common/extension_api.h"
33 #include "extensions/common/extension_messages.h"
34 #include "extensions/common/extension_urls.h"
35 #include "extensions/common/features/feature.h"
36 #include "extensions/common/features/feature_provider.h"
37 #include "extensions/common/manifest_handlers/background_info.h"
38 #include "extensions/common/manifest_handlers/incognito_info.h"
39 #include "extensions/common/permissions/permissions_data.h"
41 using base::DictionaryValue;
42 using base::ListValue;
43 using content::BrowserContext;
44 using content::BrowserThread;
46 namespace extensions {
48 namespace {
50 void DoNothing(ExtensionHost* host) {}
52 // A dictionary of event names to lists of filters that this extension has
53 // registered from its lazy background page.
54 const char kFilteredEvents[] = "filtered_events";
56 // Sends a notification about an event to the API activity monitor and the
57 // ExtensionHost for |extension_id| on the UI thread. Can be called from any
58 // thread.
59 void NotifyEventDispatched(void* browser_context_id,
60 const std::string& extension_id,
61 const std::string& event_name,
62 scoped_ptr<ListValue> args) {
63 // The ApiActivityMonitor can only be accessed from the UI thread.
64 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
65 BrowserThread::PostTask(
66 BrowserThread::UI, FROM_HERE,
67 base::Bind(&NotifyEventDispatched, browser_context_id, extension_id,
68 event_name, base::Passed(&args)));
69 return;
72 // Notify the ApiActivityMonitor about the event dispatch.
73 BrowserContext* context = static_cast<BrowserContext*>(browser_context_id);
74 if (!ExtensionsBrowserClient::Get()->IsValidContext(context))
75 return;
76 ApiActivityMonitor* monitor =
77 ExtensionsBrowserClient::Get()->GetApiActivityMonitor(context);
78 if (monitor)
79 monitor->OnApiEventDispatched(extension_id, event_name, args.Pass());
82 // A global identifier used to distinguish extension events.
83 base::StaticAtomicSequenceNumber g_extension_event_id;
85 } // namespace
87 const char EventRouter::kRegisteredEvents[] = "events";
89 struct EventRouter::ListenerProcess {
90 content::RenderProcessHost* process;
91 std::string extension_id;
93 ListenerProcess(content::RenderProcessHost* process,
94 const std::string& extension_id)
95 : process(process), extension_id(extension_id) {}
97 bool operator<(const ListenerProcess& that) const {
98 if (process < that.process)
99 return true;
100 if (process == that.process && extension_id < that.extension_id)
101 return true;
102 return false;
106 // static
107 void EventRouter::DispatchExtensionMessage(IPC::Sender* ipc_sender,
108 void* browser_context_id,
109 const std::string& extension_id,
110 int event_id,
111 const std::string& event_name,
112 ListValue* event_args,
113 UserGestureState user_gesture,
114 const EventFilteringInfo& info) {
115 NotifyEventDispatched(browser_context_id, extension_id, event_name,
116 make_scoped_ptr(event_args->DeepCopy()));
118 // TODO(chirantan): Make event dispatch a separate IPC so that it doesn't
119 // piggyback off MessageInvoke, which is used for other things.
120 ListValue args;
121 args.Set(0, new base::StringValue(event_name));
122 args.Set(1, event_args);
123 args.Set(2, info.AsValue().release());
124 args.Set(3, new base::FundamentalValue(event_id));
125 ipc_sender->Send(new ExtensionMsg_MessageInvoke(
126 MSG_ROUTING_CONTROL,
127 extension_id,
128 kEventBindings,
129 "dispatchEvent",
130 args,
131 user_gesture == USER_GESTURE_ENABLED));
133 // DispatchExtensionMessage does _not_ take ownership of event_args, so we
134 // must ensure that the destruction of args does not attempt to free it.
135 scoped_ptr<base::Value> removed_event_args;
136 args.Remove(1, &removed_event_args);
137 ignore_result(removed_event_args.release());
140 // static
141 EventRouter* EventRouter::Get(content::BrowserContext* browser_context) {
142 return EventRouterFactory::GetForBrowserContext(browser_context);
145 // static
146 std::string EventRouter::GetBaseEventName(const std::string& full_event_name) {
147 size_t slash_sep = full_event_name.find('/');
148 return full_event_name.substr(0, slash_sep);
151 // static
152 void EventRouter::DispatchEventToSender(IPC::Sender* ipc_sender,
153 void* browser_context_id,
154 const std::string& extension_id,
155 events::HistogramValue histogram_value,
156 const std::string& event_name,
157 scoped_ptr<ListValue> event_args,
158 UserGestureState user_gesture,
159 const EventFilteringInfo& info) {
160 int event_id = g_extension_event_id.GetNext();
162 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
163 DoDispatchEventToSenderBookkeepingOnUI(browser_context_id, extension_id,
164 event_id, histogram_value,
165 event_name);
166 } else {
167 // This is called from WebRequest API.
168 // TODO(lazyboy): Skip this entirely: http://crbug.com/488747.
169 BrowserThread::PostTask(
170 BrowserThread::UI, FROM_HERE,
171 base::Bind(&EventRouter::DoDispatchEventToSenderBookkeepingOnUI,
172 browser_context_id, extension_id, event_id, histogram_value,
173 event_name));
176 DispatchExtensionMessage(ipc_sender, browser_context_id, extension_id,
177 event_id, event_name, event_args.get(), user_gesture,
178 info);
181 EventRouter::EventRouter(BrowserContext* browser_context,
182 ExtensionPrefs* extension_prefs)
183 : browser_context_(browser_context),
184 extension_prefs_(extension_prefs),
185 extension_registry_observer_(this),
186 listeners_(this) {
187 registrar_.Add(this,
188 extensions::NOTIFICATION_EXTENSION_ENABLED,
189 content::Source<BrowserContext>(browser_context_));
190 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
193 EventRouter::~EventRouter() {
194 for (auto process : observed_process_set_)
195 process->RemoveObserver(this);
198 void EventRouter::AddEventListener(const std::string& event_name,
199 content::RenderProcessHost* process,
200 const std::string& extension_id) {
201 listeners_.AddListener(EventListener::ForExtension(
202 event_name, extension_id, process, scoped_ptr<DictionaryValue>()));
205 void EventRouter::RemoveEventListener(const std::string& event_name,
206 content::RenderProcessHost* process,
207 const std::string& extension_id) {
208 scoped_ptr<EventListener> listener = EventListener::ForExtension(
209 event_name, extension_id, process, scoped_ptr<DictionaryValue>());
210 listeners_.RemoveListener(listener.get());
213 void EventRouter::AddEventListenerForURL(const std::string& event_name,
214 content::RenderProcessHost* process,
215 const GURL& listener_url) {
216 listeners_.AddListener(EventListener::ForURL(
217 event_name, listener_url, process, scoped_ptr<DictionaryValue>()));
220 void EventRouter::RemoveEventListenerForURL(const std::string& event_name,
221 content::RenderProcessHost* process,
222 const GURL& listener_url) {
223 scoped_ptr<EventListener> listener = EventListener::ForURL(
224 event_name, listener_url, process, scoped_ptr<DictionaryValue>());
225 listeners_.RemoveListener(listener.get());
228 void EventRouter::RegisterObserver(Observer* observer,
229 const std::string& event_name) {
230 // Observing sub-event names like "foo.onBar/123" is not allowed.
231 DCHECK(event_name.find('/') == std::string::npos);
232 observers_[event_name] = observer;
235 void EventRouter::UnregisterObserver(Observer* observer) {
236 std::vector<ObserverMap::iterator> iters_to_remove;
237 for (ObserverMap::iterator iter = observers_.begin();
238 iter != observers_.end(); ++iter) {
239 if (iter->second == observer)
240 iters_to_remove.push_back(iter);
242 for (size_t i = 0; i < iters_to_remove.size(); ++i)
243 observers_.erase(iters_to_remove[i]);
246 void EventRouter::OnListenerAdded(const EventListener* listener) {
247 const EventListenerInfo details(listener->event_name(),
248 listener->extension_id(),
249 listener->listener_url(),
250 listener->GetBrowserContext());
251 std::string base_event_name = GetBaseEventName(listener->event_name());
252 ObserverMap::iterator observer = observers_.find(base_event_name);
253 if (observer != observers_.end())
254 observer->second->OnListenerAdded(details);
256 content::RenderProcessHost* process = listener->process();
257 if (process) {
258 bool inserted = observed_process_set_.insert(process).second;
259 if (inserted)
260 process->AddObserver(this);
264 void EventRouter::OnListenerRemoved(const EventListener* listener) {
265 const EventListenerInfo details(listener->event_name(),
266 listener->extension_id(),
267 listener->listener_url(),
268 listener->GetBrowserContext());
269 std::string base_event_name = GetBaseEventName(listener->event_name());
270 ObserverMap::iterator observer = observers_.find(base_event_name);
271 if (observer != observers_.end())
272 observer->second->OnListenerRemoved(details);
275 void EventRouter::RenderProcessExited(content::RenderProcessHost* host,
276 base::TerminationStatus status,
277 int exit_code) {
278 listeners_.RemoveListenersForProcess(host);
279 observed_process_set_.erase(host);
280 host->RemoveObserver(this);
283 void EventRouter::RenderProcessHostDestroyed(content::RenderProcessHost* host) {
284 listeners_.RemoveListenersForProcess(host);
285 observed_process_set_.erase(host);
286 host->RemoveObserver(this);
289 void EventRouter::AddLazyEventListener(const std::string& event_name,
290 const std::string& extension_id) {
291 bool is_new = listeners_.AddListener(EventListener::ForExtension(
292 event_name, extension_id, NULL, scoped_ptr<DictionaryValue>()));
294 if (is_new) {
295 std::set<std::string> events = GetRegisteredEvents(extension_id);
296 bool prefs_is_new = events.insert(event_name).second;
297 if (prefs_is_new)
298 SetRegisteredEvents(extension_id, events);
302 void EventRouter::RemoveLazyEventListener(const std::string& event_name,
303 const std::string& extension_id) {
304 scoped_ptr<EventListener> listener = EventListener::ForExtension(
305 event_name, extension_id, NULL, scoped_ptr<DictionaryValue>());
306 bool did_exist = listeners_.RemoveListener(listener.get());
308 if (did_exist) {
309 std::set<std::string> events = GetRegisteredEvents(extension_id);
310 bool prefs_did_exist = events.erase(event_name) > 0;
311 DCHECK(prefs_did_exist);
312 SetRegisteredEvents(extension_id, events);
316 void EventRouter::AddFilteredEventListener(const std::string& event_name,
317 content::RenderProcessHost* process,
318 const std::string& extension_id,
319 const base::DictionaryValue& filter,
320 bool add_lazy_listener) {
321 listeners_.AddListener(EventListener::ForExtension(
322 event_name,
323 extension_id,
324 process,
325 scoped_ptr<DictionaryValue>(filter.DeepCopy())));
327 if (add_lazy_listener) {
328 bool added = listeners_.AddListener(EventListener::ForExtension(
329 event_name,
330 extension_id,
331 NULL,
332 scoped_ptr<DictionaryValue>(filter.DeepCopy())));
334 if (added)
335 AddFilterToEvent(event_name, extension_id, &filter);
339 void EventRouter::RemoveFilteredEventListener(
340 const std::string& event_name,
341 content::RenderProcessHost* process,
342 const std::string& extension_id,
343 const base::DictionaryValue& filter,
344 bool remove_lazy_listener) {
345 scoped_ptr<EventListener> listener = EventListener::ForExtension(
346 event_name,
347 extension_id,
348 process,
349 scoped_ptr<DictionaryValue>(filter.DeepCopy()));
351 listeners_.RemoveListener(listener.get());
353 if (remove_lazy_listener) {
354 listener->MakeLazy();
355 bool removed = listeners_.RemoveListener(listener.get());
357 if (removed)
358 RemoveFilterFromEvent(event_name, extension_id, &filter);
362 bool EventRouter::HasEventListener(const std::string& event_name) {
363 return listeners_.HasListenerForEvent(event_name);
366 bool EventRouter::ExtensionHasEventListener(const std::string& extension_id,
367 const std::string& event_name) {
368 return listeners_.HasListenerForExtension(extension_id, event_name);
371 bool EventRouter::HasEventListenerImpl(const ListenerMap& listener_map,
372 const std::string& extension_id,
373 const std::string& event_name) {
374 ListenerMap::const_iterator it = listener_map.find(event_name);
375 if (it == listener_map.end())
376 return false;
378 const std::set<ListenerProcess>& listeners = it->second;
379 if (extension_id.empty())
380 return !listeners.empty();
382 for (std::set<ListenerProcess>::const_iterator listener = listeners.begin();
383 listener != listeners.end(); ++listener) {
384 if (listener->extension_id == extension_id)
385 return true;
387 return false;
390 std::set<std::string> EventRouter::GetRegisteredEvents(
391 const std::string& extension_id) {
392 std::set<std::string> events;
393 const ListValue* events_value = NULL;
395 if (!extension_prefs_ ||
396 !extension_prefs_->ReadPrefAsList(
397 extension_id, kRegisteredEvents, &events_value)) {
398 return events;
401 for (size_t i = 0; i < events_value->GetSize(); ++i) {
402 std::string event;
403 if (events_value->GetString(i, &event))
404 events.insert(event);
406 return events;
409 void EventRouter::SetRegisteredEvents(const std::string& extension_id,
410 const std::set<std::string>& events) {
411 ListValue* events_value = new ListValue;
412 for (std::set<std::string>::const_iterator iter = events.begin();
413 iter != events.end(); ++iter) {
414 events_value->Append(new base::StringValue(*iter));
416 extension_prefs_->UpdateExtensionPref(
417 extension_id, kRegisteredEvents, events_value);
420 void EventRouter::AddFilterToEvent(const std::string& event_name,
421 const std::string& extension_id,
422 const DictionaryValue* filter) {
423 ExtensionPrefs::ScopedDictionaryUpdate update(
424 extension_prefs_, extension_id, kFilteredEvents);
425 DictionaryValue* filtered_events = update.Get();
426 if (!filtered_events)
427 filtered_events = update.Create();
429 ListValue* filter_list = NULL;
430 if (!filtered_events->GetList(event_name, &filter_list)) {
431 filter_list = new ListValue;
432 filtered_events->SetWithoutPathExpansion(event_name, filter_list);
435 filter_list->Append(filter->DeepCopy());
438 void EventRouter::RemoveFilterFromEvent(const std::string& event_name,
439 const std::string& extension_id,
440 const DictionaryValue* filter) {
441 ExtensionPrefs::ScopedDictionaryUpdate update(
442 extension_prefs_, extension_id, kFilteredEvents);
443 DictionaryValue* filtered_events = update.Get();
444 ListValue* filter_list = NULL;
445 if (!filtered_events ||
446 !filtered_events->GetListWithoutPathExpansion(event_name, &filter_list)) {
447 return;
450 for (size_t i = 0; i < filter_list->GetSize(); i++) {
451 DictionaryValue* filter = NULL;
452 CHECK(filter_list->GetDictionary(i, &filter));
453 if (filter->Equals(filter)) {
454 filter_list->Remove(i, NULL);
455 break;
460 const DictionaryValue* EventRouter::GetFilteredEvents(
461 const std::string& extension_id) {
462 const DictionaryValue* events = NULL;
463 extension_prefs_->ReadPrefAsDictionary(
464 extension_id, kFilteredEvents, &events);
465 return events;
468 void EventRouter::BroadcastEvent(scoped_ptr<Event> event) {
469 DispatchEventImpl(std::string(), linked_ptr<Event>(event.release()));
472 void EventRouter::DispatchEventToExtension(const std::string& extension_id,
473 scoped_ptr<Event> event) {
474 DCHECK(!extension_id.empty());
475 DispatchEventImpl(extension_id, linked_ptr<Event>(event.release()));
478 void EventRouter::DispatchEventWithLazyListener(const std::string& extension_id,
479 scoped_ptr<Event> event) {
480 DCHECK(!extension_id.empty());
481 std::string event_name = event->event_name;
482 bool has_listener = ExtensionHasEventListener(extension_id, event_name);
483 if (!has_listener)
484 AddLazyEventListener(event_name, extension_id);
485 DispatchEventToExtension(extension_id, event.Pass());
486 if (!has_listener)
487 RemoveLazyEventListener(event_name, extension_id);
490 void EventRouter::DispatchEventImpl(const std::string& restrict_to_extension_id,
491 const linked_ptr<Event>& event) {
492 // We don't expect to get events from a completely different browser context.
493 DCHECK(!event->restrict_to_browser_context ||
494 ExtensionsBrowserClient::Get()->IsSameContext(
495 browser_context_, event->restrict_to_browser_context));
497 std::set<const EventListener*> listeners(
498 listeners_.GetEventListeners(*event));
500 std::set<EventDispatchIdentifier> already_dispatched;
502 // We dispatch events for lazy background pages first because attempting to do
503 // so will cause those that are being suspended to cancel that suspension.
504 // As canceling a suspension entails sending an event to the affected
505 // background page, and as that event needs to be delivered before we dispatch
506 // the event we are dispatching here, we dispatch to the lazy listeners here
507 // first.
508 for (const EventListener* listener : listeners) {
509 if (restrict_to_extension_id.empty() ||
510 restrict_to_extension_id == listener->extension_id()) {
511 if (listener->IsLazy()) {
512 DispatchLazyEvent(listener->extension_id(), event, &already_dispatched,
513 listener->filter());
518 for (const EventListener* listener : listeners) {
519 if (restrict_to_extension_id.empty() ||
520 restrict_to_extension_id == listener->extension_id()) {
521 if (listener->process()) {
522 EventDispatchIdentifier dispatch_id(listener->GetBrowserContext(),
523 listener->extension_id());
524 if (!ContainsKey(already_dispatched, dispatch_id)) {
525 DispatchEventToProcess(listener->extension_id(),
526 listener->listener_url(), listener->process(),
527 event, listener->filter(),
528 false /* did_enqueue */);
535 void EventRouter::DispatchLazyEvent(
536 const std::string& extension_id,
537 const linked_ptr<Event>& event,
538 std::set<EventDispatchIdentifier>* already_dispatched,
539 const base::DictionaryValue* listener_filter) {
540 // Check both the original and the incognito browser context to see if we
541 // should load a lazy bg page to handle the event. The latter case
542 // occurs in the case of split-mode extensions.
543 const Extension* extension =
544 ExtensionRegistry::Get(browser_context_)->enabled_extensions().GetByID(
545 extension_id);
546 if (!extension)
547 return;
549 if (MaybeLoadLazyBackgroundPageToDispatchEvent(browser_context_, extension,
550 event, listener_filter)) {
551 already_dispatched->insert(std::make_pair(browser_context_, extension_id));
554 ExtensionsBrowserClient* browser_client = ExtensionsBrowserClient::Get();
555 if (browser_client->HasOffTheRecordContext(browser_context_) &&
556 IncognitoInfo::IsSplitMode(extension)) {
557 BrowserContext* incognito_context =
558 browser_client->GetOffTheRecordContext(browser_context_);
559 if (MaybeLoadLazyBackgroundPageToDispatchEvent(incognito_context, extension,
560 event, listener_filter)) {
561 already_dispatched->insert(
562 std::make_pair(incognito_context, extension_id));
567 void EventRouter::DispatchEventToProcess(
568 const std::string& extension_id,
569 const GURL& listener_url,
570 content::RenderProcessHost* process,
571 const linked_ptr<Event>& event,
572 const base::DictionaryValue* listener_filter,
573 bool did_enqueue) {
574 BrowserContext* listener_context = process->GetBrowserContext();
575 ProcessMap* process_map = ProcessMap::Get(listener_context);
577 // NOTE: |extension| being NULL does not necessarily imply that this event
578 // shouldn't be dispatched. Events can be dispatched to WebUI and webviews as
579 // well. It all depends on what GetMostLikelyContextType returns.
580 const Extension* extension =
581 ExtensionRegistry::Get(browser_context_)->enabled_extensions().GetByID(
582 extension_id);
584 if (!extension && !extension_id.empty()) {
585 // Trying to dispatch an event to an extension that doesn't exist. The
586 // extension could have been removed, but we do not unregister it until the
587 // extension process is unloaded.
588 return;
591 if (extension) {
592 // Extension-specific checks.
593 // Firstly, if the event is for a URL, the Extension must have permission
594 // to access that URL.
595 if (!event->event_url.is_empty() &&
596 event->event_url.host() != extension->id() && // event for self is ok
597 !extension->permissions_data()
598 ->active_permissions()
599 ->HasEffectiveAccessToURL(event->event_url)) {
600 return;
602 // Secondly, if the event is for incognito mode, the Extension must be
603 // enabled in incognito mode.
604 if (!CanDispatchEventToBrowserContext(listener_context, extension, event)) {
605 return;
609 Feature::Context target_context =
610 process_map->GetMostLikelyContextType(extension, process->GetID());
612 // We shouldn't be dispatching an event to a webpage, since all such events
613 // (e.g. messaging) don't go through EventRouter.
614 DCHECK_NE(Feature::WEB_PAGE_CONTEXT, target_context)
615 << "Trying to dispatch event " << event->event_name << " to a webpage,"
616 << " but this shouldn't be possible";
618 Feature::Availability availability =
619 ExtensionAPI::GetSharedInstance()->IsAvailable(
620 event->event_name, extension, target_context, listener_url);
621 if (!availability.is_available()) {
622 // It shouldn't be possible to reach here, because access is checked on
623 // registration. However, for paranoia, check on dispatch as well.
624 NOTREACHED() << "Trying to dispatch event " << event->event_name
625 << " which the target does not have access to: "
626 << availability.message();
627 return;
630 if (!event->will_dispatch_callback.is_null() &&
631 !event->will_dispatch_callback.Run(listener_context, extension,
632 event.get(), listener_filter)) {
633 return;
636 int event_id = g_extension_event_id.GetNext();
637 DispatchExtensionMessage(process, listener_context, extension_id, event_id,
638 event->event_name, event->event_args.get(),
639 event->user_gesture, event->filter_info);
641 if (extension) {
642 ReportEvent(event->histogram_value, extension, did_enqueue);
643 IncrementInFlightEvents(listener_context, extension, event_id,
644 event->event_name);
648 bool EventRouter::CanDispatchEventToBrowserContext(
649 BrowserContext* context,
650 const Extension* extension,
651 const linked_ptr<Event>& event) {
652 // Is this event from a different browser context than the renderer (ie, an
653 // incognito tab event sent to a normal process, or vice versa).
654 bool cross_incognito = event->restrict_to_browser_context &&
655 context != event->restrict_to_browser_context;
656 if (!cross_incognito)
657 return true;
658 return ExtensionsBrowserClient::Get()->CanExtensionCrossIncognito(
659 extension, context);
662 bool EventRouter::MaybeLoadLazyBackgroundPageToDispatchEvent(
663 BrowserContext* context,
664 const Extension* extension,
665 const linked_ptr<Event>& event,
666 const base::DictionaryValue* listener_filter) {
667 if (!CanDispatchEventToBrowserContext(context, extension, event))
668 return false;
670 LazyBackgroundTaskQueue* queue = LazyBackgroundTaskQueue::Get(context);
671 if (queue->ShouldEnqueueTask(context, extension)) {
672 linked_ptr<Event> dispatched_event(event);
674 // If there's a dispatch callback, call it now (rather than dispatch time)
675 // to avoid lifetime issues. Use a separate copy of the event args, so they
676 // last until the event is dispatched.
677 if (!event->will_dispatch_callback.is_null()) {
678 dispatched_event.reset(event->DeepCopy());
679 if (!dispatched_event->will_dispatch_callback.Run(
680 context, extension, dispatched_event.get(), listener_filter)) {
681 // The event has been canceled.
682 return true;
684 // Ensure we don't call it again at dispatch time.
685 dispatched_event->will_dispatch_callback.Reset();
688 queue->AddPendingTask(context, extension->id(),
689 base::Bind(&EventRouter::DispatchPendingEvent,
690 base::Unretained(this), dispatched_event));
691 return true;
694 return false;
697 // static
698 void EventRouter::DoDispatchEventToSenderBookkeepingOnUI(
699 void* browser_context_id,
700 const std::string& extension_id,
701 int event_id,
702 events::HistogramValue histogram_value,
703 const std::string& event_name) {
704 DCHECK_CURRENTLY_ON(BrowserThread::UI);
705 BrowserContext* browser_context =
706 reinterpret_cast<BrowserContext*>(browser_context_id);
707 if (!ExtensionsBrowserClient::Get()->IsValidContext(browser_context))
708 return;
709 const Extension* extension =
710 ExtensionRegistry::Get(browser_context)->enabled_extensions().GetByID(
711 extension_id);
712 if (!extension)
713 return;
714 EventRouter* event_router = EventRouter::Get(browser_context);
715 event_router->IncrementInFlightEvents(browser_context, extension, event_id,
716 event_name);
717 event_router->ReportEvent(histogram_value, extension,
718 false /* did_enqueue */);
721 void EventRouter::IncrementInFlightEvents(BrowserContext* context,
722 const Extension* extension,
723 int event_id,
724 const std::string& event_name) {
725 // TODO(chirantan): Turn this on once crbug.com/464513 is fixed.
726 // DCHECK_CURRENTLY_ON(BrowserThread::UI);
728 // Only increment in-flight events if the lazy background page is active,
729 // because that's the only time we'll get an ACK.
730 if (BackgroundInfo::HasLazyBackgroundPage(extension)) {
731 ProcessManager* pm = ProcessManager::Get(context);
732 ExtensionHost* host = pm->GetBackgroundHostForExtension(extension->id());
733 if (host) {
734 pm->IncrementLazyKeepaliveCount(extension);
735 host->OnBackgroundEventDispatched(event_name, event_id);
740 void EventRouter::OnEventAck(BrowserContext* context,
741 const std::string& extension_id) {
742 ProcessManager* pm = ProcessManager::Get(context);
743 ExtensionHost* host = pm->GetBackgroundHostForExtension(extension_id);
744 // The event ACK is routed to the background host, so this should never be
745 // NULL.
746 CHECK(host);
747 // TODO(mpcomplete): We should never get this message unless
748 // HasLazyBackgroundPage is true. Find out why we're getting it anyway.
749 if (host->extension() &&
750 BackgroundInfo::HasLazyBackgroundPage(host->extension()))
751 pm->DecrementLazyKeepaliveCount(host->extension());
754 void EventRouter::ReportEvent(events::HistogramValue histogram_value,
755 const Extension* extension,
756 bool did_enqueue) {
757 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
759 // Record every event fired.
760 UMA_HISTOGRAM_ENUMERATION("Extensions.Events.Dispatch", histogram_value,
761 events::ENUM_BOUNDARY);
763 // Record events for component extensions. These should be kept to a minimum,
764 // especially if they wake its event page. Component extensions should use
765 // declarative APIs as much as possible.
766 if (Manifest::IsComponentLocation(extension->location())) {
767 UMA_HISTOGRAM_ENUMERATION("Extensions.Events.DispatchToComponent",
768 histogram_value, events::ENUM_BOUNDARY);
771 // Record events for background pages, if any. The most important statistic
772 // is DispatchWithSuspendedEventPage. Events reported there woke an event
773 // page. Implementing either filtered or declarative versions of these events
774 // should be prioritised.
776 // Note: all we know is that the extension *has* a persistent or event page,
777 // not that the event is being dispatched *to* such a page. However, this is
778 // academic, since extensions with any background page have that background
779 // page running (or in the case of suspended event pages, must be started)
780 // regardless of where the event is being dispatched. Events are dispatched
781 // to a *process* not a *frame*.
782 if (BackgroundInfo::HasPersistentBackgroundPage(extension)) {
783 UMA_HISTOGRAM_ENUMERATION(
784 "Extensions.Events.DispatchWithPersistentBackgroundPage",
785 histogram_value, events::ENUM_BOUNDARY);
786 } else if (BackgroundInfo::HasLazyBackgroundPage(extension)) {
787 if (did_enqueue) {
788 UMA_HISTOGRAM_ENUMERATION(
789 "Extensions.Events.DispatchWithSuspendedEventPage", histogram_value,
790 events::ENUM_BOUNDARY);
791 } else {
792 UMA_HISTOGRAM_ENUMERATION(
793 "Extensions.Events.DispatchWithRunningEventPage", histogram_value,
794 events::ENUM_BOUNDARY);
799 void EventRouter::DispatchPendingEvent(const linked_ptr<Event>& event,
800 ExtensionHost* host) {
801 if (!host)
802 return;
804 if (listeners_.HasProcessListener(host->render_process_host(),
805 host->extension()->id())) {
806 DispatchEventToProcess(host->extension()->id(), host->GetURL(),
807 host->render_process_host(), event, nullptr,
808 true /* did_enqueue */);
812 void EventRouter::Observe(int type,
813 const content::NotificationSource& source,
814 const content::NotificationDetails& details) {
815 switch (type) {
816 case extensions::NOTIFICATION_EXTENSION_ENABLED: {
817 // If the extension has a lazy background page, make sure it gets loaded
818 // to register the events the extension is interested in.
819 const Extension* extension =
820 content::Details<const Extension>(details).ptr();
821 if (BackgroundInfo::HasLazyBackgroundPage(extension)) {
822 LazyBackgroundTaskQueue* queue =
823 LazyBackgroundTaskQueue::Get(browser_context_);
824 queue->AddPendingTask(browser_context_, extension->id(),
825 base::Bind(&DoNothing));
827 break;
829 default:
830 NOTREACHED();
834 void EventRouter::OnExtensionLoaded(content::BrowserContext* browser_context,
835 const Extension* extension) {
836 // Add all registered lazy listeners to our cache.
837 std::set<std::string> registered_events =
838 GetRegisteredEvents(extension->id());
839 listeners_.LoadUnfilteredLazyListeners(extension->id(), registered_events);
840 const DictionaryValue* filtered_events = GetFilteredEvents(extension->id());
841 if (filtered_events)
842 listeners_.LoadFilteredLazyListeners(extension->id(), *filtered_events);
845 void EventRouter::OnExtensionUnloaded(content::BrowserContext* browser_context,
846 const Extension* extension,
847 UnloadedExtensionInfo::Reason reason) {
848 // Remove all registered listeners from our cache.
849 listeners_.RemoveListenersForExtension(extension->id());
852 Event::Event(events::HistogramValue histogram_value,
853 const std::string& event_name,
854 scoped_ptr<base::ListValue> event_args)
855 : Event(histogram_value, event_name, event_args.Pass(), nullptr) {}
857 Event::Event(events::HistogramValue histogram_value,
858 const std::string& event_name,
859 scoped_ptr<base::ListValue> event_args,
860 BrowserContext* restrict_to_browser_context)
861 : Event(histogram_value,
862 event_name,
863 event_args.Pass(),
864 restrict_to_browser_context,
865 GURL(),
866 EventRouter::USER_GESTURE_UNKNOWN,
867 EventFilteringInfo()) {}
869 Event::Event(events::HistogramValue histogram_value,
870 const std::string& event_name,
871 scoped_ptr<ListValue> event_args_tmp,
872 BrowserContext* restrict_to_browser_context,
873 const GURL& event_url,
874 EventRouter::UserGestureState user_gesture,
875 const EventFilteringInfo& filter_info)
876 : histogram_value(histogram_value),
877 event_name(event_name),
878 event_args(event_args_tmp.Pass()),
879 restrict_to_browser_context(restrict_to_browser_context),
880 event_url(event_url),
881 user_gesture(user_gesture),
882 filter_info(filter_info) {
883 DCHECK(event_args);
884 DCHECK_NE(events::UNKNOWN, histogram_value)
885 << "events::UNKNOWN cannot be used as a histogram value.\n"
886 << "If this is a test, use events::FOR_TEST.\n"
887 << "If this is production code, it is important that you use a realistic "
888 << "value so that we can accurately track event usage. "
889 << "See extension_event_histogram_value.h for inspiration.";
892 Event::~Event() {}
894 Event* Event::DeepCopy() {
895 Event* copy = new Event(histogram_value, event_name,
896 scoped_ptr<base::ListValue>(event_args->DeepCopy()),
897 restrict_to_browser_context, event_url, user_gesture,
898 filter_info);
899 copy->will_dispatch_callback = will_dispatch_callback;
900 return copy;
903 EventListenerInfo::EventListenerInfo(const std::string& event_name,
904 const std::string& extension_id,
905 const GURL& listener_url,
906 content::BrowserContext* browser_context)
907 : event_name(event_name),
908 extension_id(extension_id),
909 listener_url(listener_url),
910 browser_context(browser_context) {
913 } // namespace extensions