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"
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
{
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
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
)));
72 // Notify the ApiActivityMonitor about the event dispatch.
73 BrowserContext
* context
= static_cast<BrowserContext
*>(browser_context_id
);
74 if (!ExtensionsBrowserClient::Get()->IsValidContext(context
))
76 ApiActivityMonitor
* monitor
=
77 ExtensionsBrowserClient::Get()->GetApiActivityMonitor(context
);
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
;
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
)
100 if (process
== that
.process
&& extension_id
< that
.extension_id
)
107 void EventRouter::DispatchExtensionMessage(IPC::Sender
* ipc_sender
,
108 void* browser_context_id
,
109 const std::string
& extension_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.
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(
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());
141 EventRouter
* EventRouter::Get(content::BrowserContext
* browser_context
) {
142 return EventRouterFactory::GetForBrowserContext(browser_context
);
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
);
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
,
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
,
176 DispatchExtensionMessage(ipc_sender
, browser_context_id
, extension_id
,
177 event_id
, event_name
, event_args
.get(), user_gesture
,
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),
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();
258 bool inserted
= observed_process_set_
.insert(process
).second
;
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
,
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
>()));
295 std::set
<std::string
> events
= GetRegisteredEvents(extension_id
);
296 bool prefs_is_new
= events
.insert(event_name
).second
;
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());
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(
325 scoped_ptr
<DictionaryValue
>(filter
.DeepCopy())));
327 if (add_lazy_listener
) {
328 bool added
= listeners_
.AddListener(EventListener::ForExtension(
332 scoped_ptr
<DictionaryValue
>(filter
.DeepCopy())));
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(
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());
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())
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
)
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
)) {
401 for (size_t i
= 0; i
< events_value
->GetSize(); ++i
) {
403 if (events_value
->GetString(i
, &event
))
404 events
.insert(event
);
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
)) {
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
);
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
);
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
);
484 AddLazyEventListener(event_name
, extension_id
);
485 DispatchEventToExtension(extension_id
, event
.Pass());
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
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
,
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(
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
,
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(
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.
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
)) {
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
)) {
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();
630 if (!event
->will_dispatch_callback
.is_null() &&
631 !event
->will_dispatch_callback
.Run(listener_context
, extension
,
632 event
.get(), listener_filter
)) {
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
);
642 ReportEvent(event
->histogram_value
, extension
, did_enqueue
);
643 IncrementInFlightEvents(listener_context
, extension
, event_id
,
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
)
658 return ExtensionsBrowserClient::Get()->CanExtensionCrossIncognito(
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
))
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.
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
));
698 void EventRouter::DoDispatchEventToSenderBookkeepingOnUI(
699 void* browser_context_id
,
700 const std::string
& extension_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
))
709 const Extension
* extension
=
710 ExtensionRegistry::Get(browser_context
)->enabled_extensions().GetByID(
714 EventRouter
* event_router
= EventRouter::Get(browser_context
);
715 event_router
->IncrementInFlightEvents(browser_context
, extension
, event_id
,
717 event_router
->ReportEvent(histogram_value
, extension
,
718 false /* did_enqueue */);
721 void EventRouter::IncrementInFlightEvents(BrowserContext
* context
,
722 const Extension
* extension
,
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());
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
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
,
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
)) {
788 UMA_HISTOGRAM_ENUMERATION(
789 "Extensions.Events.DispatchWithSuspendedEventPage", histogram_value
,
790 events::ENUM_BOUNDARY
);
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
) {
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
) {
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
));
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());
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
,
864 restrict_to_browser_context
,
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
) {
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.";
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
,
899 copy
->will_dispatch_callback
= will_dispatch_callback
;
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