Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(279)

Side by Side Diff: chrome/browser/extensions/extension_event_router.cc

Issue 7672009: Lazy creating of background pages --enable-lazy-background-pages) (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: use ExtensionEvent for args to DispatchEventImpl Created 9 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/extensions/extension_event_router.h" 5 #include "chrome/browser/extensions/extension_event_router.h"
6 6
7 #include "base/command_line.h"
7 #include "base/values.h" 8 #include "base/values.h"
8 #include "chrome/browser/extensions/extension_devtools_manager.h" 9 #include "chrome/browser/extensions/extension_devtools_manager.h"
10 #include "chrome/browser/extensions/extension_host.h"
9 #include "chrome/browser/extensions/extension_processes_api.h" 11 #include "chrome/browser/extensions/extension_processes_api.h"
10 #include "chrome/browser/extensions/extension_processes_api_constants.h" 12 #include "chrome/browser/extensions/extension_processes_api_constants.h"
11 #include "chrome/browser/extensions/extension_service.h" 13 #include "chrome/browser/extensions/extension_service.h"
12 #include "chrome/browser/extensions/extension_tabs_module.h" 14 #include "chrome/browser/extensions/extension_tabs_module.h"
13 #include "chrome/browser/extensions/extension_webrequest_api.h" 15 #include "chrome/browser/extensions/extension_webrequest_api.h"
14 #include "chrome/browser/profiles/profile.h" 16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/common/chrome_switches.h"
15 #include "chrome/common/extensions/extension.h" 18 #include "chrome/common/extensions/extension.h"
16 #include "chrome/common/extensions/extension_messages.h" 19 #include "chrome/common/extensions/extension_messages.h"
17 #include "content/browser/child_process_security_policy.h" 20 #include "content/browser/child_process_security_policy.h"
18 #include "content/browser/renderer_host/render_process_host.h" 21 #include "content/browser/renderer_host/render_process_host.h"
19 #include "content/common/notification_service.h" 22 #include "content/common/notification_service.h"
20 23
21 namespace { 24 namespace {
22 25
23 const char kDispatchEvent[] = "Event.dispatchJSON"; 26 const char kDispatchEvent[] = "Event.dispatchJSON";
24 27
(...skipping 16 matching lines...) Expand all
41 44
42 bool operator<(const EventListener& that) const { 45 bool operator<(const EventListener& that) const {
43 if (process < that.process) 46 if (process < that.process)
44 return true; 47 return true;
45 if (process == that.process && extension_id < that.extension_id) 48 if (process == that.process && extension_id < that.extension_id)
46 return true; 49 return true;
47 return false; 50 return false;
48 } 51 }
49 }; 52 };
50 53
54 struct ExtensionEventRouter::ExtensionEvent {
55 std::string extension_id;
56 std::string event_name;
57 std::string event_args;
58 GURL event_url;
59 Profile* restrict_to_profile;
60 std::string cross_incognito_args;
61
62 ExtensionEvent(const std::string& extension_id,
63 const std::string& event_name,
64 const std::string& event_args,
65 const GURL& event_url,
66 Profile* restrict_to_profile,
67 const std::string cross_incognito_args)
68 : extension_id(extension_id),
69 event_name(event_name),
70 event_args(event_args),
71 event_url(event_url),
72 restrict_to_profile(restrict_to_profile),
73 cross_incognito_args(cross_incognito_args) {}
74 };
75
51 // static 76 // static
52 void ExtensionEventRouter::DispatchEvent(IPC::Message::Sender* ipc_sender, 77 void ExtensionEventRouter::DispatchEvent(IPC::Message::Sender* ipc_sender,
53 const std::string& extension_id, 78 const std::string& extension_id,
54 const std::string& event_name, 79 const std::string& event_name,
55 const std::string& event_args, 80 const std::string& event_args,
56 const GURL& event_url) { 81 const GURL& event_url) {
57 ListValue args; 82 ListValue args;
58 args.Set(0, Value::CreateStringValue(event_name)); 83 args.Set(0, Value::CreateStringValue(event_name));
59 args.Set(1, Value::CreateStringValue(event_args)); 84 args.Set(1, Value::CreateStringValue(event_args));
60 ipc_sender->Send(new ExtensionMsg_MessageInvoke(MSG_ROUTING_CONTROL, 85 ipc_sender->Send(new ExtensionMsg_MessageInvoke(MSG_ROUTING_CONTROL,
61 extension_id, kDispatchEvent, args, event_url)); 86 extension_id, kDispatchEvent, args, event_url));
62 } 87 }
63 88
64 ExtensionEventRouter::ExtensionEventRouter(Profile* profile) 89 ExtensionEventRouter::ExtensionEventRouter(Profile* profile)
65 : profile_(profile), 90 : profile_(profile),
66 extension_devtools_manager_(profile->GetExtensionDevToolsManager()) { 91 extension_devtools_manager_(profile->GetExtensionDevToolsManager()),
92 ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)) {
67 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, 93 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
68 NotificationService::AllSources()); 94 NotificationService::AllSources());
69 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, 95 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
70 NotificationService::AllSources()); 96 NotificationService::AllSources());
97 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING,
98 Source<Profile>(profile_));
99 // TODO(tessamac): also get notified for background page crash/failure.
71 } 100 }
72 101
73 ExtensionEventRouter::~ExtensionEventRouter() { 102 ExtensionEventRouter::~ExtensionEventRouter() {}
74 }
75 103
76 void ExtensionEventRouter::AddEventListener( 104 void ExtensionEventRouter::AddEventListener(
77 const std::string& event_name, 105 const std::string& event_name,
78 RenderProcessHost* process, 106 RenderProcessHost* process,
79 const std::string& extension_id) { 107 const std::string& extension_id) {
80 EventListener listener(process, extension_id); 108 EventListener listener(process, extension_id);
81 DCHECK_EQ(listeners_[event_name].count(listener), 0u) << event_name; 109 DCHECK_EQ(listeners_[event_name].count(listener), 0u) << event_name;
82 listeners_[event_name].insert(listener); 110 listeners_[event_name].insert(listener);
83 111
84 if (extension_devtools_manager_.get()) 112 if (extension_devtools_manager_.get())
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
135 return true; 163 return true;
136 } 164 }
137 return false; 165 return false;
138 } 166 }
139 167
140 void ExtensionEventRouter::DispatchEventToRenderers( 168 void ExtensionEventRouter::DispatchEventToRenderers(
141 const std::string& event_name, 169 const std::string& event_name,
142 const std::string& event_args, 170 const std::string& event_args,
143 Profile* restrict_to_profile, 171 Profile* restrict_to_profile,
144 const GURL& event_url) { 172 const GURL& event_url) {
145 DispatchEventImpl("", event_name, event_args, restrict_to_profile, "", 173 ExtensionEvent event("", event_name, event_args, event_url,
146 event_url); 174 restrict_to_profile, "");
175 DispatchEventImpl(event, false);
147 } 176 }
148 177
149 void ExtensionEventRouter::DispatchEventToExtension( 178 void ExtensionEventRouter::DispatchEventToExtension(
150 const std::string& extension_id, 179 const std::string& extension_id,
151 const std::string& event_name, 180 const std::string& event_name,
152 const std::string& event_args, 181 const std::string& event_args,
153 Profile* restrict_to_profile, 182 Profile* restrict_to_profile,
154 const GURL& event_url) { 183 const GURL& event_url) {
155 DCHECK(!extension_id.empty()); 184 DCHECK(!extension_id.empty());
156 DispatchEventImpl(extension_id, event_name, event_args, restrict_to_profile, 185 ExtensionEvent event(extension_id, event_name, event_args, event_url,
157 "", event_url); 186 restrict_to_profile, "");
187 DispatchEventImpl(event, false);
158 } 188 }
159 189
160 void ExtensionEventRouter::DispatchEventsToRenderersAcrossIncognito( 190 void ExtensionEventRouter::DispatchEventsToRenderersAcrossIncognito(
161 const std::string& event_name, 191 const std::string& event_name,
162 const std::string& event_args, 192 const std::string& event_args,
163 Profile* restrict_to_profile, 193 Profile* restrict_to_profile,
164 const std::string& cross_incognito_args, 194 const std::string& cross_incognito_args,
165 const GURL& event_url) { 195 const GURL& event_url) {
166 DispatchEventImpl("", event_name, event_args, restrict_to_profile, 196 ExtensionEvent event("", event_name, event_args, event_url,
167 cross_incognito_args, event_url); 197 restrict_to_profile, cross_incognito_args);
198 DispatchEventImpl(event, false);
199 }
200
201 bool ExtensionEventRouter::CanDispatchEventNow(
202 const std::string& extension_id) {
203 if (!CommandLine::ForCurrentProcess()->HasSwitch(
204 switches::kEnableLazyBackgroundPages))
205 return true;
206
207 if (extension_id.empty())
208 // TODO(tessamac): Create all background pages. Wait for all to be loaded?
209 // or dispatch event to each extension when it's ready?
210 return true;
211
212 const Extension* extension = profile_->GetExtensionService()->
213 GetExtensionById(extension_id, false); // exclude disabled extensions
214 if (extension && extension->background_url().is_valid()) {
215 ExtensionProcessManager* pm = profile_->GetExtensionProcessManager();
216 if (!pm->GetBackgroundHostForExtension(extension)) {
217 pm->CreateBackgroundHost(extension, extension->background_url());
218 return false;
219 }
220 }
221
222 return true;
168 } 223 }
169 224
170 void ExtensionEventRouter::DispatchEventImpl( 225 void ExtensionEventRouter::DispatchEventImpl(
171 const std::string& extension_id, 226 const ExtensionEvent& event, bool was_pending) {
172 const std::string& event_name,
173 const std::string& event_args,
174 Profile* restrict_to_profile,
175 const std::string& cross_incognito_args,
176 const GURL& event_url) {
177 if (!profile_) 227 if (!profile_)
178 return; 228 return;
179 229
180 // We don't expect to get events from a completely different profile. 230 // We don't expect to get events from a completely different profile.
181 DCHECK(!restrict_to_profile || profile_->IsSameProfile(restrict_to_profile)); 231 DCHECK(!event.restrict_to_profile ||
232 profile_->IsSameProfile(event.restrict_to_profile));
182 233
183 ListenerMap::iterator it = listeners_.find(event_name); 234 if (!CanDispatchEventNow(event.extension_id)) {
235 // Events should not be made pending twice. This may happen if the
236 // background page is shutdown before we finish dispatching pending events.
237 CHECK(!was_pending);
Matt Perry 2011/08/26 00:13:00 Since it can actually happen in a legit case, don'
Tessa MacDuff 2011/08/26 01:12:53 Actually it should never happen. I feel pretty co
238 // TODO(tessamac): make sure Background Page notification doesn't
239 // happen before the event is added to the pending list.
240 AppendEvent(event);
241 return;
242 }
243
244 ListenerMap::iterator it = listeners_.find(event.event_name);
184 if (it == listeners_.end()) 245 if (it == listeners_.end())
185 return; 246 return;
186 247
187 std::set<EventListener>& listeners = it->second; 248 std::set<EventListener>& listeners = it->second;
188 ExtensionService* service = profile_->GetExtensionService(); 249 ExtensionService* service = profile_->GetExtensionService();
189 250
190 // Send the event only to renderers that are listening for it. 251 // Send the event only to renderers that are listening for it.
191 for (std::set<EventListener>::iterator listener = listeners.begin(); 252 for (std::set<EventListener>::iterator listener = listeners.begin();
192 listener != listeners.end(); ++listener) { 253 listener != listeners.end(); ++listener) {
193 if (!ChildProcessSecurityPolicy::GetInstance()-> 254 if (!ChildProcessSecurityPolicy::GetInstance()->
194 HasExtensionBindings(listener->process->id())) { 255 HasExtensionBindings(listener->process->id())) {
195 // Don't send browser-level events to unprivileged processes. 256 // Don't send browser-level events to unprivileged processes.
196 continue; 257 continue;
197 } 258 }
198 259
199 if (!extension_id.empty() && extension_id != listener->extension_id) 260 if (!event.extension_id.empty() &&
261 event.extension_id != listener->extension_id)
200 continue; 262 continue;
201 263
202 // Is this event from a different profile than the renderer (ie, an 264 // Is this event from a different profile than the renderer (ie, an
203 // incognito tab event sent to a normal process, or vice versa). 265 // incognito tab event sent to a normal process, or vice versa).
204 bool cross_incognito = restrict_to_profile && 266 bool cross_incognito = event.restrict_to_profile &&
205 listener->process->browser_context() != restrict_to_profile; 267 listener->process->browser_context() != event.restrict_to_profile;
206 const Extension* extension = service->GetExtensionById( 268 const Extension* extension = service->GetExtensionById(
207 listener->extension_id, false); 269 listener->extension_id, false);
208 // Send the event with different arguments to extensions that can't 270 // Send the event with different arguments to extensions that can't
209 // cross incognito, if necessary. 271 // cross incognito, if necessary.
210 if (cross_incognito && !service->CanCrossIncognito(extension)) { 272 if (cross_incognito && !service->CanCrossIncognito(extension)) {
211 if (!cross_incognito_args.empty()) { 273 if (!event.cross_incognito_args.empty()) {
212 DispatchEvent(listener->process, listener->extension_id, 274 DispatchEvent(listener->process, listener->extension_id,
213 event_name, cross_incognito_args, event_url); 275 event.event_name, event.cross_incognito_args,
276 event.event_url);
214 } 277 }
215 continue; 278 continue;
216 } 279 }
217 280
218 DispatchEvent(listener->process, listener->extension_id, 281 DispatchEvent(listener->process, listener->extension_id,
219 event_name, event_args, event_url); 282 event.event_name, event.event_args, event.event_url);
220 } 283 }
221 } 284 }
222 285
286 void ExtensionEventRouter::AppendEvent(const ExtensionEvent& pending_event) {
287 PendingTasksList* tasks_list = NULL;
288 PendingTasksPerExtMap::iterator it =
289 pending_tasks_.find(pending_event.extension_id);
290 if (it == pending_tasks_.end()) {
291 tasks_list = new PendingTasksList();
292 pending_tasks_[pending_event.extension_id] =
293 linked_ptr<PendingTasksList>(tasks_list);
294 } else {
295 tasks_list = it->second.get();
296 }
297
298 tasks_list->push_back(linked_ptr<Task>(
299 task_factory_.NewRunnableMethod(&ExtensionEventRouter::DispatchEventImpl,
300 pending_event, true)));
301 }
302
303 void ExtensionEventRouter::DispatchPendingEvents(
304 const std::string &extension_id) {
305 // Find the list of pending tasks for this extension.
306 PendingTasksPerExtMap::const_iterator map_it =
307 pending_tasks_.find(extension_id);
308 if (map_it == pending_tasks_.end())
309 return;
310
311 PendingTasksList* tasks_list = map_it->second.get();
312 for (PendingTasksList::const_iterator it = tasks_list->begin();
313 it != tasks_list->end(); ++it)
314 it->get()->Run();
315
316 // Delete list.
317 tasks_list->clear();
318 pending_tasks_.erase(extension_id);
319 }
320
223 void ExtensionEventRouter::Observe(int type, 321 void ExtensionEventRouter::Observe(int type,
224 const NotificationSource& source, 322 const NotificationSource& source,
225 const NotificationDetails& details) { 323 const NotificationDetails& details) {
226 switch (type) { 324 switch (type) {
227 case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: 325 case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED:
228 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: { 326 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: {
229 RenderProcessHost* renderer = Source<RenderProcessHost>(source).ptr(); 327 RenderProcessHost* renderer = Source<RenderProcessHost>(source).ptr();
230 // Remove all event listeners associated with this renderer 328 // Remove all event listeners associated with this renderer
231 for (ListenerMap::iterator it = listeners_.begin(); 329 for (ListenerMap::iterator it = listeners_.begin();
232 it != listeners_.end(); ) { 330 it != listeners_.end(); ) {
233 ListenerMap::iterator current_it = it++; 331 ListenerMap::iterator current_it = it++;
234 for (std::set<EventListener>::iterator jt = current_it->second.begin(); 332 for (std::set<EventListener>::iterator jt = current_it->second.begin();
235 jt != current_it->second.end(); ) { 333 jt != current_it->second.end(); ) {
236 std::set<EventListener>::iterator current_jt = jt++; 334 std::set<EventListener>::iterator current_jt = jt++;
237 if (current_jt->process == renderer) { 335 if (current_jt->process == renderer) {
238 RemoveEventListener(current_it->first, 336 RemoveEventListener(current_it->first,
239 current_jt->process, 337 current_jt->process,
240 current_jt->extension_id); 338 current_jt->extension_id);
241 } 339 }
242 } 340 }
243 } 341 }
244 break; 342 break;
245 } 343 }
344 case chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING: {
345 // TODO: dispatch events in queue. ExtensionHost is in the details.
346 ExtensionHost* eh = Details<ExtensionHost>(details).ptr();
347 DispatchPendingEvents(eh->extension_id());
348 break;
349 }
350 // TODO(tessamac): if background page crashed/failed clear queue.
246 default: 351 default:
247 NOTREACHED(); 352 NOTREACHED();
248 return; 353 return;
249 } 354 }
250 } 355 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698