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