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