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