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 "extensions/browser/guest_view/guest_view_manager.h"
7 #include "base/strings/stringprintf.h"
8 #include "content/public/browser/browser_context.h"
9 #include "content/public/browser/render_process_host.h"
10 #include "content/public/browser/render_view_host.h"
11 #include "content/public/browser/user_metrics.h"
12 #include "content/public/browser/web_contents_observer.h"
13 #include "content/public/common/result_codes.h"
14 #include "content/public/common/url_constants.h"
15 #include "extensions/browser/extension_system.h"
16 #include "extensions/browser/guest_view/guest_view_base.h"
17 #include "extensions/browser/guest_view/guest_view_manager_factory.h"
18 #include "extensions/common/guest_view/guest_view_constants.h"
19 #include "net/base/escape.h"
22 using content::BrowserContext;
23 using content::SiteInstance;
24 using content::WebContents;
26 namespace extensions {
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);
64 void GuestViewManager::AttachGuest(
65 int embedder_render_process_id,
66 int embedder_routing_id,
67 int element_instance_id,
68 int guest_instance_id,
69 const base::DictionaryValue& attach_params) {
70 content::WebContents* guest_web_contents =
71 GetGuestByInstanceIDSafely(guest_instance_id, embedder_render_process_id);
72 if (!guest_web_contents)
75 GuestViewBase* guest_view =
76 GuestViewBase::FromWebContents(guest_web_contents);
79 content::RenderViewHost* rvh =
80 content::RenderViewHost::FromID(embedder_render_process_id,
82 content::WebContents* embedder_web_contents =
83 content::WebContents::FromRenderViewHost(rvh);
84 if (!embedder_web_contents)
86 ElementInstanceKey key(embedder_web_contents, element_instance_id);
88 GuestInstanceIDMap::iterator it = instance_id_map_.find(key);
89 if (it != instance_id_map_.end()) {
90 int old_guest_instance_id = it->second;
91 // Reattachment to the same guest is not currently supported.
92 if (old_guest_instance_id == guest_instance_id)
95 content::WebContents* old_guest_web_contents =
96 GetGuestByInstanceIDSafely(old_guest_instance_id,
97 embedder_render_process_id);
98 if (!old_guest_web_contents)
101 GuestViewBase* old_guest_view =
102 GuestViewBase::FromWebContents(old_guest_web_contents);
104 old_guest_view->Destroy();
106 instance_id_map_[key] = guest_instance_id;
107 reverse_instance_id_map_.insert(std::make_pair(guest_instance_id, key));
108 guest_view->SetAttachParams(attach_params);
111 int GuestViewManager::GetNextInstanceID() {
112 return ++current_instance_id_;
115 void GuestViewManager::CreateGuest(const std::string& view_type,
116 const std::string& embedder_extension_id,
117 content::WebContents* embedder_web_contents,
118 const base::DictionaryValue& create_params,
119 const WebContentsCreatedCallback& callback) {
120 int guest_instance_id = GetNextInstanceID();
121 GuestViewBase* guest =
122 GuestViewBase::Create(context_, guest_instance_id, view_type);
128 embedder_extension_id, embedder_web_contents, create_params, callback);
131 content::WebContents* GuestViewManager::CreateGuestWithWebContentsParams(
132 const std::string& view_type,
133 const std::string& embedder_extension_id,
134 int embedder_render_process_id,
135 const content::WebContents::CreateParams& create_params) {
136 int guest_instance_id = GetNextInstanceID();
137 GuestViewBase* guest =
138 GuestViewBase::Create(context_, guest_instance_id, view_type);
141 content::WebContents::CreateParams guest_create_params(create_params);
142 guest_create_params.guest_delegate = guest;
143 content::WebContents* guest_web_contents =
144 WebContents::Create(guest_create_params);
145 guest->InitWithWebContents(embedder_extension_id,
146 embedder_render_process_id,
148 return guest_web_contents;
151 content::WebContents* GuestViewManager::GetGuestByInstanceID(
152 content::WebContents* embedder_web_contents,
153 int element_instance_id) {
154 int guest_instance_id = GetGuestInstanceIDForElementID(embedder_web_contents,
155 element_instance_id);
156 if (guest_instance_id == guestview::kInstanceIDNone)
159 return GetGuestByInstanceID(guest_instance_id);
162 int GuestViewManager::GetGuestInstanceIDForElementID(
163 content::WebContents* embedder_web_contents,
164 int element_instance_id) {
165 GuestInstanceIDMap::iterator iter = instance_id_map_.find(
166 ElementInstanceKey(embedder_web_contents, element_instance_id));
167 if (iter == instance_id_map_.end())
168 return guestview::kInstanceIDNone;
172 SiteInstance* GuestViewManager::GetGuestSiteInstance(
173 const GURL& guest_site) {
174 for (GuestInstanceMap::const_iterator it =
175 guest_web_contents_by_instance_id_.begin();
176 it != guest_web_contents_by_instance_id_.end(); ++it) {
177 if (it->second->GetSiteInstance()->GetSiteURL() == guest_site)
178 return it->second->GetSiteInstance();
183 bool GuestViewManager::ForEachGuest(WebContents* embedder_web_contents,
184 const GuestCallback& callback) {
185 for (GuestInstanceMap::iterator it =
186 guest_web_contents_by_instance_id_.begin();
187 it != guest_web_contents_by_instance_id_.end(); ++it) {
188 WebContents* guest = it->second;
189 GuestViewBase* guest_view = GuestViewBase::FromWebContents(guest);
190 if (embedder_web_contents != guest_view->embedder_web_contents())
193 if (callback.Run(guest))
199 void GuestViewManager::AddGuest(int guest_instance_id,
200 WebContents* guest_web_contents) {
201 CHECK(!ContainsKey(guest_web_contents_by_instance_id_, guest_instance_id));
202 CHECK(CanUseGuestInstanceID(guest_instance_id));
203 guest_web_contents_by_instance_id_[guest_instance_id] = guest_web_contents;
206 void GuestViewManager::RemoveGuest(int guest_instance_id) {
207 GuestInstanceMap::iterator it =
208 guest_web_contents_by_instance_id_.find(guest_instance_id);
209 DCHECK(it != guest_web_contents_by_instance_id_.end());
210 guest_web_contents_by_instance_id_.erase(it);
212 GuestInstanceIDReverseMap::iterator id_iter =
213 reverse_instance_id_map_.find(guest_instance_id);
214 if (id_iter != reverse_instance_id_map_.end()) {
215 const ElementInstanceKey& instance_id_key = id_iter->second;
216 instance_id_map_.erase(instance_id_map_.find(instance_id_key));
217 reverse_instance_id_map_.erase(id_iter);
220 // All the instance IDs that lie within [0, last_instance_id_removed_]
222 // The remaining sparse invalid IDs are kept in |removed_instance_ids_| set.
223 // The following code compacts the set by incrementing
224 // |last_instance_id_removed_|.
225 if (guest_instance_id == last_instance_id_removed_ + 1) {
226 ++last_instance_id_removed_;
228 std::set<int>::iterator iter = removed_instance_ids_.begin();
229 while (iter != removed_instance_ids_.end()) {
230 int instance_id = *iter;
231 // The sparse invalid IDs must not lie within
232 // [0, last_instance_id_removed_]
233 DCHECK(instance_id > last_instance_id_removed_);
234 if (instance_id != last_instance_id_removed_ + 1)
236 ++last_instance_id_removed_;
237 removed_instance_ids_.erase(iter++);
240 removed_instance_ids_.insert(guest_instance_id);
244 content::WebContents* GuestViewManager::GetGuestByInstanceID(
245 int guest_instance_id) {
246 GuestInstanceMap::const_iterator it =
247 guest_web_contents_by_instance_id_.find(guest_instance_id);
248 if (it == guest_web_contents_by_instance_id_.end())
253 bool GuestViewManager::CanEmbedderAccessInstanceIDMaybeKill(
254 int embedder_render_process_id,
255 int guest_instance_id) {
256 if (!CanEmbedderAccessInstanceID(embedder_render_process_id,
257 guest_instance_id)) {
258 // The embedder process is trying to access a guest it does not own.
259 content::RecordAction(
260 base::UserMetricsAction("BadMessageTerminate_BPGM"));
262 content::RenderProcessHost::FromID(embedder_render_process_id)->
264 content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
270 bool GuestViewManager::CanUseGuestInstanceID(int guest_instance_id) {
271 if (guest_instance_id <= last_instance_id_removed_)
273 return !ContainsKey(removed_instance_ids_, guest_instance_id);
276 bool GuestViewManager::CanEmbedderAccessInstanceID(
277 int embedder_render_process_id,
278 int guest_instance_id) {
279 // The embedder is trying to access a guest with a negative or zero
281 if (guest_instance_id <= guestview::kInstanceIDNone)
284 // The embedder is trying to access an instance ID that has not yet been
285 // allocated by GuestViewManager. This could cause instance ID
286 // collisions in the future, and potentially give one embedder access to a
287 // guest it does not own.
288 if (guest_instance_id > current_instance_id_)
291 // We might get some late arriving messages at tear down. Let's let the
292 // embedder tear down in peace.
293 GuestInstanceMap::const_iterator it =
294 guest_web_contents_by_instance_id_.find(guest_instance_id);
295 if (it == guest_web_contents_by_instance_id_.end())
298 GuestViewBase* guest_view = GuestViewBase::FromWebContents(it->second);
302 return embedder_render_process_id == guest_view->embedder_render_process_id();
305 } // namespace extensions