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.
5 #include "content/browser/browser_plugin/browser_plugin_guest_manager.h"
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"
24 BrowserPluginHostFactory
* BrowserPluginGuestManager::factory_
= NULL
;
26 BrowserPluginGuestManager::BrowserPluginGuestManager()
27 : next_instance_id_(browser_plugin::kInstanceIDNone
) {
30 BrowserPluginGuestManager::~BrowserPluginGuestManager() {
34 BrowserPluginGuestManager
* BrowserPluginGuestManager::Create() {
36 return factory_
->CreateBrowserPluginGuestManager();
37 return new BrowserPluginGuestManager();
40 BrowserPluginGuest
* BrowserPluginGuestManager::CreateGuest(
41 SiteInstance
* embedder_site_instance
,
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
51 if (!IsStringUTF8(params
.storage_partition_id
)) {
52 content::RecordAction(UserMetricsAction("BadMessageTerminate_BPGM"));
54 embedder_process_host
->GetHandle(),
55 content::RESULT_CODE_KILLED_BAD_MESSAGE
, false);
59 // We usually require BrowserPlugins to be hosted by a storage isolated
60 // extension. We treat WebUI pages as a special case if they host the
61 // BrowserPlugin in a component extension iframe. In that case, we use the
62 // iframe's URL to determine the extension.
63 const GURL
& embedder_site_url
= embedder_site_instance
->GetSiteURL();
64 GURL
validated_frame_url(params
.embedder_frame_url
);
65 embedder_process_host
->FilterURL(false, &validated_frame_url
);
66 const std::string
& host
= content::HasWebUIScheme(embedder_site_url
) ?
67 validated_frame_url
.host() : embedder_site_url
.host();
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",
78 params
.persist_storage
? "persist" : "",
79 url_encoded_partition
.c_str()));
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
);
93 return WebContentsImpl::CreateGuest(
94 embedder_site_instance
->GetBrowserContext(),
100 BrowserPluginGuest
* BrowserPluginGuestManager::GetGuestByInstanceID(
102 int embedder_render_process_id
) const {
103 if (!CanEmbedderAccessInstanceIDMaybeKill(embedder_render_process_id
,
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())
111 return static_cast<WebContentsImpl
*>(it
->second
)->GetBrowserPluginGuest();
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
;
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
);
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"));
134 RenderProcessHost::FromID(embedder_render_process_id
)->GetHandle(),
135 content::RESULT_CODE_KILLED_BAD_MESSAGE
, false);
141 void BrowserPluginGuestManager::OnMessageReceived(const IPC::Message
& message
,
142 int render_process_id
) {
143 if (BrowserPluginGuest::ShouldForwardToBrowserPluginGuest(message
)) {
145 // All allowed messages must have instance_id as their first parameter.
146 PickleIterator
iter(message
);
147 bool success
= iter
.ReadInt(&instance_id
);
149 BrowserPluginGuest
* guest
=
150 GetGuestByInstanceID(instance_id
, render_process_id
);
151 if (guest
&& guest
->OnMessageReceivedFromEmbedder(message
))
154 IPC_BEGIN_MESSAGE_MAP(BrowserPluginGuestManager
, message
)
155 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_BuffersSwappedACK
,
156 OnUnhandledSwapBuffersACK
)
157 IPC_END_MESSAGE_MAP()
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())
170 return embedder_render_process_id
==
171 guest
->opener()->embedder_web_contents()->GetRenderProcessHost()->
175 return embedder_render_process_id
==
176 guest
->embedder_web_contents()->GetRenderProcessHost()->GetID();
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
184 if (instance_id
<= browser_plugin::kInstanceIDNone
)
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_
)
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())
198 BrowserPluginGuest
* guest
=
199 static_cast<WebContentsImpl
*>(it
->second
)->GetBrowserPluginGuest();
201 return CanEmbedderAccessGuest(embedder_render_process_id
, guest
);
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();
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(
219 const FrameHostMsg_BuffersSwappedACK_Params
& params
) {
220 BrowserPluginGuest::AcknowledgeBufferPresent(params
.gpu_route_id
,
226 bool BrowserPluginGuestManager::ForEachGuest(
227 WebContentsImpl
* embedder_web_contents
, const GuestCallback
& callback
) {
228 for (GuestInstanceMap::iterator it
=
229 guest_web_contents_by_instance_id_
.begin();
230 it
!= guest_web_contents_by_instance_id_
.end(); ++it
) {
231 BrowserPluginGuest
* guest
= it
->second
->GetBrowserPluginGuest();
232 if (embedder_web_contents
!= guest
->embedder_web_contents())
235 if (callback
.Run(guest
))
241 } // namespace content