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