1 // Copyright (c) 2011 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 "chrome/browser/ui/browser_window.h" 8 #include "content/browser/browsing_instance.h" 9 #if defined(OS_MACOSX) 10 #include "chrome/browser/extensions/extension_host_mac.h" 11 #endif 12 #include "chrome/browser/extensions/extension_host.h" 13 #include "chrome/browser/extensions/extension_service.h" 14 #include "chrome/browser/profiles/profile.h" 15 #include "chrome/browser/ui/browser.h" 16 #include "chrome/common/extensions/extension.h" 17 #include "chrome/common/extensions/extension_action.h" 18 #include "chrome/common/extensions/extension_messages.h" 19 #include "chrome/common/url_constants.h" 20 #include "content/browser/renderer_host/render_view_host.h" 21 #include "content/browser/site_instance.h" 22 #include "content/browser/tab_contents/tab_contents.h" 23 #include "content/common/notification_service.h" 24 #include "content/common/notification_type.h" 25 26 namespace { 27 28 // Incognito profiles use this process manager. It is mostly a shim that decides 29 // whether to fall back on the original profile's ExtensionProcessManager based 30 // on whether a given extension uses "split" or "spanning" incognito behavior. 31 class IncognitoExtensionProcessManager : public ExtensionProcessManager { 32 public: 33 explicit IncognitoExtensionProcessManager(Profile* profile); 34 virtual ~IncognitoExtensionProcessManager() {} 35 virtual ExtensionHost* CreateView(const Extension* extension, 36 const GURL& url, 37 Browser* browser, 38 ViewType::Type view_type); 39 virtual void CreateBackgroundHost(const Extension* extension, 40 const GURL& url); 41 virtual SiteInstance* GetSiteInstanceForURL(const GURL& url); 42 virtual RenderProcessHost* GetExtensionProcess(const GURL& url); 43 44 private: 45 // NotificationObserver: 46 virtual void Observe(NotificationType type, 47 const NotificationSource& source, 48 const NotificationDetails& details); 49 50 // Returns the extension for an URL, which can either be a chrome-extension 51 // URL or a web app URL. 52 const Extension* GetExtensionOrAppByURL(const GURL& url); 53 54 // Returns true if the extension is allowed to run in incognito mode. 55 bool IsIncognitoEnabled(const Extension* extension); 56 57 ExtensionProcessManager* original_manager_; 58 }; 59 60 static void CreateBackgroundHost( 61 ExtensionProcessManager* manager, const Extension* extension) { 62 // Start the process for the master page, if it exists. 63 if (extension->background_url().is_valid()) 64 manager->CreateBackgroundHost(extension, extension->background_url()); 65 } 66 67 static void CreateBackgroundHosts( 68 ExtensionProcessManager* manager, const ExtensionList* extensions) { 69 for (ExtensionList::const_iterator extension = extensions->begin(); 70 extension != extensions->end(); ++extension) { 71 CreateBackgroundHost(manager, *extension); 72 } 73 } 74 75 } // namespace 76 77 extern bool g_log_bug53991; 78 79 // 80 // ExtensionProcessManager 81 // 82 83 // static 84 ExtensionProcessManager* ExtensionProcessManager::Create(Profile* profile) { 85 return (profile->IsOffTheRecord()) ? 86 new IncognitoExtensionProcessManager(profile) : 87 new ExtensionProcessManager(profile); 88 } 89 90 ExtensionProcessManager::ExtensionProcessManager(Profile* profile) 91 : browsing_instance_(new BrowsingInstance(profile)) { 92 Profile* original_profile = profile->GetOriginalProfile(); 93 registrar_.Add(this, NotificationType::EXTENSIONS_READY, 94 Source<Profile>(original_profile)); 95 registrar_.Add(this, NotificationType::EXTENSION_LOADED, 96 Source<Profile>(original_profile)); 97 registrar_.Add(this, NotificationType::EXTENSION_UNLOADED, 98 Source<Profile>(original_profile)); 99 registrar_.Add(this, NotificationType::EXTENSION_HOST_DESTROYED, 100 Source<Profile>(profile)); 101 registrar_.Add(this, NotificationType::RENDERER_PROCESS_TERMINATED, 102 NotificationService::AllSources()); 103 registrar_.Add(this, NotificationType::RENDERER_PROCESS_CLOSED, 104 NotificationService::AllSources()); 105 registrar_.Add(this, NotificationType::APP_TERMINATING, 106 NotificationService::AllSources()); 107 } 108 109 ExtensionProcessManager::~ExtensionProcessManager() { 110 VLOG_IF(1, g_log_bug53991) << "~ExtensionProcessManager: " << this; 111 CloseBackgroundHosts(); 112 DCHECK(background_hosts_.empty()); 113 } 114 115 ExtensionHost* ExtensionProcessManager::CreateView(const Extension* extension, 116 const GURL& url, 117 Browser* browser, 118 ViewType::Type view_type) { 119 DCHECK(extension); 120 // A NULL browser may only be given for pop-up views. 121 DCHECK(browser || (!browser && view_type == ViewType::EXTENSION_POPUP)); 122 ExtensionHost* host = 123 #if defined(OS_MACOSX) 124 new ExtensionHostMac(extension, GetSiteInstanceForURL(url), url, 125 view_type); 126 #else 127 new ExtensionHost(extension, GetSiteInstanceForURL(url), url, view_type); 128 #endif 129 host->CreateView(browser); 130 OnExtensionHostCreated(host, false); 131 return host; 132 } 133 134 ExtensionHost* ExtensionProcessManager::CreateView(const GURL& url, 135 Browser* browser, 136 ViewType::Type view_type) { 137 // A NULL browser may only be given for pop-up views. 138 DCHECK(browser || (!browser && view_type == ViewType::EXTENSION_POPUP)); 139 ExtensionService* service = 140 browsing_instance_->profile()->GetExtensionService(); 141 if (service) { 142 const Extension* extension = service->GetExtensionByURL(url); 143 if (extension) 144 return CreateView(extension, url, browser, view_type); 145 } 146 return NULL; 147 } 148 149 ExtensionHost* ExtensionProcessManager::CreatePopup(const Extension* extension, 150 const GURL& url, 151 Browser* browser) { 152 return CreateView(extension, url, browser, ViewType::EXTENSION_POPUP); 153 } 154 155 ExtensionHost* ExtensionProcessManager::CreatePopup(const GURL& url, 156 Browser* browser) { 157 return CreateView(url, browser, ViewType::EXTENSION_POPUP); 158 } 159 160 ExtensionHost* ExtensionProcessManager::CreateInfobar( 161 const Extension* extension, const GURL& url, Browser* browser) { 162 return CreateView(extension, url, browser, ViewType::EXTENSION_INFOBAR); 163 } 164 165 ExtensionHost* ExtensionProcessManager::CreateInfobar(const GURL& url, 166 Browser* browser) { 167 return CreateView(url, browser, ViewType::EXTENSION_INFOBAR); 168 } 169 170 void ExtensionProcessManager::CreateBackgroundHost( 171 const Extension* extension, const GURL& url) { 172 // Hosted apps are taken care of from BackgroundContentsService. Ignore them 173 // here. 174 if (extension->is_hosted_app()) 175 return; 176 177 // Don't create multiple background hosts for an extension. 178 if (GetBackgroundHostForExtension(extension)) 179 return; 180 181 ExtensionHost* host = 182 #if defined(OS_MACOSX) 183 new ExtensionHostMac(extension, GetSiteInstanceForURL(url), url, 184 ViewType::EXTENSION_BACKGROUND_PAGE); 185 #else 186 new ExtensionHost(extension, GetSiteInstanceForURL(url), url, 187 ViewType::EXTENSION_BACKGROUND_PAGE); 188 #endif 189 190 host->CreateRenderViewSoon(NULL); // create a RenderViewHost with no view 191 OnExtensionHostCreated(host, true); 192 } 193 194 void ExtensionProcessManager::OpenOptionsPage(const Extension* extension, 195 Browser* browser) { 196 DCHECK(!extension->options_url().is_empty()); 197 198 // Force the options page to open in non-OTR window, because it won't be 199 // able to save settings from OTR. 200 if (!browser || browser->profile()->IsOffTheRecord()) { 201 browser = Browser::GetOrCreateTabbedBrowser( 202 browsing_instance_->profile()->GetOriginalProfile()); 203 } 204 205 browser->OpenURL(extension->options_url(), GURL(), SINGLETON_TAB, 206 PageTransition::LINK); 207 browser->window()->Show(); 208 browser->GetSelectedTabContents()->Activate(); 209 } 210 211 ExtensionHost* ExtensionProcessManager::GetBackgroundHostForExtension( 212 const Extension* extension) { 213 for (ExtensionHostSet::iterator iter = background_hosts_.begin(); 214 iter != background_hosts_.end(); ++iter) { 215 ExtensionHost* host = *iter; 216 if (host->extension() == extension) 217 return host; 218 } 219 return NULL; 220 } 221 222 void ExtensionProcessManager::RegisterExtensionProcess( 223 const std::string& extension_id, int process_id) { 224 // TODO(mpcomplete): This is the only place we actually read process_ids_. 225 // Is it necessary? 226 ProcessIDMap::const_iterator it = process_ids_.find(extension_id); 227 if (it != process_ids_.end() && (*it).second == process_id) 228 return; 229 230 // Extension ids should get removed from the map before the process ids get 231 // reused from a dead renderer. 232 DCHECK(it == process_ids_.end()); 233 process_ids_[extension_id] = process_id; 234 235 ExtensionService* extension_service = 236 browsing_instance_->profile()->GetExtensionService(); 237 238 std::vector<std::string> page_action_ids; 239 const Extension* extension = 240 extension_service->GetExtensionById(extension_id, false); 241 if (extension->page_action()) 242 page_action_ids.push_back(extension->page_action()->id()); 243 244 RenderProcessHost* rph = RenderProcessHost::FromID(process_id); 245 rph->Send(new ExtensionMsg_UpdatePageActions(extension_id, page_action_ids)); 246 } 247 248 void ExtensionProcessManager::UnregisterExtensionProcess(int process_id) { 249 ProcessIDMap::iterator it = process_ids_.begin(); 250 while (it != process_ids_.end()) { 251 if (it->second == process_id) 252 process_ids_.erase(it++); 253 else 254 ++it; 255 } 256 } 257 258 RenderProcessHost* ExtensionProcessManager::GetExtensionProcess( 259 const GURL& url) { 260 if (!browsing_instance_->HasSiteInstance(url)) 261 return NULL; 262 scoped_refptr<SiteInstance> site( 263 browsing_instance_->GetSiteInstanceForURL(url)); 264 if (site->HasProcess()) 265 return site->GetProcess(); 266 return NULL; 267 } 268 269 RenderProcessHost* ExtensionProcessManager::GetExtensionProcess( 270 const std::string& extension_id) { 271 return GetExtensionProcess( 272 Extension::GetBaseURLFromExtensionId(extension_id)); 273 } 274 275 SiteInstance* ExtensionProcessManager::GetSiteInstanceForURL(const GURL& url) { 276 return browsing_instance_->GetSiteInstanceForURL(url); 277 } 278 279 bool ExtensionProcessManager::HasExtensionHost(ExtensionHost* host) const { 280 return all_hosts_.find(host) != all_hosts_.end(); 281 } 282 283 void ExtensionProcessManager::Observe(NotificationType type, 284 const NotificationSource& source, 285 const NotificationDetails& details) { 286 switch (type.value) { 287 case NotificationType::EXTENSIONS_READY: { 288 CreateBackgroundHosts(this, 289 Source<Profile>(source).ptr()->GetExtensionService()->extensions()); 290 break; 291 } 292 293 case NotificationType::EXTENSION_LOADED: { 294 ExtensionService* service = 295 Source<Profile>(source).ptr()->GetExtensionService(); 296 if (service->is_ready()) { 297 const Extension* extension = Details<const Extension>(details).ptr(); 298 ::CreateBackgroundHost(this, extension); 299 } 300 break; 301 } 302 303 case NotificationType::EXTENSION_UNLOADED: { 304 const Extension* extension = 305 Details<UnloadedExtensionInfo>(details)->extension; 306 for (ExtensionHostSet::iterator iter = background_hosts_.begin(); 307 iter != background_hosts_.end(); ++iter) { 308 ExtensionHost* host = *iter; 309 if (host->extension_id() == extension->id()) { 310 delete host; 311 // |host| should deregister itself from our structures. 312 DCHECK(background_hosts_.find(host) == background_hosts_.end()); 313 break; 314 } 315 } 316 break; 317 } 318 319 case NotificationType::EXTENSION_HOST_DESTROYED: { 320 ExtensionHost* host = Details<ExtensionHost>(details).ptr(); 321 all_hosts_.erase(host); 322 background_hosts_.erase(host); 323 break; 324 } 325 326 case NotificationType::RENDERER_PROCESS_TERMINATED: 327 case NotificationType::RENDERER_PROCESS_CLOSED: { 328 RenderProcessHost* host = Source<RenderProcessHost>(source).ptr(); 329 UnregisterExtensionProcess(host->id()); 330 break; 331 } 332 333 case NotificationType::APP_TERMINATING: { 334 // Close background hosts when the last browser is closed so that they 335 // have time to shutdown various objects on different threads. Our 336 // destructor is called too late in the shutdown sequence. 337 CloseBackgroundHosts(); 338 break; 339 } 340 341 default: 342 NOTREACHED(); 343 } 344 } 345 346 void ExtensionProcessManager::OnExtensionHostCreated(ExtensionHost* host, 347 bool is_background) { 348 DCHECK_EQ(browsing_instance_->profile(), host->profile()); 349 350 all_hosts_.insert(host); 351 if (is_background) 352 background_hosts_.insert(host); 353 NotificationService::current()->Notify( 354 NotificationType::EXTENSION_HOST_CREATED, 355 Source<ExtensionProcessManager>(this), 356 Details<ExtensionHost>(host)); 357 } 358 359 void ExtensionProcessManager::CloseBackgroundHosts() { 360 VLOG_IF(1, g_log_bug53991) << "CloseBackgroundHosts: " << this; 361 for (ExtensionHostSet::iterator iter = background_hosts_.begin(); 362 iter != background_hosts_.end(); ) { 363 ExtensionHostSet::iterator current = iter++; 364 delete *current; 365 } 366 } 367 368 // 369 // IncognitoExtensionProcessManager 370 // 371 372 IncognitoExtensionProcessManager::IncognitoExtensionProcessManager( 373 Profile* profile) 374 : ExtensionProcessManager(profile), 375 original_manager_(profile->GetOriginalProfile()-> 376 GetExtensionProcessManager()) { 377 DCHECK(profile->IsOffTheRecord()); 378 379 registrar_.Add(this, NotificationType::BROWSER_WINDOW_READY, 380 NotificationService::AllSources()); 381 } 382 383 ExtensionHost* IncognitoExtensionProcessManager::CreateView( 384 const Extension* extension, 385 const GURL& url, 386 Browser* browser, 387 ViewType::Type view_type) { 388 if (extension->incognito_split_mode()) { 389 if (IsIncognitoEnabled(extension)) { 390 return ExtensionProcessManager::CreateView(extension, url, 391 browser, view_type); 392 } else { 393 NOTREACHED() << 394 "We shouldn't be trying to create an incognito extension view unless " 395 "it has been enabled for incognito."; 396 return NULL; 397 } 398 } else { 399 return original_manager_->CreateView(extension, url, browser, view_type); 400 } 401 } 402 403 void IncognitoExtensionProcessManager::CreateBackgroundHost( 404 const Extension* extension, const GURL& url) { 405 if (extension->incognito_split_mode()) { 406 if (IsIncognitoEnabled(extension)) 407 ExtensionProcessManager::CreateBackgroundHost(extension, url); 408 } else { 409 // Do nothing. If an extension is spanning, then its original-profile 410 // background page is shared with incognito, so we don't create another. 411 } 412 } 413 414 SiteInstance* IncognitoExtensionProcessManager::GetSiteInstanceForURL( 415 const GURL& url) { 416 const Extension* extension = GetExtensionOrAppByURL(url); 417 if (!extension || extension->incognito_split_mode()) { 418 return ExtensionProcessManager::GetSiteInstanceForURL(url); 419 } else { 420 return original_manager_->GetSiteInstanceForURL(url); 421 } 422 } 423 424 RenderProcessHost* IncognitoExtensionProcessManager::GetExtensionProcess( 425 const GURL& url) { 426 const Extension* extension = GetExtensionOrAppByURL(url); 427 if (!extension || extension->incognito_split_mode()) { 428 return ExtensionProcessManager::GetExtensionProcess(url); 429 } else { 430 return original_manager_->GetExtensionProcess(url); 431 } 432 } 433 434 const Extension* IncognitoExtensionProcessManager::GetExtensionOrAppByURL( 435 const GURL& url) { 436 ExtensionService* service = 437 browsing_instance_->profile()->GetExtensionService(); 438 if (!service) 439 return NULL; 440 return (url.SchemeIs(chrome::kExtensionScheme)) ? 441 service->GetExtensionByURL(url) : service->GetExtensionByWebExtent(url); 442 } 443 444 bool IncognitoExtensionProcessManager::IsIncognitoEnabled( 445 const Extension* extension) { 446 ExtensionService* service = 447 browsing_instance_->profile()->GetExtensionService(); 448 return service && service->IsIncognitoEnabled(extension->id()); 449 } 450 451 void IncognitoExtensionProcessManager::Observe( 452 NotificationType type, 453 const NotificationSource& source, 454 const NotificationDetails& details) { 455 switch (type.value) { 456 case NotificationType::BROWSER_WINDOW_READY: { 457 // We want to spawn our background hosts as soon as the user opens an 458 // incognito window. Watch for new browsers and create the hosts if 459 // it matches our profile. 460 Browser* browser = Source<Browser>(source).ptr(); 461 if (browser->profile() == browsing_instance_->profile()) { 462 // On Chrome OS, a login screen is implemented as a browser. 463 // This browser has no extension service. In this case, 464 // service will be NULL. 465 ExtensionService* service = 466 browsing_instance_->profile()->GetExtensionService(); 467 if (service && service->is_ready()) 468 CreateBackgroundHosts(this, service->extensions()); 469 } 470 break; 471 } 472 default: 473 ExtensionProcessManager::Observe(type, source, details); 474 break; 475 } 476 } 477