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.
5 #include "chrome/browser/guest_view/guest_view_manager.h"
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"
24 using content::BrowserContext;
25 using content::SiteInstance;
26 using content::WebContents;
29 GuestViewManagerFactory* GuestViewManager::factory_ = NULL;
31 GuestViewManager::GuestViewManager(content::BrowserContext* context)
32 : current_instance_id_(0), last_instance_id_removed_(0), context_(context) {
35 GuestViewManager::~GuestViewManager() {}
38 GuestViewManager* GuestViewManager::FromBrowserContext(
39 BrowserContext* context) {
40 GuestViewManager* guest_manager =
41 static_cast<GuestViewManager*>(context->GetUserData(
42 guestview::kGuestViewManagerKeyName));
45 guest_manager = factory_->CreateGuestViewManager(context);
47 guest_manager = new GuestViewManager(context);
49 context->SetUserData(guestview::kGuestViewManagerKeyName, guest_manager);
54 content::WebContents* GuestViewManager::GetGuestByInstanceIDSafely(
55 int guest_instance_id,
56 int embedder_render_process_id) {
57 if (!CanEmbedderAccessInstanceIDMaybeKill(embedder_render_process_id,
61 return GetGuestByInstanceID(guest_instance_id, embedder_render_process_id);
64 int GuestViewManager::GetNextInstanceID() {
65 return ++current_instance_id_;
68 content::WebContents* GuestViewManager::CreateGuest(
69 content::SiteInstance* embedder_site_instance,
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);
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
84 if (!base::IsStringUTF8(storage_partition_id)) {
85 content::RecordAction(
86 base::UserMetricsAction("BadMessageTerminate_BPGM"));
88 embedder_process_host->GetHandle(),
89 content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
93 const GURL& embedder_site_url = embedder_site_instance->GetSiteURL();
94 const std::string& host = embedder_site_url.host();
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,
105 persist_storage ? "persist" : "",
106 url_encoded_partition.c_str()));
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);
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);
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.
136 content::WebContents* guest_web_contents =
137 GetGuestByInstanceID(guest_instance_id, embedder_render_process_id);
138 callback.Run(guest_web_contents);
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();
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())
162 if (callback.Run(guest))
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;
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);
181 // All the instance IDs that lie within [0, last_instance_id_removed_]
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_;
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)
197 ++last_instance_id_removed_;
198 removed_instance_ids_.erase(iter++);
201 removed_instance_ids_.insert(guest_instance_id);
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())
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"));
224 content::RenderProcessHost::FromID(embedder_render_process_id)->
226 content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
232 bool GuestViewManager::CanUseGuestInstanceID(int guest_instance_id) {
233 if (guest_instance_id <= last_instance_id_removed_)
235 return !ContainsKey(removed_instance_ids_, guest_instance_id);
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
243 if (guest_instance_id <= guestview::kInstanceIDNone)
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_)
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())
258 GuestViewBase* guest_view = GuestViewBase::FromWebContents(it->second);
262 return CanEmbedderAccessGuest(embedder_render_process_id, guest_view);
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())
273 return embedder_render_process_id ==
274 guest->GetOpener()->embedder_web_contents()->GetRenderProcessHost()->
278 return embedder_render_process_id ==
279 guest->embedder_web_contents()->GetRenderProcessHost()->GetID();