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 21 namespace { 22 23 const char kDispatchEvent[] = "Event.dispatchJSON"; 24 25 static 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 37 static 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 47 struct 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 65 bool 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 73 bool 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 83 ExtensionEventRouter::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 92 ExtensionEventRouter::~ExtensionEventRouter() { 93 } 94 95 void 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 112 void 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 139 bool ExtensionEventRouter::HasEventListener(const std::string& event_name) { 140 return (listeners_.find(event_name) != listeners_.end() && 141 !listeners_[event_name].empty()); 142 } 143 144 bool 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 159 void 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 165 void 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 174 void 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 217 void 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