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