IndexedDBFactory now ForceCloses databases.
[chromium-blink-merge.git] / content / browser / browser_plugin / browser_plugin_guest_manager.cc
blob2b4935c9c07aec2106514560a45c2f15e27d9f54
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"
21 namespace content {
23 // static
24 BrowserPluginHostFactory* BrowserPluginGuestManager::factory_ = NULL;
26 BrowserPluginGuestManager::BrowserPluginGuestManager()
27 : next_instance_id_(browser_plugin::kInstanceIDNone) {
30 BrowserPluginGuestManager::~BrowserPluginGuestManager() {
33 // static
34 BrowserPluginGuestManager* BrowserPluginGuestManager::Create() {
35 if (factory_)
36 return factory_->CreateBrowserPluginGuestManager();
37 return new BrowserPluginGuestManager();
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(UserMetricsAction("BadMessageTerminate_BPGM"));
53 base::KillProcess(
54 embedder_process_host->GetHandle(),
55 content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
56 return NULL;
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",
76 kGuestScheme,
77 host.c_str(),
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(),
95 guest_site_instance,
96 instance_id,
97 extra_params.Pass());
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;
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();
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"));
133 base::KillProcess(
134 RenderProcessHost::FromID(embedder_render_process_id)->GetHandle(),
135 content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
136 return false;
138 return true;
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;
154 IPC_BEGIN_MESSAGE_MAP(BrowserPluginGuestManager, message)
155 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_BuffersSwappedACK,
156 OnUnhandledSwapBuffersACK)
157 IPC_END_MESSAGE_MAP()
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;
170 return embedder_render_process_id ==
171 guest->opener()->embedder_web_contents()->GetRenderProcessHost()->
172 GetID();
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
183 // instance ID.
184 if (instance_id <= browser_plugin::kInstanceIDNone)
185 return false;
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;
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();
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();
212 return NULL;
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 const FrameHostMsg_BuffersSwappedACK_Params& params) {
220 BrowserPluginGuest::AcknowledgeBufferPresent(params.gpu_route_id,
221 params.gpu_host_id,
222 params.mailbox_name,
223 params.sync_point);
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())
233 continue;
235 if (callback.Run(guest))
236 return true;
238 return false;
241 } // namespace content