Home | History | Annotate | Download | only in extensions
      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