extension_event_router.cc revision ddb351dbec246cf1fab5ec20d2d5520909041de1
1// Copyright (c) 2011 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.
4
5#include "chrome/browser/extensions/extension_event_router.h"
6
7#include "base/values.h"
8#include "chrome/browser/extensions/extension_devtools_manager.h"
9#include "chrome/browser/extensions/extension_processes_api.h"
10#include "chrome/browser/extensions/extension_processes_api_constants.h"
11#include "chrome/browser/extensions/extension_service.h"
12#include "chrome/browser/extensions/extension_tabs_module.h"
13#include "chrome/browser/extensions/extension_webrequest_api.h"
14#include "chrome/browser/profiles/profile.h"
15#include "chrome/common/extensions/extension.h"
16#include "chrome/common/extensions/extension_messages.h"
17#include "content/browser/child_process_security_policy.h"
18#include "content/browser/renderer_host/render_process_host.h"
19#include "content/common/notification_service.h"
20
21namespace {
22
23const char kDispatchEvent[] = "Event.dispatchJSON";
24
25static void DispatchEvent(RenderProcessHost* renderer,
26                          const std::string& extension_id,
27                          const std::string& event_name,
28                          const std::string& event_args,
29                          const GURL& event_url) {
30  ListValue args;
31  args.Set(0, Value::CreateStringValue(event_name));
32  args.Set(1, Value::CreateStringValue(event_args));
33  renderer->Send(new ExtensionMsg_MessageInvoke(MSG_ROUTING_CONTROL,
34      extension_id, kDispatchEvent, args, event_url));
35}
36
37static void NotifyEventListenerRemovedOnIOThread(
38    ProfileId profile_id,
39    const std::string& extension_id,
40    const std::string& sub_event_name) {
41  ExtensionWebRequestEventRouter::GetInstance()->RemoveEventListener(
42      profile_id, extension_id, sub_event_name);
43}
44
45}  // namespace
46
47struct ExtensionEventRouter::EventListener {
48  RenderProcessHost* process;
49  std::string extension_id;
50
51  explicit EventListener(RenderProcessHost* process,
52                         const std::string& extension_id)
53      : process(process), extension_id(extension_id) {}
54
55  bool operator<(const EventListener& that) const {
56    if (process < that.process)
57      return true;
58    if (process == that.process && extension_id < that.extension_id)
59      return true;
60    return false;
61  }
62};
63
64// static
65bool ExtensionEventRouter::CanCrossIncognito(Profile* profile,
66                                             const std::string& extension_id) {
67  const Extension* extension =
68      profile->GetExtensionService()->GetExtensionById(extension_id, false);
69  return CanCrossIncognito(profile, extension);
70}
71
72// static
73bool ExtensionEventRouter::CanCrossIncognito(Profile* profile,
74                                             const Extension* extension) {
75  // We allow the extension to see events and data from another profile iff it
76  // uses "spanning" behavior and it has incognito access. "split" mode
77  // extensions only see events for a matching profile.
78  return
79      (profile->GetExtensionService()->IsIncognitoEnabled(extension->id()) &&
80       !extension->incognito_split_mode());
81}
82
83ExtensionEventRouter::ExtensionEventRouter(Profile* profile)
84    : profile_(profile),
85      extension_devtools_manager_(profile->GetExtensionDevToolsManager()) {
86  registrar_.Add(this, NotificationType::RENDERER_PROCESS_TERMINATED,
87                 NotificationService::AllSources());
88  registrar_.Add(this, NotificationType::RENDERER_PROCESS_CLOSED,
89                 NotificationService::AllSources());
90}
91
92ExtensionEventRouter::~ExtensionEventRouter() {
93}
94
95void ExtensionEventRouter::AddEventListener(
96    const std::string& event_name,
97    RenderProcessHost* process,
98    const std::string& extension_id) {
99  EventListener listener(process, extension_id);
100  DCHECK_EQ(listeners_[event_name].count(listener), 0u) << event_name;
101  listeners_[event_name].insert(listener);
102
103  if (extension_devtools_manager_.get())
104    extension_devtools_manager_->AddEventListener(event_name, process->id());
105
106  // We lazily tell the TaskManager to start updating when listeners to the
107  // processes.onUpdated event arrive.
108  if (event_name.compare(extension_processes_api_constants::kOnUpdated) == 0)
109    ExtensionProcessesEventRouter::GetInstance()->ListenerAdded();
110}
111
112void ExtensionEventRouter::RemoveEventListener(
113    const std::string& event_name,
114    RenderProcessHost* process,
115    const std::string& extension_id) {
116  EventListener listener(process, extension_id);
117  DCHECK_EQ(listeners_[event_name].count(listener), 1u) <<
118      " PID=" << process->id() << " extension=" << extension_id <<
119      " event=" << event_name;
120  listeners_[event_name].erase(listener);
121  // Note: extension_id may point to data in the now-deleted listeners_ object.
122  // Do not use.
123
124  if (extension_devtools_manager_.get())
125    extension_devtools_manager_->RemoveEventListener(event_name, process->id());
126
127  // If a processes.onUpdated event listener is removed (or a process with one
128  // exits), then we let the TaskManager know that it has one fewer listener.
129  if (event_name.compare(extension_processes_api_constants::kOnUpdated) == 0)
130    ExtensionProcessesEventRouter::GetInstance()->ListenerRemoved();
131
132  BrowserThread::PostTask(
133      BrowserThread::IO, FROM_HERE,
134      NewRunnableFunction(
135          &NotifyEventListenerRemovedOnIOThread,
136          profile_->GetRuntimeId(), listener.extension_id, event_name));
137}
138
139bool ExtensionEventRouter::HasEventListener(const std::string& event_name) {
140  return (listeners_.find(event_name) != listeners_.end() &&
141          !listeners_[event_name].empty());
142}
143
144bool ExtensionEventRouter::ExtensionHasEventListener(
145    const std::string& extension_id, const std::string& event_name) {
146  ListenerMap::iterator it = listeners_.find(event_name);
147  if (it == listeners_.end())
148    return false;
149
150  std::set<EventListener>& listeners = it->second;
151  for (std::set<EventListener>::iterator listener = listeners.begin();
152       listener != listeners.end(); ++listener) {
153    if (listener->extension_id == extension_id)
154      return true;
155  }
156  return false;
157}
158
159void ExtensionEventRouter::DispatchEventToRenderers(
160    const std::string& event_name, const std::string& event_args,
161    Profile* restrict_to_profile, const GURL& event_url) {
162  DispatchEventImpl("", event_name, event_args, restrict_to_profile, event_url);
163}
164
165void ExtensionEventRouter::DispatchEventToExtension(
166    const std::string& extension_id,
167    const std::string& event_name, const std::string& event_args,
168    Profile* restrict_to_profile, const GURL& event_url) {
169  DCHECK(!extension_id.empty());
170  DispatchEventImpl(extension_id, event_name, event_args, restrict_to_profile,
171                    event_url);
172}
173
174void ExtensionEventRouter::DispatchEventImpl(
175    const std::string& extension_id,
176    const std::string& event_name, const std::string& event_args,
177    Profile* restrict_to_profile, const GURL& event_url) {
178  if (!profile_)
179    return;
180
181  // We don't expect to get events from a completely different profile.
182  DCHECK(!restrict_to_profile || profile_->IsSameProfile(restrict_to_profile));
183
184  ListenerMap::iterator it = listeners_.find(event_name);
185  if (it == listeners_.end())
186    return;
187
188  std::set<EventListener>& listeners = it->second;
189  ExtensionService* service = profile_->GetExtensionService();
190
191  // Send the event only to renderers that are listening for it.
192  for (std::set<EventListener>::iterator listener = listeners.begin();
193       listener != listeners.end(); ++listener) {
194    if (!ChildProcessSecurityPolicy::GetInstance()->
195            HasExtensionBindings(listener->process->id())) {
196      // Don't send browser-level events to unprivileged processes.
197      continue;
198    }
199
200    if (!extension_id.empty() && extension_id != listener->extension_id)
201      continue;
202
203    // Is this event from a different profile than the renderer (ie, an
204    // incognito tab event sent to a normal process, or vice versa).
205    bool cross_incognito = restrict_to_profile &&
206        listener->process->profile() != restrict_to_profile;
207    const Extension* extension = service->GetExtensionById(
208        listener->extension_id, false);
209    if (cross_incognito && !service->CanCrossIncognito(extension))
210      continue;
211
212    DispatchEvent(listener->process, listener->extension_id,
213                  event_name, event_args, event_url);
214  }
215}
216
217void ExtensionEventRouter::Observe(NotificationType type,
218                                   const NotificationSource& source,
219                                   const NotificationDetails& details) {
220  switch (type.value) {
221    case NotificationType::RENDERER_PROCESS_TERMINATED:
222    case NotificationType::RENDERER_PROCESS_CLOSED: {
223      RenderProcessHost* renderer = Source<RenderProcessHost>(source).ptr();
224      // Remove all event listeners associated with this renderer
225      for (ListenerMap::iterator it = listeners_.begin();
226           it != listeners_.end(); ) {
227        ListenerMap::iterator current_it = it++;
228        for (std::set<EventListener>::iterator jt = current_it->second.begin();
229             jt != current_it->second.end(); ) {
230          std::set<EventListener>::iterator current_jt = jt++;
231          if (current_jt->process == renderer) {
232            RemoveEventListener(current_it->first,
233                                current_jt->process,
234                                current_jt->extension_id);
235          }
236        }
237      }
238      break;
239    }
240    default:
241      NOTREACHED();
242      return;
243  }
244}
245