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