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