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         bool supports_browser_plugin = GetContentClient()->browser()->
    108             SupportsBrowserPlugin(browser_context, site_);
    109         process_ = new RenderProcessHostImpl(browser_context,
    110                                              partition,
    111                                              supports_browser_plugin,
    112                                              site_.SchemeIs(kGuestScheme));
    113       }
    114     }
    115     CHECK(process_);
    116     process_->AddObserver(this);
    117 
    118     // If we are using process-per-site, we need to register this process
    119     // for the current site so that we can find it again.  (If no site is set
    120     // at this time, we will register it in SetSite().)
    121     if (use_process_per_site) {
    122       RenderProcessHostImpl::RegisterProcessHostForSite(browser_context,
    123                                                         process_, site_);
    124     }
    125 
    126     GetContentClient()->browser()->SiteInstanceGotProcess(this);
    127 
    128     if (has_site_)
    129       LockToOrigin();
    130   }
    131   DCHECK(process_);
    132 
    133   return process_;
    134 }
    135 
    136 void SiteInstanceImpl::SetSite(const GURL& url) {
    137   // A SiteInstance's site should not change.
    138   // TODO(creis): When following links or script navigations, we can currently
    139   // render pages from other sites in this SiteInstance.  This will eventually
    140   // be fixed, but until then, we should still not set the site of a
    141   // SiteInstance more than once.
    142   DCHECK(!has_site_);
    143 
    144   // Remember that this SiteInstance has been used to load a URL, even if the
    145   // URL is invalid.
    146   has_site_ = true;
    147   BrowserContext* browser_context = browsing_instance_->browser_context();
    148   site_ = GetSiteForURL(browser_context, url);
    149 
    150   // Now that we have a site, register it with the BrowsingInstance.  This
    151   // ensures that we won't create another SiteInstance for this site within
    152   // the same BrowsingInstance, because all same-site pages within a
    153   // BrowsingInstance can script each other.
    154   browsing_instance_->RegisterSiteInstance(this);
    155 
    156   if (process_) {
    157     LockToOrigin();
    158 
    159     // Ensure the process is registered for this site if necessary.
    160     if (RenderProcessHost::ShouldUseProcessPerSite(browser_context, site_)) {
    161       RenderProcessHostImpl::RegisterProcessHostForSite(
    162           browser_context, process_, site_);
    163     }
    164   }
    165 }
    166 
    167 const GURL& SiteInstanceImpl::GetSiteURL() const {
    168   return site_;
    169 }
    170 
    171 bool SiteInstanceImpl::HasSite() const {
    172   return has_site_;
    173 }
    174 
    175 bool SiteInstanceImpl::HasRelatedSiteInstance(const GURL& url) {
    176   return browsing_instance_->HasSiteInstance(url);
    177 }
    178 
    179 SiteInstance* SiteInstanceImpl::GetRelatedSiteInstance(const GURL& url) {
    180   return browsing_instance_->GetSiteInstanceForURL(url);
    181 }
    182 
    183 bool SiteInstanceImpl::IsRelatedSiteInstance(const SiteInstance* instance) {
    184   return browsing_instance_.get() == static_cast<const SiteInstanceImpl*>(
    185                                          instance)->browsing_instance_.get();
    186 }
    187 
    188 bool SiteInstanceImpl::HasWrongProcessForURL(const GURL& url) {
    189   // Having no process isn't a problem, since we'll assign it correctly.
    190   // Note that HasProcess() may return true if process_ is null, in
    191   // process-per-site cases where there's an existing process available.
    192   // We want to use such a process in the IsSuitableHost check, so we
    193   // may end up assigning process_ in the GetProcess() call below.
    194   if (!HasProcess())
    195     return false;
    196 
    197   // If the URL to navigate to can be associated with any site instance,
    198   // we want to keep it in the same process.
    199   if (IsRendererDebugURL(url))
    200     return false;
    201 
    202   // If the site URL is an extension (e.g., for hosted apps or WebUI) but the
    203   // process is not (or vice versa), make sure we notice and fix it.
    204   GURL site_url = GetSiteForURL(browsing_instance_->browser_context(), url);
    205   return !RenderProcessHostImpl::IsSuitableHost(
    206       GetProcess(), browsing_instance_->browser_context(), site_url);
    207 }
    208 
    209 void SiteInstanceImpl::set_render_process_host_factory(
    210     const RenderProcessHostFactory* rph_factory) {
    211   g_render_process_host_factory_ = rph_factory;
    212 }
    213 
    214 BrowserContext* SiteInstanceImpl::GetBrowserContext() const {
    215   return browsing_instance_->browser_context();
    216 }
    217 
    218 /*static*/
    219 SiteInstance* SiteInstance::Create(BrowserContext* browser_context) {
    220   return new SiteInstanceImpl(new BrowsingInstance(browser_context));
    221 }
    222 
    223 /*static*/
    224 SiteInstance* SiteInstance::CreateForURL(BrowserContext* browser_context,
    225                                          const GURL& url) {
    226   // This BrowsingInstance may be deleted if it returns an existing
    227   // SiteInstance.
    228   scoped_refptr<BrowsingInstance> instance(
    229       new BrowsingInstance(browser_context));
    230   return instance->GetSiteInstanceForURL(url);
    231 }
    232 
    233 /*static*/
    234 bool SiteInstance::IsSameWebSite(BrowserContext* browser_context,
    235                                  const GURL& real_url1,
    236                                  const GURL& real_url2) {
    237   GURL url1 = SiteInstanceImpl::GetEffectiveURL(browser_context, real_url1);
    238   GURL url2 = SiteInstanceImpl::GetEffectiveURL(browser_context, real_url2);
    239 
    240   // We infer web site boundaries based on the registered domain name of the
    241   // top-level page and the scheme.  We do not pay attention to the port if
    242   // one is present, because pages served from different ports can still
    243   // access each other if they change their document.domain variable.
    244 
    245   // Some special URLs will match the site instance of any other URL. This is
    246   // done before checking both of them for validity, since we want these URLs
    247   // to have the same site instance as even an invalid one.
    248   if (IsRendererDebugURL(url1) || IsRendererDebugURL(url2))
    249     return true;
    250 
    251   // If either URL is invalid, they aren't part of the same site.
    252   if (!url1.is_valid() || !url2.is_valid())
    253     return false;
    254 
    255   // If the schemes differ, they aren't part of the same site.
    256   if (url1.scheme() != url2.scheme())
    257     return false;
    258 
    259   return net::registry_controlled_domains::SameDomainOrHost(
    260       url1,
    261       url2,
    262       net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
    263 }
    264 
    265 /*static*/
    266 GURL SiteInstance::GetSiteForURL(BrowserContext* browser_context,
    267                                  const GURL& real_url) {
    268   // TODO(fsamuel, creis): For some reason appID is not recognized as a host.
    269   if (real_url.SchemeIs(kGuestScheme))
    270     return real_url;
    271 
    272   GURL url = SiteInstanceImpl::GetEffectiveURL(browser_context, real_url);
    273 
    274   // URLs with no host should have an empty site.
    275   GURL site;
    276 
    277   // TODO(creis): For many protocols, we should just treat the scheme as the
    278   // site, since there is no host.  e.g., file:, about:, chrome:
    279 
    280   // If the url has a host, then determine the site.
    281   if (url.has_host()) {
    282     // Only keep the scheme and registered domain as given by GetOrigin.  This
    283     // may also include a port, which we need to drop.
    284     site = url.GetOrigin();
    285 
    286     // Remove port, if any.
    287     if (site.has_port()) {
    288       GURL::Replacements rep;
    289       rep.ClearPort();
    290       site = site.ReplaceComponents(rep);
    291     }
    292 
    293     // If this URL has a registered domain, we only want to remember that part.
    294     std::string domain =
    295         net::registry_controlled_domains::GetDomainAndRegistry(
    296             url,
    297             net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
    298     if (!domain.empty()) {
    299       GURL::Replacements rep;
    300       rep.SetHostStr(domain);
    301       site = site.ReplaceComponents(rep);
    302     }
    303   }
    304   return site;
    305 }
    306 
    307 /*static*/
    308 GURL SiteInstanceImpl::GetEffectiveURL(BrowserContext* browser_context,
    309                                        const GURL& url) {
    310   return GetContentClient()->browser()->
    311       GetEffectiveURL(browser_context, url);
    312 }
    313 
    314 void SiteInstanceImpl::RenderProcessHostDestroyed(RenderProcessHost* host) {
    315   DCHECK_EQ(process_, host);
    316   process_->RemoveObserver(this);
    317   process_ = NULL;
    318 }
    319 
    320 void SiteInstanceImpl::LockToOrigin() {
    321   // We currently only restrict this process to a particular site if the
    322   // --enable-strict-site-isolation or --site-per-process flags are present.
    323   const CommandLine& command_line = *CommandLine::ForCurrentProcess();
    324   if (command_line.HasSwitch(switches::kEnableStrictSiteIsolation) ||
    325       command_line.HasSwitch(switches::kSitePerProcess)) {
    326     ChildProcessSecurityPolicyImpl* policy =
    327         ChildProcessSecurityPolicyImpl::GetInstance();
    328     policy->LockToOrigin(process_->GetID(), site_);
    329   }
    330 }
    331 
    332 }  // namespace content
    333