Home | History | Annotate | Download | only in browser_plugin
      1 // Copyright (c) 2013 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/browser_plugin/browser_plugin_guest_manager.h"
      6 
      7 #include "content/browser/browser_plugin/browser_plugin_guest.h"
      8 #include "content/browser/browser_plugin/browser_plugin_host_factory.h"
      9 #include "content/browser/renderer_host/render_view_host_impl.h"
     10 #include "content/browser/web_contents/web_contents_impl.h"
     11 #include "content/common/browser_plugin/browser_plugin_constants.h"
     12 #include "content/common/browser_plugin/browser_plugin_messages.h"
     13 #include "content/common/content_export.h"
     14 #include "content/public/browser/user_metrics.h"
     15 #include "content/public/common/result_codes.h"
     16 #include "content/public/common/url_constants.h"
     17 #include "content/public/common/url_utils.h"
     18 #include "net/base/escape.h"
     19 
     20 namespace content {
     21 
     22 // static
     23 BrowserPluginHostFactory* BrowserPluginGuestManager::factory_ = NULL;
     24 
     25 BrowserPluginGuestManager::BrowserPluginGuestManager()
     26     : next_instance_id_(browser_plugin::kInstanceIDNone) {
     27 }
     28 
     29 BrowserPluginGuestManager::~BrowserPluginGuestManager() {
     30 }
     31 
     32 // static
     33 BrowserPluginGuestManager* BrowserPluginGuestManager::Create() {
     34   if (factory_)
     35     return factory_->CreateBrowserPluginGuestManager();
     36   return new BrowserPluginGuestManager();
     37 }
     38 
     39 BrowserPluginGuest* BrowserPluginGuestManager::CreateGuest(
     40     SiteInstance* embedder_site_instance,
     41     int instance_id,
     42     const BrowserPluginHostMsg_Attach_Params& params,
     43     scoped_ptr<base::DictionaryValue> extra_params) {
     44   RenderProcessHost* embedder_process_host =
     45       embedder_site_instance->GetProcess();
     46   // Validate that the partition id coming from the renderer is valid UTF-8,
     47   // since we depend on this in other parts of the code, such as FilePath
     48   // creation. If the validation fails, treat it as a bad message and kill the
     49   // renderer process.
     50   if (!IsStringUTF8(params.storage_partition_id)) {
     51     content::RecordAction(UserMetricsAction("BadMessageTerminate_BPGM"));
     52     base::KillProcess(
     53         embedder_process_host->GetHandle(),
     54         content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
     55     return NULL;
     56   }
     57 
     58   // We usually require BrowserPlugins to be hosted by a storage isolated
     59   // extension. We treat WebUI pages as a special case if they host the
     60   // BrowserPlugin in a component extension iframe. In that case, we use the
     61   // iframe's URL to determine the extension.
     62   const GURL& embedder_site_url = embedder_site_instance->GetSiteURL();
     63   GURL validated_frame_url(params.embedder_frame_url);
     64   RenderViewHost::FilterURL(
     65       embedder_process_host, false, &validated_frame_url);
     66   const std::string& host = content::HasWebUIScheme(embedder_site_url) ?
     67        validated_frame_url.host() : embedder_site_url.host();
     68 
     69   std::string url_encoded_partition = net::EscapeQueryParamValue(
     70       params.storage_partition_id, false);
     71   // The SiteInstance of a given webview tag is based on the fact that it's
     72   // a guest process in addition to which platform application the tag
     73   // belongs to and what storage partition is in use, rather than the URL
     74   // that the tag is being navigated to.
     75   GURL guest_site(base::StringPrintf("%s://%s/%s?%s",
     76                                      kGuestScheme,
     77                                      host.c_str(),
     78                                      params.persist_storage ? "persist" : "",
     79                                      url_encoded_partition.c_str()));
     80 
     81   // If we already have a webview tag in the same app using the same storage
     82   // partition, we should use the same SiteInstance so the existing tag and
     83   // the new tag can script each other.
     84   SiteInstance* guest_site_instance = GetGuestSiteInstance(guest_site);
     85   if (!guest_site_instance) {
     86     // Create the SiteInstance in a new BrowsingInstance, which will ensure
     87     // that webview tags are also not allowed to send messages across
     88     // different partitions.
     89     guest_site_instance = SiteInstance::CreateForURL(
     90         embedder_site_instance->GetBrowserContext(), guest_site);
     91   }
     92 
     93   return WebContentsImpl::CreateGuest(
     94       embedder_site_instance->GetBrowserContext(),
     95       guest_site_instance,
     96       instance_id,
     97       extra_params.Pass());
     98 }
     99 
    100 BrowserPluginGuest* BrowserPluginGuestManager::GetGuestByInstanceID(
    101     int instance_id,
    102     int embedder_render_process_id) const {
    103   if (!CanEmbedderAccessInstanceIDMaybeKill(embedder_render_process_id,
    104                                             instance_id)) {
    105     return NULL;
    106   }
    107   GuestInstanceMap::const_iterator it =
    108       guest_web_contents_by_instance_id_.find(instance_id);
    109   if (it == guest_web_contents_by_instance_id_.end())
    110     return NULL;
    111   return static_cast<WebContentsImpl*>(it->second)->GetBrowserPluginGuest();
    112 }
    113 
    114 void BrowserPluginGuestManager::AddGuest(int instance_id,
    115                                          WebContentsImpl* guest_web_contents) {
    116   DCHECK(guest_web_contents_by_instance_id_.find(instance_id) ==
    117          guest_web_contents_by_instance_id_.end());
    118   guest_web_contents_by_instance_id_[instance_id] = guest_web_contents;
    119 }
    120 
    121 void BrowserPluginGuestManager::RemoveGuest(int instance_id) {
    122   DCHECK(guest_web_contents_by_instance_id_.find(instance_id) !=
    123          guest_web_contents_by_instance_id_.end());
    124   guest_web_contents_by_instance_id_.erase(instance_id);
    125 }
    126 
    127 bool BrowserPluginGuestManager::CanEmbedderAccessInstanceIDMaybeKill(
    128     int embedder_render_process_id,
    129     int instance_id) const {
    130   if (!CanEmbedderAccessInstanceID(embedder_render_process_id, instance_id)) {
    131     // The embedder process is trying to access a guest it does not own.
    132     content::RecordAction(UserMetricsAction("BadMessageTerminate_BPGM"));
    133     base::KillProcess(
    134         RenderProcessHost::FromID(embedder_render_process_id)->GetHandle(),
    135         content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
    136     return false;
    137   }
    138   return true;
    139 }
    140 
    141 void BrowserPluginGuestManager::OnMessageReceived(const IPC::Message& message,
    142                                                   int render_process_id) {
    143   if (BrowserPluginGuest::ShouldForwardToBrowserPluginGuest(message)) {
    144     int instance_id = 0;
    145     // All allowed messages must have instance_id as their first parameter.
    146     PickleIterator iter(message);
    147     bool success = iter.ReadInt(&instance_id);
    148     DCHECK(success);
    149     BrowserPluginGuest* guest =
    150         GetGuestByInstanceID(instance_id, render_process_id);
    151     if (guest && guest->OnMessageReceivedFromEmbedder(message))
    152       return;
    153   }
    154   IPC_BEGIN_MESSAGE_MAP(BrowserPluginGuestManager, message)
    155     IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_BuffersSwappedACK,
    156                         OnUnhandledSwapBuffersACK)
    157   IPC_END_MESSAGE_MAP()
    158 }
    159 
    160 // static
    161 bool BrowserPluginGuestManager::CanEmbedderAccessGuest(
    162     int embedder_render_process_id,
    163     BrowserPluginGuest* guest) {
    164   // The embedder can access the guest if it has not been attached and its
    165   // opener's embedder lives in the same process as the given embedder.
    166   if (!guest->attached()) {
    167     if (!guest->opener())
    168       return false;
    169 
    170     return embedder_render_process_id ==
    171         guest->opener()->embedder_web_contents()->GetRenderProcessHost()->
    172             GetID();
    173   }
    174 
    175   return embedder_render_process_id ==
    176       guest->embedder_web_contents()->GetRenderProcessHost()->GetID();
    177 }
    178 
    179 bool BrowserPluginGuestManager::CanEmbedderAccessInstanceID(
    180     int embedder_render_process_id,
    181     int instance_id) const {
    182   // The embedder is trying to access a guest with a negative or zero
    183   // instance ID.
    184   if (instance_id <= browser_plugin::kInstanceIDNone)
    185     return false;
    186 
    187   // The embedder is trying to access an instance ID that has not yet been
    188   // allocated by BrowserPluginGuestManager. This could cause instance ID
    189   // collisions in the future, and potentially give one embedder access to a
    190   // guest it does not own.
    191   if (instance_id > next_instance_id_)
    192     return false;
    193 
    194   GuestInstanceMap::const_iterator it =
    195       guest_web_contents_by_instance_id_.find(instance_id);
    196   if (it == guest_web_contents_by_instance_id_.end())
    197     return true;
    198   BrowserPluginGuest* guest =
    199       static_cast<WebContentsImpl*>(it->second)->GetBrowserPluginGuest();
    200 
    201   return CanEmbedderAccessGuest(embedder_render_process_id, guest);
    202 }
    203 
    204 SiteInstance* BrowserPluginGuestManager::GetGuestSiteInstance(
    205     const GURL& guest_site) {
    206   for (GuestInstanceMap::const_iterator it =
    207        guest_web_contents_by_instance_id_.begin();
    208        it != guest_web_contents_by_instance_id_.end(); ++it) {
    209     if (it->second->GetSiteInstance()->GetSiteURL() == guest_site)
    210       return it->second->GetSiteInstance();
    211   }
    212   return NULL;
    213 }
    214 
    215 // We only get here during teardown if we have one last buffer pending,
    216 // otherwise the ACK is handled by the guest.
    217 void BrowserPluginGuestManager::OnUnhandledSwapBuffersACK(
    218     int instance_id,
    219     int route_id,
    220     int gpu_host_id,
    221     const std::string& mailbox_name,
    222     uint32 sync_point) {
    223   BrowserPluginGuest::AcknowledgeBufferPresent(route_id,
    224                                                gpu_host_id,
    225                                                mailbox_name,
    226                                                sync_point);
    227 }
    228 
    229 bool BrowserPluginGuestManager::ForEachGuest(
    230     WebContentsImpl* embedder_web_contents, const GuestCallback& callback) {
    231   for (GuestInstanceMap::iterator it =
    232            guest_web_contents_by_instance_id_.begin();
    233        it != guest_web_contents_by_instance_id_.end(); ++it) {
    234     BrowserPluginGuest* guest = it->second->GetBrowserPluginGuest();
    235     if (embedder_web_contents != guest->embedder_web_contents())
    236       continue;
    237 
    238     if (callback.Run(guest))
    239       return true;
    240   }
    241   return false;
    242 }
    243 
    244 }  // namespace content
    245