Home | History | Annotate | Download | only in browser
      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 "content/browser/site_instance_impl.h"
      6 
      7 #include "base/command_line.h"
      8 #include "content/browser/browsing_instance.h"
      9 #include "content/browser/child_process_security_policy_impl.h"
     10 #include "content/browser/renderer_host/render_process_host_impl.h"
     11 #include "content/browser/storage_partition_impl.h"
     12 #include "content/public/browser/content_browser_client.h"
     13 #include "content/public/browser/notification_service.h"
     14 #include "content/public/browser/notification_types.h"
     15 #include "content/public/browser/render_process_host_factory.h"
     16 #include "content/public/browser/web_ui_controller_factory.h"
     17 #include "content/public/common/content_switches.h"
     18 #include "content/public/common/url_constants.h"
     19 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
     20 
     21 namespace content {
     22 
     23 static bool IsURLSameAsAnySiteInstance(const GURL& url) {
     24   if (!url.is_valid())
     25     return false;
     26 
     27   // We treat javascript: as the same site as any URL since it is actually
     28   // a modifier on existing pages.
     29   if (url.SchemeIs(chrome::kJavaScriptScheme))
     30     return true;
     31 
     32   return url == GURL(kChromeUICrashURL) ||
     33          url == GURL(kChromeUIKillURL) ||
     34          url == GURL(kChromeUIHangURL) ||
     35          url == GURL(kChromeUIShorthangURL);
     36 }
     37 
     38 const RenderProcessHostFactory*
     39     SiteInstanceImpl::g_render_process_host_factory_ = NULL;
     40 int32 SiteInstanceImpl::next_site_instance_id_ = 1;
     41 
     42 SiteInstanceImpl::SiteInstanceImpl(BrowsingInstance* browsing_instance)
     43     : id_(next_site_instance_id_++),
     44       active_view_count_(0),
     45       browsing_instance_(browsing_instance),
     46       process_(NULL),
     47       has_site_(false) {
     48   DCHECK(browsing_instance);
     49 
     50   registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED,
     51                  NotificationService::AllBrowserContextsAndSources());
     52 }
     53 
     54 SiteInstanceImpl::~SiteInstanceImpl() {
     55   GetContentClient()->browser()->SiteInstanceDeleting(this);
     56 
     57   // Now that no one is referencing us, we can safely remove ourselves from
     58   // the BrowsingInstance.  Any future visits to a page from this site
     59   // (within the same BrowsingInstance) can safely create a new SiteInstance.
     60   if (has_site_)
     61     browsing_instance_->UnregisterSiteInstance(
     62         static_cast<SiteInstance*>(this));
     63 }
     64 
     65 int32 SiteInstanceImpl::GetId() {
     66   return id_;
     67 }
     68 
     69 bool SiteInstanceImpl::HasProcess() const {
     70   if (process_ != NULL)
     71     return true;
     72 
     73   // If we would use process-per-site for this site, also check if there is an
     74   // existing process that we would use if GetProcess() were called.
     75   BrowserContext* browser_context =
     76       browsing_instance_->browser_context();
     77   if (has_site_ &&
     78       RenderProcessHost::ShouldUseProcessPerSite(browser_context, site_) &&
     79       RenderProcessHostImpl::GetProcessHostForSite(browser_context, site_)) {
     80     return true;
     81   }
     82 
     83   return false;
     84 }
     85 
     86 RenderProcessHost* SiteInstanceImpl::GetProcess() {
     87   // TODO(erikkay) It would be nice to ensure that the renderer type had been
     88   // properly set before we get here.  The default tab creation case winds up
     89   // with no site set at this point, so it will default to TYPE_NORMAL.  This
     90   // may not be correct, so we'll wind up potentially creating a process that
     91   // we then throw away, or worse sharing a process with the wrong process type.
     92   // See crbug.com/43448.
     93 
     94   // Create a new process if ours went away or was reused.
     95   if (!process_) {
     96     BrowserContext* browser_context = browsing_instance_->browser_context();
     97 
     98     // If we should use process-per-site mode (either in general or for the
     99     // given site), then look for an existing RenderProcessHost for the site.
    100     bool use_process_per_site = has_site_ &&
    101         RenderProcessHost::ShouldUseProcessPerSite(browser_context, site_);
    102     if (use_process_per_site) {
    103       process_ = RenderProcessHostImpl::GetProcessHostForSite(browser_context,
    104                                                               site_);
    105     }
    106 
    107     // If not (or if none found), see if we should reuse an existing process.
    108     if (!process_ && RenderProcessHostImpl::ShouldTryToUseExistingProcessHost(
    109             browser_context, site_)) {
    110       process_ = RenderProcessHostImpl::GetExistingProcessHost(browser_context,
    111                                                                site_);
    112     }
    113 
    114     // Otherwise (or if that fails), create a new one.
    115     if (!process_) {
    116       if (g_render_process_host_factory_) {
    117         process_ = g_render_process_host_factory_->CreateRenderProcessHost(
    118             browser_context, this);
    119       } else {
    120         StoragePartitionImpl* partition =
    121             static_cast<StoragePartitionImpl*>(
    122                 BrowserContext::GetStoragePartition(browser_context, this));
    123         bool supports_browser_plugin = GetContentClient()->browser()->
    124             SupportsBrowserPlugin(browser_context, site_);
    125         process_ =
    126             new RenderProcessHostImpl(browser_context, partition,
    127                                       supports_browser_plugin,
    128                                       site_.SchemeIs(chrome::kGuestScheme));
    129       }
    130     }
    131     CHECK(process_);
    132 
    133     // If we are using process-per-site, we need to register this process
    134     // for the current site so that we can find it again.  (If no site is set
    135     // at this time, we will register it in SetSite().)
    136     if (use_process_per_site) {
    137       RenderProcessHostImpl::RegisterProcessHostForSite(browser_context,
    138                                                         process_, site_);
    139     }
    140 
    141     GetContentClient()->browser()->SiteInstanceGotProcess(this);
    142 
    143     if (has_site_)
    144       LockToOrigin();
    145   }
    146   DCHECK(process_);
    147 
    148   return process_;
    149 }
    150 
    151 void SiteInstanceImpl::SetSite(const GURL& url) {
    152   // A SiteInstance's site should not change.
    153   // TODO(creis): When following links or script navigations, we can currently
    154   // render pages from other sites in this SiteInstance.  This will eventually
    155   // be fixed, but until then, we should still not set the site of a
    156   // SiteInstance more than once.
    157   DCHECK(!has_site_);
    158 
    159   // Remember that this SiteInstance has been used to load a URL, even if the
    160   // URL is invalid.
    161   has_site_ = true;
    162   BrowserContext* browser_context = browsing_instance_->browser_context();
    163   site_ = GetSiteForURL(browser_context, url);
    164 
    165   // Now that we have a site, register it with the BrowsingInstance.  This
    166   // ensures that we won't create another SiteInstance for this site within
    167   // the same BrowsingInstance, because all same-site pages within a
    168   // BrowsingInstance can script each other.
    169   browsing_instance_->RegisterSiteInstance(this);
    170 
    171   if (process_) {
    172     LockToOrigin();
    173 
    174     // Ensure the process is registered for this site if necessary.
    175     if (RenderProcessHost::ShouldUseProcessPerSite(browser_context, site_)) {
    176       RenderProcessHostImpl::RegisterProcessHostForSite(
    177           browser_context, process_, site_);
    178     }
    179   }
    180 }
    181 
    182 const GURL& SiteInstanceImpl::GetSiteURL() const {
    183   return site_;
    184 }
    185 
    186 bool SiteInstanceImpl::HasSite() const {
    187   return has_site_;
    188 }
    189 
    190 bool SiteInstanceImpl::HasRelatedSiteInstance(const GURL& url) {
    191   return browsing_instance_->HasSiteInstance(url);
    192 }
    193 
    194 SiteInstance* SiteInstanceImpl::GetRelatedSiteInstance(const GURL& url) {
    195   return browsing_instance_->GetSiteInstanceForURL(url);
    196 }
    197 
    198 bool SiteInstanceImpl::IsRelatedSiteInstance(const SiteInstance* instance) {
    199   return browsing_instance_.get() == static_cast<const SiteInstanceImpl*>(
    200                                          instance)->browsing_instance_.get();
    201 }
    202 
    203 bool SiteInstanceImpl::HasWrongProcessForURL(const GURL& url) {
    204   // Having no process isn't a problem, since we'll assign it correctly.
    205   // Note that HasProcess() may return true if process_ is null, in
    206   // process-per-site cases where there's an existing process available.
    207   // We want to use such a process in the IsSuitableHost check, so we
    208   // may end up assigning process_ in the GetProcess() call below.
    209   if (!HasProcess())
    210     return false;
    211 
    212   // If the URL to navigate to can be associated with any site instance,
    213   // we want to keep it in the same process.
    214   if (IsURLSameAsAnySiteInstance(url))
    215     return false;
    216 
    217   // If the site URL is an extension (e.g., for hosted apps or WebUI) but the
    218   // process is not (or vice versa), make sure we notice and fix it.
    219   GURL site_url = GetSiteForURL(browsing_instance_->browser_context(), url);
    220   return !RenderProcessHostImpl::IsSuitableHost(
    221       GetProcess(), browsing_instance_->browser_context(), site_url);
    222 }
    223 
    224 void SiteInstanceImpl::set_render_process_host_factory(
    225     const RenderProcessHostFactory* rph_factory) {
    226   g_render_process_host_factory_ = rph_factory;
    227 }
    228 
    229 BrowserContext* SiteInstanceImpl::GetBrowserContext() const {
    230   return browsing_instance_->browser_context();
    231 }
    232 
    233 /*static*/
    234 SiteInstance* SiteInstance::Create(BrowserContext* browser_context) {
    235   return new SiteInstanceImpl(new BrowsingInstance(browser_context));
    236 }
    237 
    238 /*static*/
    239 SiteInstance* SiteInstance::CreateForURL(BrowserContext* browser_context,
    240                                          const GURL& url) {
    241   // This BrowsingInstance may be deleted if it returns an existing
    242   // SiteInstance.
    243   scoped_refptr<BrowsingInstance> instance(
    244       new BrowsingInstance(browser_context));
    245   return instance->GetSiteInstanceForURL(url);
    246 }
    247 
    248 /*static*/
    249 bool SiteInstance::IsSameWebSite(BrowserContext* browser_context,
    250                                  const GURL& real_url1,
    251                                  const GURL& real_url2) {
    252   GURL url1 = SiteInstanceImpl::GetEffectiveURL(browser_context, real_url1);
    253   GURL url2 = SiteInstanceImpl::GetEffectiveURL(browser_context, real_url2);
    254 
    255   // We infer web site boundaries based on the registered domain name of the
    256   // top-level page and the scheme.  We do not pay attention to the port if
    257   // one is present, because pages served from different ports can still
    258   // access each other if they change their document.domain variable.
    259 
    260   // Some special URLs will match the site instance of any other URL. This is
    261   // done before checking both of them for validity, since we want these URLs
    262   // to have the same site instance as even an invalid one.
    263   if (IsURLSameAsAnySiteInstance(url1) || IsURLSameAsAnySiteInstance(url2))
    264     return true;
    265 
    266   // If either URL is invalid, they aren't part of the same site.
    267   if (!url1.is_valid() || !url2.is_valid())
    268     return false;
    269 
    270   // If the schemes differ, they aren't part of the same site.
    271   if (url1.scheme() != url2.scheme())
    272     return false;
    273 
    274   return net::registry_controlled_domains::SameDomainOrHost(
    275       url1,
    276       url2,
    277       net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
    278 }
    279 
    280 /*static*/
    281 GURL SiteInstance::GetSiteForURL(BrowserContext* browser_context,
    282                                  const GURL& real_url) {
    283   // TODO(fsamuel, creis): For some reason appID is not recognized as a host.
    284   if (real_url.SchemeIs(chrome::kGuestScheme))
    285     return real_url;
    286 
    287   GURL url = SiteInstanceImpl::GetEffectiveURL(browser_context, real_url);
    288 
    289   // URLs with no host should have an empty site.
    290   GURL site;
    291 
    292   // TODO(creis): For many protocols, we should just treat the scheme as the
    293   // site, since there is no host.  e.g., file:, about:, chrome:
    294 
    295   // If the url has a host, then determine the site.
    296   if (url.has_host()) {
    297     // Only keep the scheme and registered domain as given by GetOrigin.  This
    298     // may also include a port, which we need to drop.
    299     site = url.GetOrigin();
    300 
    301     // Remove port, if any.
    302     if (site.has_port()) {
    303       GURL::Replacements rep;
    304       rep.ClearPort();
    305       site = site.ReplaceComponents(rep);
    306     }
    307 
    308     // If this URL has a registered domain, we only want to remember that part.
    309     std::string domain =
    310         net::registry_controlled_domains::GetDomainAndRegistry(
    311             url,
    312             net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
    313     if (!domain.empty()) {
    314       GURL::Replacements rep;
    315       rep.SetHostStr(domain);
    316       site = site.ReplaceComponents(rep);
    317     }
    318   }
    319   return site;
    320 }
    321 
    322 /*static*/
    323 GURL SiteInstanceImpl::GetEffectiveURL(BrowserContext* browser_context,
    324                                        const GURL& url) {
    325   return GetContentClient()->browser()->
    326       GetEffectiveURL(browser_context, url);
    327 }
    328 
    329 void SiteInstanceImpl::Observe(int type,
    330                                const NotificationSource& source,
    331                                const NotificationDetails& details) {
    332   DCHECK(type == NOTIFICATION_RENDERER_PROCESS_TERMINATED);
    333   RenderProcessHost* rph = Source<RenderProcessHost>(source).ptr();
    334   if (rph == process_)
    335     process_ = NULL;
    336 }
    337 
    338 void SiteInstanceImpl::LockToOrigin() {
    339   // We currently only restrict this process to a particular site if the
    340   // --enable-strict-site-isolation or --site-per-process flags are present.
    341   const CommandLine& command_line = *CommandLine::ForCurrentProcess();
    342   if (command_line.HasSwitch(switches::kEnableStrictSiteIsolation) ||
    343       command_line.HasSwitch(switches::kSitePerProcess)) {
    344     ChildProcessSecurityPolicyImpl* policy =
    345         ChildProcessSecurityPolicyImpl::GetInstance();
    346     policy->LockToOrigin(process_->GetID(), site_);
    347   }
    348 }
    349 
    350 }  // namespace content
    351