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