Home | History | Annotate | Download | only in guest_view
      1 // Copyright 2014 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/guest_view/guest_view_manager.h"
      6 
      7 #include "base/strings/stringprintf.h"
      8 #include "chrome/browser/extensions/extension_service.h"
      9 #include "chrome/browser/guest_view/guest_view_base.h"
     10 #include "chrome/browser/guest_view/guest_view_constants.h"
     11 #include "chrome/browser/guest_view/guest_view_manager_factory.h"
     12 #include "chrome/browser/guest_view/web_view/web_view_guest.h"
     13 #include "chrome/browser/profiles/profile.h"
     14 #include "content/public/browser/browser_context.h"
     15 #include "content/public/browser/render_process_host.h"
     16 #include "content/public/browser/user_metrics.h"
     17 #include "content/public/browser/web_contents_observer.h"
     18 #include "content/public/common/result_codes.h"
     19 #include "content/public/common/url_constants.h"
     20 #include "extensions/browser/extension_system.h"
     21 #include "net/base/escape.h"
     22 #include "url/gurl.h"
     23 
     24 using content::BrowserContext;
     25 using content::SiteInstance;
     26 using content::WebContents;
     27 
     28 // static
     29 GuestViewManagerFactory* GuestViewManager::factory_ = NULL;
     30 
     31 GuestViewManager::GuestViewManager(content::BrowserContext* context)
     32     : current_instance_id_(0), last_instance_id_removed_(0), context_(context) {
     33 }
     34 
     35 GuestViewManager::~GuestViewManager() {}
     36 
     37 // static.
     38 GuestViewManager* GuestViewManager::FromBrowserContext(
     39     BrowserContext* context) {
     40   GuestViewManager* guest_manager =
     41       static_cast<GuestViewManager*>(context->GetUserData(
     42           guestview::kGuestViewManagerKeyName));
     43   if (!guest_manager) {
     44     if (factory_) {
     45       guest_manager = factory_->CreateGuestViewManager(context);
     46     } else {
     47       guest_manager = new GuestViewManager(context);
     48     }
     49     context->SetUserData(guestview::kGuestViewManagerKeyName, guest_manager);
     50   }
     51   return guest_manager;
     52 }
     53 
     54 content::WebContents* GuestViewManager::GetGuestByInstanceIDSafely(
     55     int guest_instance_id,
     56     int embedder_render_process_id) {
     57   if (!CanEmbedderAccessInstanceIDMaybeKill(embedder_render_process_id,
     58                                             guest_instance_id)) {
     59     return NULL;
     60   }
     61   return GetGuestByInstanceID(guest_instance_id, embedder_render_process_id);
     62 }
     63 
     64 int GuestViewManager::GetNextInstanceID() {
     65   return ++current_instance_id_;
     66 }
     67 
     68 content::WebContents* GuestViewManager::CreateGuest(
     69     content::SiteInstance* embedder_site_instance,
     70     int instance_id,
     71     scoped_ptr<base::DictionaryValue> extra_params) {
     72   std::string storage_partition_id;
     73   bool persist_storage = false;
     74   std::string storage_partition_string;
     75   WebViewGuest::ParsePartitionParam(
     76       extra_params.get(), &storage_partition_id, &persist_storage);
     77 
     78   content::RenderProcessHost* embedder_process_host =
     79       embedder_site_instance->GetProcess();
     80   // Validate that the partition id coming from the renderer is valid UTF-8,
     81   // since we depend on this in other parts of the code, such as FilePath
     82   // creation. If the validation fails, treat it as a bad message and kill the
     83   // renderer process.
     84   if (!base::IsStringUTF8(storage_partition_id)) {
     85     content::RecordAction(
     86         base::UserMetricsAction("BadMessageTerminate_BPGM"));
     87     base::KillProcess(
     88         embedder_process_host->GetHandle(),
     89         content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
     90     return NULL;
     91   }
     92 
     93   const GURL& embedder_site_url = embedder_site_instance->GetSiteURL();
     94   const std::string& host = embedder_site_url.host();
     95 
     96   std::string url_encoded_partition = net::EscapeQueryParamValue(
     97       storage_partition_id, false);
     98   // The SiteInstance of a given webview tag is based on the fact that it's
     99   // a guest process in addition to which platform application the tag
    100   // belongs to and what storage partition is in use, rather than the URL
    101   // that the tag is being navigated to.
    102   GURL guest_site(base::StringPrintf("%s://%s/%s?%s",
    103                                      content::kGuestScheme,
    104                                      host.c_str(),
    105                                      persist_storage ? "persist" : "",
    106                                      url_encoded_partition.c_str()));
    107 
    108   // If we already have a webview tag in the same app using the same storage
    109   // partition, we should use the same SiteInstance so the existing tag and
    110   // the new tag can script each other.
    111   SiteInstance* guest_site_instance = GetGuestSiteInstance(guest_site);
    112   if (!guest_site_instance) {
    113     // Create the SiteInstance in a new BrowsingInstance, which will ensure
    114     // that webview tags are also not allowed to send messages across
    115     // different partitions.
    116     guest_site_instance = SiteInstance::CreateForURL(
    117         embedder_site_instance->GetBrowserContext(), guest_site);
    118   }
    119   WebContents::CreateParams create_params(
    120       embedder_site_instance->GetBrowserContext(),
    121       guest_site_instance);
    122   create_params.guest_instance_id = instance_id;
    123   create_params.guest_extra_params.reset(extra_params.release());
    124   return WebContents::Create(create_params);
    125 }
    126 
    127 void GuestViewManager::MaybeGetGuestByInstanceIDOrKill(
    128     int guest_instance_id,
    129     int embedder_render_process_id,
    130     const GuestByInstanceIDCallback& callback) {
    131   if (!CanEmbedderAccessInstanceIDMaybeKill(embedder_render_process_id,
    132                                             guest_instance_id)) {
    133     // If we kill the embedder, then don't bother calling back.
    134     return;
    135   }
    136   content::WebContents* guest_web_contents =
    137       GetGuestByInstanceID(guest_instance_id, embedder_render_process_id);
    138   callback.Run(guest_web_contents);
    139 }
    140 
    141 SiteInstance* GuestViewManager::GetGuestSiteInstance(
    142     const GURL& guest_site) {
    143   for (GuestInstanceMap::const_iterator it =
    144        guest_web_contents_by_instance_id_.begin();
    145        it != guest_web_contents_by_instance_id_.end(); ++it) {
    146     if (it->second->GetSiteInstance()->GetSiteURL() == guest_site)
    147       return it->second->GetSiteInstance();
    148   }
    149   return NULL;
    150 }
    151 
    152 bool GuestViewManager::ForEachGuest(WebContents* embedder_web_contents,
    153                                     const GuestCallback& callback) {
    154   for (GuestInstanceMap::iterator it =
    155            guest_web_contents_by_instance_id_.begin();
    156        it != guest_web_contents_by_instance_id_.end(); ++it) {
    157     WebContents* guest = it->second;
    158     GuestViewBase* guest_view = GuestViewBase::FromWebContents(guest);
    159     if (embedder_web_contents != guest_view->embedder_web_contents())
    160       continue;
    161 
    162     if (callback.Run(guest))
    163       return true;
    164   }
    165   return false;
    166 }
    167 
    168 void GuestViewManager::AddGuest(int guest_instance_id,
    169                                 WebContents* guest_web_contents) {
    170   CHECK(!ContainsKey(guest_web_contents_by_instance_id_, guest_instance_id));
    171   CHECK(CanUseGuestInstanceID(guest_instance_id));
    172   guest_web_contents_by_instance_id_[guest_instance_id] = guest_web_contents;
    173 }
    174 
    175 void GuestViewManager::RemoveGuest(int guest_instance_id) {
    176   GuestInstanceMap::iterator it =
    177       guest_web_contents_by_instance_id_.find(guest_instance_id);
    178   DCHECK(it != guest_web_contents_by_instance_id_.end());
    179   guest_web_contents_by_instance_id_.erase(it);
    180 
    181   // All the instance IDs that lie within [0, last_instance_id_removed_]
    182   // are invalid.
    183   // The remaining sparse invalid IDs are kept in |removed_instance_ids_| set.
    184   // The following code compacts the set by incrementing
    185   // |last_instance_id_removed_|.
    186   if (guest_instance_id == last_instance_id_removed_ + 1) {
    187     ++last_instance_id_removed_;
    188     // Compact.
    189     std::set<int>::iterator iter = removed_instance_ids_.begin();
    190     while (iter != removed_instance_ids_.end()) {
    191       int instance_id = *iter;
    192       // The sparse invalid IDs must not lie within
    193       // [0, last_instance_id_removed_]
    194       DCHECK(instance_id > last_instance_id_removed_);
    195       if (instance_id != last_instance_id_removed_ + 1)
    196         break;
    197       ++last_instance_id_removed_;
    198       removed_instance_ids_.erase(iter++);
    199     }
    200   } else {
    201     removed_instance_ids_.insert(guest_instance_id);
    202   }
    203 }
    204 
    205 content::WebContents* GuestViewManager::GetGuestByInstanceID(
    206     int guest_instance_id,
    207     int embedder_render_process_id) {
    208   GuestInstanceMap::const_iterator it =
    209       guest_web_contents_by_instance_id_.find(guest_instance_id);
    210   if (it == guest_web_contents_by_instance_id_.end())
    211     return NULL;
    212   return it->second;
    213 }
    214 
    215 bool GuestViewManager::CanEmbedderAccessInstanceIDMaybeKill(
    216     int embedder_render_process_id,
    217     int guest_instance_id) {
    218   if (!CanEmbedderAccessInstanceID(embedder_render_process_id,
    219                                    guest_instance_id)) {
    220     // The embedder process is trying to access a guest it does not own.
    221     content::RecordAction(
    222         base::UserMetricsAction("BadMessageTerminate_BPGM"));
    223     base::KillProcess(
    224         content::RenderProcessHost::FromID(embedder_render_process_id)->
    225             GetHandle(),
    226         content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
    227     return false;
    228   }
    229   return true;
    230 }
    231 
    232 bool GuestViewManager::CanUseGuestInstanceID(int guest_instance_id) {
    233   if (guest_instance_id <= last_instance_id_removed_)
    234     return false;
    235   return !ContainsKey(removed_instance_ids_, guest_instance_id);
    236 }
    237 
    238 bool GuestViewManager::CanEmbedderAccessInstanceID(
    239     int embedder_render_process_id,
    240     int guest_instance_id) {
    241   // The embedder is trying to access a guest with a negative or zero
    242   // instance ID.
    243   if (guest_instance_id <= guestview::kInstanceIDNone)
    244     return false;
    245 
    246   // The embedder is trying to access an instance ID that has not yet been
    247   // allocated by GuestViewManager. This could cause instance ID
    248   // collisions in the future, and potentially give one embedder access to a
    249   // guest it does not own.
    250   if (guest_instance_id > current_instance_id_)
    251     return false;
    252 
    253   GuestInstanceMap::const_iterator it =
    254       guest_web_contents_by_instance_id_.find(guest_instance_id);
    255   if (it == guest_web_contents_by_instance_id_.end())
    256     return true;
    257 
    258   GuestViewBase* guest_view = GuestViewBase::FromWebContents(it->second);
    259   if (!guest_view)
    260     return false;
    261 
    262   return CanEmbedderAccessGuest(embedder_render_process_id, guest_view);
    263 }
    264 
    265 bool GuestViewManager::CanEmbedderAccessGuest(int embedder_render_process_id,
    266                                               GuestViewBase* guest) {
    267   // The embedder can access the guest if it has not been attached and its
    268   // opener's embedder lives in the same process as the given embedder.
    269   if (!guest->attached()) {
    270     if (!guest->GetOpener())
    271       return false;
    272 
    273     return embedder_render_process_id ==
    274         guest->GetOpener()->embedder_web_contents()->GetRenderProcessHost()->
    275             GetID();
    276   }
    277 
    278   return embedder_render_process_id ==
    279       guest->embedder_web_contents()->GetRenderProcessHost()->GetID();
    280 }
    281