OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "chrome/browser/extensions/extension_process_manager.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/command_line.h" | |
9 #include "base/lazy_instance.h" | |
10 #include "base/logging.h" | |
11 #include "base/message_loop/message_loop.h" | |
12 #include "base/metrics/histogram.h" | |
13 #include "base/stl_util.h" | |
14 #include "base/strings/string_number_conversions.h" | |
15 #include "base/time/time.h" | |
16 #include "chrome/browser/chrome_notification_types.h" | |
17 #include "chrome/browser/extensions/api/runtime/runtime_api.h" | |
18 #include "chrome/browser/extensions/extension_host.h" | |
19 #include "chrome/browser/extensions/extension_service.h" | |
20 #include "chrome/browser/extensions/extension_system.h" | |
21 #include "chrome/browser/extensions/extension_util.h" | |
22 #include "chrome/common/extensions/extension.h" | |
23 #include "chrome/common/extensions/extension_messages.h" | |
24 #include "content/public/browser/browser_context.h" | |
25 #include "content/public/browser/browser_thread.h" | |
26 #include "content/public/browser/devtools_agent_host.h" | |
27 #include "content/public/browser/devtools_manager.h" | |
28 #include "content/public/browser/notification_service.h" | |
29 #include "content/public/browser/render_process_host.h" | |
30 #include "content/public/browser/render_view_host.h" | |
31 #include "content/public/browser/site_instance.h" | |
32 #include "content/public/browser/web_contents.h" | |
33 #include "content/public/browser/web_contents_delegate.h" | |
34 #include "content/public/browser/web_contents_observer.h" | |
35 #include "content/public/browser/web_contents_user_data.h" | |
36 #include "content/public/common/renderer_preferences.h" | |
37 #include "extensions/browser/extensions_browser_client.h" | |
38 #include "extensions/browser/view_type_utils.h" | |
39 #include "extensions/common/manifest_handlers/background_info.h" | |
40 #include "extensions/common/manifest_handlers/incognito_info.h" | |
41 #include "extensions/common/switches.h" | |
42 | |
43 #if defined(OS_MACOSX) | |
44 #include "chrome/browser/extensions/extension_host_mac.h" | |
45 #endif | |
46 | |
47 using content::BrowserContext; | |
48 using content::RenderViewHost; | |
49 using content::SiteInstance; | |
50 using content::WebContents; | |
51 using extensions::BackgroundInfo; | |
52 using extensions::BackgroundManifestHandler; | |
53 using extensions::Extension; | |
54 using extensions::ExtensionHost; | |
55 using extensions::ExtensionsBrowserClient; | |
56 using extensions::ExtensionSystem; | |
57 | |
58 class RenderViewHostDestructionObserver; | |
59 DEFINE_WEB_CONTENTS_USER_DATA_KEY(RenderViewHostDestructionObserver); | |
60 | |
61 namespace { | |
62 | |
63 std::string GetExtensionID(RenderViewHost* render_view_host) { | |
64 // This works for both apps and extensions because the site has been | |
65 // normalized to the extension URL for apps. | |
66 if (!render_view_host->GetSiteInstance()) | |
67 return std::string(); | |
68 | |
69 return render_view_host->GetSiteInstance()->GetSiteURL().host(); | |
70 } | |
71 | |
72 void OnRenderViewHostUnregistered(BrowserContext* context, | |
73 RenderViewHost* render_view_host) { | |
74 content::NotificationService::current()->Notify( | |
75 chrome::NOTIFICATION_EXTENSION_VIEW_UNREGISTERED, | |
76 content::Source<BrowserContext>(context), | |
77 content::Details<RenderViewHost>(render_view_host)); | |
78 } | |
79 | |
80 // Incognito profiles use this process manager. It is mostly a shim that decides | |
81 // whether to fall back on the original profile's ExtensionProcessManager based | |
82 // on whether a given extension uses "split" or "spanning" incognito behavior. | |
83 class IncognitoExtensionProcessManager : public ExtensionProcessManager { | |
84 public: | |
85 IncognitoExtensionProcessManager(BrowserContext* incognito_context, | |
86 BrowserContext* original_context); | |
87 virtual ~IncognitoExtensionProcessManager(); | |
88 virtual ExtensionHost* CreateBackgroundHost(const Extension* extension, | |
89 const GURL& url) OVERRIDE; | |
90 virtual SiteInstance* GetSiteInstanceForURL(const GURL& url) OVERRIDE; | |
91 | |
92 private: | |
93 // Returns true if the extension is allowed to run in incognito mode. | |
94 bool IsIncognitoEnabled(const Extension* extension); | |
95 | |
96 ExtensionProcessManager* original_manager_; | |
97 | |
98 DISALLOW_COPY_AND_ASSIGN(IncognitoExtensionProcessManager); | |
99 }; | |
100 | |
101 static void CreateBackgroundHostForExtensionLoad( | |
102 ExtensionProcessManager* manager, const Extension* extension) { | |
103 DVLOG(1) << "CreateBackgroundHostForExtensionLoad"; | |
104 if (BackgroundInfo::HasPersistentBackgroundPage(extension)) | |
105 manager->CreateBackgroundHost(extension, | |
106 BackgroundInfo::GetBackgroundURL(extension)); | |
107 } | |
108 | |
109 } // namespace | |
110 | |
111 class RenderViewHostDestructionObserver | |
112 : public content::WebContentsObserver, | |
113 public content::WebContentsUserData<RenderViewHostDestructionObserver> { | |
114 public: | |
115 virtual ~RenderViewHostDestructionObserver() {} | |
116 | |
117 private: | |
118 explicit RenderViewHostDestructionObserver(WebContents* web_contents) | |
119 : WebContentsObserver(web_contents) { | |
120 BrowserContext* context = web_contents->GetBrowserContext(); | |
121 process_manager_ = | |
122 ExtensionSystem::GetForBrowserContext(context)->process_manager(); | |
123 } | |
124 | |
125 friend class content::WebContentsUserData<RenderViewHostDestructionObserver>; | |
126 | |
127 // content::WebContentsObserver overrides. | |
128 virtual void RenderViewDeleted(RenderViewHost* render_view_host) OVERRIDE { | |
129 process_manager_->UnregisterRenderViewHost(render_view_host); | |
130 } | |
131 | |
132 ExtensionProcessManager* process_manager_; | |
133 | |
134 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestructionObserver); | |
135 }; | |
136 | |
137 struct ExtensionProcessManager::BackgroundPageData { | |
138 // The count of things keeping the lazy background page alive. | |
139 int lazy_keepalive_count; | |
140 | |
141 // This is used with the ShouldSuspend message, to ensure that the extension | |
142 // remained idle between sending the message and receiving the ack. | |
143 int close_sequence_id; | |
144 | |
145 // True if the page responded to the ShouldSuspend message and is currently | |
146 // dispatching the suspend event. During this time any events that arrive will | |
147 // cancel the suspend process and an onSuspendCanceled event will be | |
148 // dispatched to the page. | |
149 bool is_closing; | |
150 | |
151 // Keeps track of when this page was last suspended. Used for perf metrics. | |
152 linked_ptr<base::ElapsedTimer> since_suspended; | |
153 | |
154 BackgroundPageData() | |
155 : lazy_keepalive_count(0), close_sequence_id(0), is_closing(false) {} | |
156 }; | |
157 | |
158 // | |
159 // ExtensionProcessManager | |
160 // | |
161 | |
162 // static | |
163 ExtensionProcessManager* ExtensionProcessManager::Create( | |
164 BrowserContext* context) { | |
165 if (context->IsOffTheRecord()) { | |
166 BrowserContext* original_context = | |
167 ExtensionsBrowserClient::Get()->GetOriginalContext(context); | |
168 return new IncognitoExtensionProcessManager(context, original_context); | |
169 } | |
170 return new ExtensionProcessManager(context, context); | |
171 } | |
172 | |
173 ExtensionProcessManager::ExtensionProcessManager( | |
174 BrowserContext* context, | |
175 BrowserContext* original_context) | |
176 : site_instance_(SiteInstance::Create(context)), | |
177 defer_background_host_creation_(false), | |
178 startup_background_hosts_created_(false), | |
179 devtools_callback_(base::Bind( | |
180 &ExtensionProcessManager::OnDevToolsStateChanged, | |
181 base::Unretained(this))), | |
182 weak_ptr_factory_(this) { | |
183 registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY, | |
184 content::Source<BrowserContext>(original_context)); | |
185 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, | |
186 content::Source<BrowserContext>(original_context)); | |
187 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, | |
188 content::Source<BrowserContext>(original_context)); | |
189 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED, | |
190 content::Source<BrowserContext>(context)); | |
191 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE, | |
192 content::Source<BrowserContext>(context)); | |
193 registrar_.Add(this, content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED, | |
194 content::NotificationService::AllSources()); | |
195 registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_CONNECTED, | |
196 content::NotificationService::AllSources()); | |
197 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CREATED, | |
198 content::Source<BrowserContext>(original_context)); | |
199 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED, | |
200 content::Source<BrowserContext>(context)); | |
201 if (context->IsOffTheRecord()) { | |
202 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED, | |
203 content::Source<BrowserContext>(original_context)); | |
204 } | |
205 | |
206 event_page_idle_time_ = base::TimeDelta::FromSeconds(10); | |
207 unsigned idle_time_sec = 0; | |
208 if (base::StringToUint(CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | |
209 extensions::switches::kEventPageIdleTime), &idle_time_sec)) { | |
210 event_page_idle_time_ = base::TimeDelta::FromSeconds(idle_time_sec); | |
211 } | |
212 event_page_suspending_time_ = base::TimeDelta::FromSeconds(5); | |
213 unsigned suspending_time_sec = 0; | |
214 if (base::StringToUint(CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | |
215 extensions::switches::kEventPageSuspendingTime), | |
216 &suspending_time_sec)) { | |
217 event_page_suspending_time_ = | |
218 base::TimeDelta::FromSeconds(suspending_time_sec); | |
219 } | |
220 | |
221 content::DevToolsManager::GetInstance()->AddAgentStateCallback( | |
222 devtools_callback_); | |
223 } | |
224 | |
225 ExtensionProcessManager::~ExtensionProcessManager() { | |
226 CloseBackgroundHosts(); | |
227 DCHECK(background_hosts_.empty()); | |
228 content::DevToolsManager::GetInstance()->RemoveAgentStateCallback( | |
229 devtools_callback_); | |
230 } | |
231 | |
232 const ExtensionProcessManager::ViewSet | |
233 ExtensionProcessManager::GetAllViews() const { | |
234 ViewSet result; | |
235 for (ExtensionRenderViews::const_iterator iter = | |
236 all_extension_views_.begin(); | |
237 iter != all_extension_views_.end(); ++iter) { | |
238 result.insert(iter->first); | |
239 } | |
240 return result; | |
241 } | |
242 | |
243 ExtensionHost* ExtensionProcessManager::CreateBackgroundHost( | |
244 const Extension* extension, const GURL& url) { | |
245 DVLOG(1) << "CreateBackgroundHost " << url.spec(); | |
246 // Hosted apps are taken care of from BackgroundContentsService. Ignore them | |
247 // here. | |
248 if (extension->is_hosted_app()) | |
249 return NULL; | |
250 | |
251 // Don't create multiple background hosts for an extension. | |
252 if (ExtensionHost* host = GetBackgroundHostForExtension(extension->id())) | |
253 return host; // TODO(kalman): return NULL here? It might break things... | |
254 | |
255 ExtensionHost* host = | |
256 #if defined(OS_MACOSX) | |
257 new extensions::ExtensionHostMac( | |
258 extension, GetSiteInstanceForURL(url), url, | |
259 extensions::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE); | |
260 #else | |
261 new ExtensionHost(extension, GetSiteInstanceForURL(url), url, | |
262 extensions::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE); | |
263 #endif | |
264 | |
265 host->CreateRenderViewSoon(); | |
266 OnBackgroundHostCreated(host); | |
267 return host; | |
268 } | |
269 | |
270 ExtensionHost* ExtensionProcessManager::GetBackgroundHostForExtension( | |
271 const std::string& extension_id) { | |
272 for (ExtensionHostSet::iterator iter = background_hosts_.begin(); | |
273 iter != background_hosts_.end(); ++iter) { | |
274 ExtensionHost* host = *iter; | |
275 if (host->extension_id() == extension_id) | |
276 return host; | |
277 } | |
278 return NULL; | |
279 } | |
280 | |
281 std::set<RenderViewHost*> | |
282 ExtensionProcessManager::GetRenderViewHostsForExtension( | |
283 const std::string& extension_id) { | |
284 std::set<RenderViewHost*> result; | |
285 | |
286 SiteInstance* site_instance = GetSiteInstanceForURL( | |
287 Extension::GetBaseURLFromExtensionId(extension_id)); | |
288 if (!site_instance) | |
289 return result; | |
290 | |
291 // Gather up all the views for that site. | |
292 for (ExtensionRenderViews::iterator view = all_extension_views_.begin(); | |
293 view != all_extension_views_.end(); ++view) { | |
294 if (view->first->GetSiteInstance() == site_instance) | |
295 result.insert(view->first); | |
296 } | |
297 | |
298 return result; | |
299 } | |
300 | |
301 const Extension* ExtensionProcessManager::GetExtensionForRenderViewHost( | |
302 RenderViewHost* render_view_host) { | |
303 if (!render_view_host->GetSiteInstance()) | |
304 return NULL; | |
305 | |
306 ExtensionService* service = ExtensionSystem::GetForBrowserContext( | |
307 GetBrowserContext())->extension_service(); | |
308 if (!service) | |
309 return NULL; | |
310 | |
311 return service->extensions()->GetByID(GetExtensionID(render_view_host)); | |
312 } | |
313 | |
314 void ExtensionProcessManager::UnregisterRenderViewHost( | |
315 RenderViewHost* render_view_host) { | |
316 ExtensionRenderViews::iterator view = | |
317 all_extension_views_.find(render_view_host); | |
318 if (view == all_extension_views_.end()) | |
319 return; | |
320 | |
321 OnRenderViewHostUnregistered(GetBrowserContext(), render_view_host); | |
322 extensions::ViewType view_type = view->second; | |
323 all_extension_views_.erase(view); | |
324 | |
325 // Keepalive count, balanced in RegisterRenderViewHost. | |
326 if (view_type != extensions::VIEW_TYPE_INVALID && | |
327 view_type != extensions::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) { | |
328 const Extension* extension = GetExtensionForRenderViewHost( | |
329 render_view_host); | |
330 if (extension) | |
331 DecrementLazyKeepaliveCount(extension); | |
332 } | |
333 } | |
334 | |
335 void ExtensionProcessManager::RegisterRenderViewHost( | |
336 RenderViewHost* render_view_host) { | |
337 const Extension* extension = GetExtensionForRenderViewHost( | |
338 render_view_host); | |
339 if (!extension) | |
340 return; | |
341 | |
342 WebContents* web_contents = WebContents::FromRenderViewHost(render_view_host); | |
343 all_extension_views_[render_view_host] = | |
344 extensions::GetViewType(web_contents); | |
345 | |
346 // Keep the lazy background page alive as long as any non-background-page | |
347 // extension views are visible. Keepalive count balanced in | |
348 // UnregisterRenderViewHost. | |
349 IncrementLazyKeepaliveCountForView(render_view_host); | |
350 } | |
351 | |
352 SiteInstance* ExtensionProcessManager::GetSiteInstanceForURL(const GURL& url) { | |
353 return site_instance_->GetRelatedSiteInstance(url); | |
354 } | |
355 | |
356 bool ExtensionProcessManager::IsBackgroundHostClosing( | |
357 const std::string& extension_id) { | |
358 ExtensionHost* host = GetBackgroundHostForExtension(extension_id); | |
359 return (host && background_page_data_[extension_id].is_closing); | |
360 } | |
361 | |
362 int ExtensionProcessManager::GetLazyKeepaliveCount(const Extension* extension) { | |
363 if (!BackgroundInfo::HasLazyBackgroundPage(extension)) | |
364 return 0; | |
365 | |
366 return background_page_data_[extension->id()].lazy_keepalive_count; | |
367 } | |
368 | |
369 int ExtensionProcessManager::IncrementLazyKeepaliveCount( | |
370 const Extension* extension) { | |
371 if (!BackgroundInfo::HasLazyBackgroundPage(extension)) | |
372 return 0; | |
373 | |
374 int& count = background_page_data_[extension->id()].lazy_keepalive_count; | |
375 if (++count == 1) | |
376 OnLazyBackgroundPageActive(extension->id()); | |
377 | |
378 return count; | |
379 } | |
380 | |
381 int ExtensionProcessManager::DecrementLazyKeepaliveCount( | |
382 const Extension* extension) { | |
383 if (!BackgroundInfo::HasLazyBackgroundPage(extension)) | |
384 return 0; | |
385 | |
386 int& count = background_page_data_[extension->id()].lazy_keepalive_count; | |
387 DCHECK_GT(count, 0); | |
388 | |
389 // If we reach a zero keepalive count when the lazy background page is about | |
390 // to be closed, incrementing close_sequence_id will cancel the close | |
391 // sequence and cause the background page to linger. So check is_closing | |
392 // before initiating another close sequence. | |
393 if (--count == 0 && !background_page_data_[extension->id()].is_closing) { | |
394 base::MessageLoop::current()->PostDelayedTask( | |
395 FROM_HERE, | |
396 base::Bind(&ExtensionProcessManager::OnLazyBackgroundPageIdle, | |
397 weak_ptr_factory_.GetWeakPtr(), extension->id(), | |
398 ++background_page_data_[extension->id()].close_sequence_id), | |
399 event_page_idle_time_); | |
400 } | |
401 | |
402 return count; | |
403 } | |
404 | |
405 void ExtensionProcessManager::IncrementLazyKeepaliveCountForView( | |
406 RenderViewHost* render_view_host) { | |
407 WebContents* web_contents = | |
408 WebContents::FromRenderViewHost(render_view_host); | |
409 extensions::ViewType view_type = extensions::GetViewType(web_contents); | |
410 if (view_type != extensions::VIEW_TYPE_INVALID && | |
411 view_type != extensions::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) { | |
412 const Extension* extension = GetExtensionForRenderViewHost( | |
413 render_view_host); | |
414 if (extension) | |
415 IncrementLazyKeepaliveCount(extension); | |
416 } | |
417 } | |
418 | |
419 void ExtensionProcessManager::OnLazyBackgroundPageIdle( | |
420 const std::string& extension_id, int sequence_id) { | |
421 ExtensionHost* host = GetBackgroundHostForExtension(extension_id); | |
422 if (host && !background_page_data_[extension_id].is_closing && | |
423 sequence_id == background_page_data_[extension_id].close_sequence_id) { | |
424 // Tell the renderer we are about to close. This is a simple ping that the | |
425 // renderer will respond to. The purpose is to control sequencing: if the | |
426 // extension remains idle until the renderer responds with an ACK, then we | |
427 // know that the extension process is ready to shut down. If our | |
428 // close_sequence_id has already changed, then we would ignore the | |
429 // ShouldSuspendAck, so we don't send the ping. | |
430 host->render_view_host()->Send(new ExtensionMsg_ShouldSuspend( | |
431 extension_id, sequence_id)); | |
432 } | |
433 } | |
434 | |
435 void ExtensionProcessManager::OnLazyBackgroundPageActive( | |
436 const std::string& extension_id) { | |
437 ExtensionHost* host = GetBackgroundHostForExtension(extension_id); | |
438 if (host && !background_page_data_[extension_id].is_closing) { | |
439 // Cancel the current close sequence by changing the close_sequence_id, | |
440 // which causes us to ignore the next ShouldSuspendAck. | |
441 ++background_page_data_[extension_id].close_sequence_id; | |
442 } | |
443 } | |
444 | |
445 void ExtensionProcessManager::OnShouldSuspendAck( | |
446 const std::string& extension_id, int sequence_id) { | |
447 ExtensionHost* host = GetBackgroundHostForExtension(extension_id); | |
448 if (host && | |
449 sequence_id == background_page_data_[extension_id].close_sequence_id) { | |
450 host->render_view_host()->Send(new ExtensionMsg_Suspend(extension_id)); | |
451 } | |
452 } | |
453 | |
454 void ExtensionProcessManager::OnSuspendAck(const std::string& extension_id) { | |
455 background_page_data_[extension_id].is_closing = true; | |
456 int sequence_id = background_page_data_[extension_id].close_sequence_id; | |
457 base::MessageLoop::current()->PostDelayedTask( | |
458 FROM_HERE, | |
459 base::Bind(&ExtensionProcessManager::CloseLazyBackgroundPageNow, | |
460 weak_ptr_factory_.GetWeakPtr(), extension_id, sequence_id), | |
461 event_page_suspending_time_); | |
462 } | |
463 | |
464 void ExtensionProcessManager::CloseLazyBackgroundPageNow( | |
465 const std::string& extension_id, int sequence_id) { | |
466 ExtensionHost* host = GetBackgroundHostForExtension(extension_id); | |
467 if (host && | |
468 sequence_id == background_page_data_[extension_id].close_sequence_id) { | |
469 ExtensionHost* host = GetBackgroundHostForExtension(extension_id); | |
470 if (host) | |
471 CloseBackgroundHost(host); | |
472 } | |
473 } | |
474 | |
475 void ExtensionProcessManager::OnNetworkRequestStarted( | |
476 RenderViewHost* render_view_host) { | |
477 ExtensionHost* host = GetBackgroundHostForExtension( | |
478 GetExtensionID(render_view_host)); | |
479 if (host && host->render_view_host() == render_view_host) | |
480 IncrementLazyKeepaliveCount(host->extension()); | |
481 } | |
482 | |
483 void ExtensionProcessManager::OnNetworkRequestDone( | |
484 RenderViewHost* render_view_host) { | |
485 ExtensionHost* host = GetBackgroundHostForExtension( | |
486 GetExtensionID(render_view_host)); | |
487 if (host && host->render_view_host() == render_view_host) | |
488 DecrementLazyKeepaliveCount(host->extension()); | |
489 } | |
490 | |
491 void ExtensionProcessManager::CancelSuspend(const Extension* extension) { | |
492 bool& is_closing = background_page_data_[extension->id()].is_closing; | |
493 ExtensionHost* host = GetBackgroundHostForExtension(extension->id()); | |
494 if (host && is_closing) { | |
495 is_closing = false; | |
496 host->render_view_host()->Send( | |
497 new ExtensionMsg_CancelSuspend(extension->id())); | |
498 // This increment / decrement is to simulate an instantaneous event. This | |
499 // has the effect of invalidating close_sequence_id, preventing any in | |
500 // progress closes from completing and starting a new close process if | |
501 // necessary. | |
502 IncrementLazyKeepaliveCount(extension); | |
503 DecrementLazyKeepaliveCount(extension); | |
504 } | |
505 } | |
506 | |
507 void ExtensionProcessManager::DeferBackgroundHostCreation(bool defer) { | |
508 bool previous = defer_background_host_creation_; | |
509 defer_background_host_creation_ = defer; | |
510 | |
511 // If we were deferred, and we switch to non-deferred, then create the | |
512 // background hosts. | |
513 if (previous && !defer_background_host_creation_) | |
514 CreateBackgroundHostsForProfileStartup(); | |
515 } | |
516 | |
517 void ExtensionProcessManager::OnBrowserWindowReady() { | |
518 ExtensionService* service = ExtensionSystem::GetForBrowserContext( | |
519 GetBrowserContext())->extension_service(); | |
520 // On Chrome OS, a login screen is implemented as a browser. | |
521 // This browser has no extension service. In this case, | |
522 // service will be NULL. | |
523 if (!service || !service->is_ready()) | |
524 return; | |
525 | |
526 CreateBackgroundHostsForProfileStartup(); | |
527 } | |
528 | |
529 content::BrowserContext* ExtensionProcessManager::GetBrowserContext() const { | |
530 return site_instance_->GetBrowserContext(); | |
531 } | |
532 | |
533 void ExtensionProcessManager::Observe( | |
534 int type, | |
535 const content::NotificationSource& source, | |
536 const content::NotificationDetails& details) { | |
537 switch (type) { | |
538 case chrome::NOTIFICATION_EXTENSIONS_READY: | |
539 case chrome::NOTIFICATION_PROFILE_CREATED: { | |
540 CreateBackgroundHostsForProfileStartup(); | |
541 break; | |
542 } | |
543 | |
544 case chrome::NOTIFICATION_EXTENSION_LOADED: { | |
545 BrowserContext* context = content::Source<BrowserContext>(source).ptr(); | |
546 ExtensionService* service = | |
547 ExtensionSystem::GetForBrowserContext(context)->extension_service(); | |
548 if (service->is_ready()) { | |
549 const Extension* extension = | |
550 content::Details<const Extension>(details).ptr(); | |
551 CreateBackgroundHostForExtensionLoad(this, extension); | |
552 } | |
553 break; | |
554 } | |
555 | |
556 case chrome::NOTIFICATION_EXTENSION_UNLOADED: { | |
557 const Extension* extension = | |
558 content::Details<extensions::UnloadedExtensionInfo>( | |
559 details)->extension; | |
560 for (ExtensionHostSet::iterator iter = background_hosts_.begin(); | |
561 iter != background_hosts_.end(); ++iter) { | |
562 ExtensionHost* host = *iter; | |
563 if (host->extension_id() == extension->id()) { | |
564 CloseBackgroundHost(host); | |
565 break; | |
566 } | |
567 } | |
568 UnregisterExtension(extension->id()); | |
569 break; | |
570 } | |
571 | |
572 case chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED: { | |
573 ExtensionHost* host = content::Details<ExtensionHost>(details).ptr(); | |
574 if (background_hosts_.erase(host)) { | |
575 ClearBackgroundPageData(host->extension()->id()); | |
576 background_page_data_[host->extension()->id()].since_suspended.reset( | |
577 new base::ElapsedTimer()); | |
578 } | |
579 break; | |
580 } | |
581 | |
582 case chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE: { | |
583 ExtensionHost* host = content::Details<ExtensionHost>(details).ptr(); | |
584 if (host->extension_host_type() == | |
585 extensions::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) { | |
586 CloseBackgroundHost(host); | |
587 } | |
588 break; | |
589 } | |
590 | |
591 case content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED: { | |
592 // We get this notification both for new WebContents and when one | |
593 // has its RenderViewHost replaced (e.g. when a user does a cross-site | |
594 // navigation away from an extension URL). For the replaced case, we must | |
595 // unregister the old RVH so it doesn't count as an active view that would | |
596 // keep the event page alive. | |
597 WebContents* contents = content::Source<WebContents>(source).ptr(); | |
598 if (contents->GetBrowserContext() != GetBrowserContext()) | |
599 break; | |
600 | |
601 typedef std::pair<RenderViewHost*, RenderViewHost*> RVHPair; | |
602 RVHPair* switched_details = content::Details<RVHPair>(details).ptr(); | |
603 if (switched_details->first) | |
604 UnregisterRenderViewHost(switched_details->first); | |
605 | |
606 // The above will unregister a RVH when it gets swapped out with a new | |
607 // one. However we need to watch the WebContents to know when a RVH is | |
608 // deleted because the WebContents has gone away. | |
609 RenderViewHostDestructionObserver::CreateForWebContents(contents); | |
610 RegisterRenderViewHost(switched_details->second); | |
611 break; | |
612 } | |
613 | |
614 case content::NOTIFICATION_WEB_CONTENTS_CONNECTED: { | |
615 WebContents* contents = content::Source<WebContents>(source).ptr(); | |
616 if (contents->GetBrowserContext() != GetBrowserContext()) | |
617 break; | |
618 const Extension* extension = GetExtensionForRenderViewHost( | |
619 contents->GetRenderViewHost()); | |
620 if (!extension) | |
621 return; | |
622 | |
623 // RegisterRenderViewHost is called too early (before the process is | |
624 // available), so we need to wait until now to notify. | |
625 content::NotificationService::current()->Notify( | |
626 chrome::NOTIFICATION_EXTENSION_VIEW_REGISTERED, | |
627 content::Source<BrowserContext>(GetBrowserContext()), | |
628 content::Details<RenderViewHost>(contents->GetRenderViewHost())); | |
629 break; | |
630 } | |
631 | |
632 case chrome::NOTIFICATION_PROFILE_DESTROYED: { | |
633 // Close background hosts when the last browser is closed so that they | |
634 // have time to shutdown various objects on different threads. Our | |
635 // destructor is called too late in the shutdown sequence. | |
636 CloseBackgroundHosts(); | |
637 break; | |
638 } | |
639 | |
640 default: | |
641 NOTREACHED(); | |
642 } | |
643 } | |
644 | |
645 void ExtensionProcessManager::OnDevToolsStateChanged( | |
646 content::DevToolsAgentHost* agent_host, bool attached) { | |
647 RenderViewHost* rvh = agent_host->GetRenderViewHost(); | |
648 // Ignore unrelated notifications. | |
649 if (!rvh || | |
650 rvh->GetSiteInstance()->GetProcess()->GetBrowserContext() != | |
651 GetBrowserContext()) | |
652 return; | |
653 if (extensions::GetViewType(WebContents::FromRenderViewHost(rvh)) != | |
654 extensions::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) | |
655 return; | |
656 const Extension* extension = GetExtensionForRenderViewHost(rvh); | |
657 if (!extension) | |
658 return; | |
659 if (attached) { | |
660 // Keep the lazy background page alive while it's being inspected. | |
661 CancelSuspend(extension); | |
662 IncrementLazyKeepaliveCount(extension); | |
663 } else { | |
664 DecrementLazyKeepaliveCount(extension); | |
665 } | |
666 } | |
667 | |
668 void ExtensionProcessManager::CreateBackgroundHostsForProfileStartup() { | |
669 if (startup_background_hosts_created_) | |
670 return; | |
671 | |
672 // Don't load background hosts now if the loading should be deferred. | |
673 // Instead they will be loaded when a browser window for this profile | |
674 // (or an incognito profile from this profile) is ready, or when | |
675 // DeferBackgroundHostCreation is called with false. | |
676 if (DeferLoadingBackgroundHosts()) | |
677 return; | |
678 | |
679 ExtensionService* service = ExtensionSystem::GetForBrowserContext( | |
680 GetBrowserContext())->extension_service(); | |
681 DCHECK(service); | |
682 for (ExtensionSet::const_iterator extension = service->extensions()->begin(); | |
683 extension != service->extensions()->end(); ++extension) { | |
684 CreateBackgroundHostForExtensionLoad(this, extension->get()); | |
685 | |
686 extensions::RuntimeEventRouter::DispatchOnStartupEvent( | |
687 GetBrowserContext(), (*extension)->id()); | |
688 } | |
689 startup_background_hosts_created_ = true; | |
690 | |
691 // Background pages should only be loaded once. To prevent any further loads | |
692 // occurring, we remove the notification listeners. | |
693 BrowserContext* original_context = | |
694 ExtensionsBrowserClient::Get()->GetOriginalContext(GetBrowserContext()); | |
695 if (registrar_.IsRegistered( | |
696 this, | |
697 chrome::NOTIFICATION_PROFILE_CREATED, | |
698 content::Source<BrowserContext>(original_context))) { | |
699 registrar_.Remove(this, | |
700 chrome::NOTIFICATION_PROFILE_CREATED, | |
701 content::Source<BrowserContext>(original_context)); | |
702 } | |
703 if (registrar_.IsRegistered( | |
704 this, | |
705 chrome::NOTIFICATION_EXTENSIONS_READY, | |
706 content::Source<BrowserContext>(original_context))) { | |
707 registrar_.Remove(this, | |
708 chrome::NOTIFICATION_EXTENSIONS_READY, | |
709 content::Source<BrowserContext>(original_context)); | |
710 } | |
711 } | |
712 | |
713 void ExtensionProcessManager::OnBackgroundHostCreated(ExtensionHost* host) { | |
714 DCHECK_EQ(GetBrowserContext(), host->browser_context()); | |
715 background_hosts_.insert(host); | |
716 | |
717 if (BackgroundInfo::HasLazyBackgroundPage(host->extension())) { | |
718 linked_ptr<base::ElapsedTimer> since_suspended( | |
719 background_page_data_[host->extension()->id()]. | |
720 since_suspended.release()); | |
721 if (since_suspended.get()) { | |
722 UMA_HISTOGRAM_LONG_TIMES("Extensions.EventPageIdleTime", | |
723 since_suspended->Elapsed()); | |
724 } | |
725 } | |
726 } | |
727 | |
728 void ExtensionProcessManager::CloseBackgroundHost(ExtensionHost* host) { | |
729 CHECK(host->extension_host_type() == | |
730 extensions::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE); | |
731 delete host; | |
732 // |host| should deregister itself from our structures. | |
733 CHECK(background_hosts_.find(host) == background_hosts_.end()); | |
734 } | |
735 | |
736 void ExtensionProcessManager::CloseBackgroundHosts() { | |
737 for (ExtensionHostSet::iterator iter = background_hosts_.begin(); | |
738 iter != background_hosts_.end(); ) { | |
739 ExtensionHostSet::iterator current = iter++; | |
740 delete *current; | |
741 } | |
742 } | |
743 | |
744 void ExtensionProcessManager::UnregisterExtension( | |
745 const std::string& extension_id) { | |
746 // The lazy_keepalive_count may be greater than zero at this point because | |
747 // RenderViewHosts are still alive. During extension reloading, they will | |
748 // decrement the lazy_keepalive_count to negative for the new extension | |
749 // instance when they are destroyed. Since we are erasing the background page | |
750 // data for the unloaded extension, unregister the RenderViewHosts too. | |
751 BrowserContext* context = GetBrowserContext(); | |
752 for (ExtensionRenderViews::iterator it = all_extension_views_.begin(); | |
753 it != all_extension_views_.end(); ) { | |
754 if (GetExtensionID(it->first) == extension_id) { | |
755 OnRenderViewHostUnregistered(context, it->first); | |
756 all_extension_views_.erase(it++); | |
757 } else { | |
758 ++it; | |
759 } | |
760 } | |
761 | |
762 background_page_data_.erase(extension_id); | |
763 } | |
764 | |
765 void ExtensionProcessManager::ClearBackgroundPageData( | |
766 const std::string& extension_id) { | |
767 background_page_data_.erase(extension_id); | |
768 | |
769 // Re-register all RenderViews for this extension. We do this to restore | |
770 // the lazy_keepalive_count (if any) to properly reflect the number of open | |
771 // views. | |
772 for (ExtensionRenderViews::const_iterator it = all_extension_views_.begin(); | |
773 it != all_extension_views_.end(); ++it) { | |
774 if (GetExtensionID(it->first) == extension_id) | |
775 IncrementLazyKeepaliveCountForView(it->first); | |
776 } | |
777 } | |
778 | |
779 bool ExtensionProcessManager::DeferLoadingBackgroundHosts() const { | |
780 // Don't load background hosts now if the loading should be deferred. | |
781 if (defer_background_host_creation_) | |
782 return true; | |
783 | |
784 // The extensions embedder may have special rules about background hosts. | |
785 return ExtensionsBrowserClient::Get()->DeferLoadingBackgroundHosts( | |
786 GetBrowserContext()); | |
787 } | |
788 | |
789 // | |
790 // IncognitoExtensionProcessManager | |
791 // | |
792 | |
793 IncognitoExtensionProcessManager::IncognitoExtensionProcessManager( | |
794 BrowserContext* incognito_context, | |
795 BrowserContext* original_context) | |
796 : ExtensionProcessManager(incognito_context, original_context), | |
797 original_manager_(extensions::ExtensionSystem::GetForBrowserContext( | |
798 original_context)->process_manager()) { | |
799 DCHECK(incognito_context->IsOffTheRecord()); | |
800 | |
801 // The original profile will have its own ExtensionProcessManager to | |
802 // load the background pages of the spanning extensions. This process | |
803 // manager need only worry about the split mode extensions, which is handled | |
804 // in the NOTIFICATION_BROWSER_WINDOW_READY notification handler. | |
805 registrar_.Remove(this, chrome::NOTIFICATION_EXTENSIONS_READY, | |
806 content::Source<BrowserContext>(original_context)); | |
807 registrar_.Remove(this, chrome::NOTIFICATION_PROFILE_CREATED, | |
808 content::Source<BrowserContext>(original_context)); | |
809 } | |
810 | |
811 IncognitoExtensionProcessManager::~IncognitoExtensionProcessManager() { | |
812 // TODO(yoz): This cleanup code belongs in the MenuManager. | |
813 // Remove "incognito" "split" mode context menu items. | |
814 ExtensionService* service = ExtensionSystem::GetForBrowserContext( | |
815 GetBrowserContext())->extension_service(); | |
816 if (service) | |
817 service->menu_manager()->RemoveAllIncognitoContextItems(); | |
818 } | |
819 | |
820 ExtensionHost* IncognitoExtensionProcessManager::CreateBackgroundHost( | |
821 const Extension* extension, const GURL& url) { | |
822 if (extensions::IncognitoInfo::IsSplitMode(extension)) { | |
823 if (IsIncognitoEnabled(extension)) | |
824 return ExtensionProcessManager::CreateBackgroundHost(extension, url); | |
825 } else { | |
826 // Do nothing. If an extension is spanning, then its original-profile | |
827 // background page is shared with incognito, so we don't create another. | |
828 } | |
829 return NULL; | |
830 } | |
831 | |
832 SiteInstance* IncognitoExtensionProcessManager::GetSiteInstanceForURL( | |
833 const GURL& url) { | |
834 ExtensionService* service = ExtensionSystem::GetForBrowserContext( | |
835 GetBrowserContext())->extension_service(); | |
836 if (service) { | |
837 const Extension* extension = | |
838 service->extensions()->GetExtensionOrAppByURL(url); | |
839 if (extension && | |
840 !extensions::IncognitoInfo::IsSplitMode(extension)) { | |
841 return original_manager_->GetSiteInstanceForURL(url); | |
842 } | |
843 } | |
844 return ExtensionProcessManager::GetSiteInstanceForURL(url); | |
845 } | |
846 | |
847 bool IncognitoExtensionProcessManager::IsIncognitoEnabled( | |
848 const Extension* extension) { | |
849 // Keep in sync with duplicate in extension_info_map.cc. | |
850 ExtensionService* service = ExtensionSystem::GetForBrowserContext( | |
851 GetBrowserContext())->extension_service(); | |
852 return extension_util::IsIncognitoEnabled(extension->id(), service); | |
853 } | |
OLD | NEW |