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