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_frame_host.h"
10 #include "content/public/browser/render_process_host.h"
11 #include "content/public/browser/render_view_host.h"
12 #include "content/public/browser/user_metrics.h"
13 #include "content/public/browser/web_contents_observer.h"
14 #include "content/public/common/result_codes.h"
15 #include "content/public/common/url_constants.h"
16 #include "extensions/browser/extension_system.h"
17 #include "extensions/browser/guest_view/guest_view_base.h"
18 #include "extensions/browser/guest_view/guest_view_manager_factory.h"
19 #include "extensions/common/extension_messages.h"
20 #include "extensions/common/guest_view/guest_view_constants.h"
21 #include "net/base/escape.h"
24 using content::BrowserContext
;
25 using content::SiteInstance
;
26 using content::WebContents
;
28 namespace extensions
{
31 GuestViewManagerFactory
* GuestViewManager::factory_
= nullptr;
33 GuestViewManager::GuestViewManager(content::BrowserContext
* context
)
34 : current_instance_id_(0), last_instance_id_removed_(0), context_(context
) {
37 GuestViewManager::~GuestViewManager() {}
40 GuestViewManager
* GuestViewManager::FromBrowserContext(
41 BrowserContext
* context
) {
42 GuestViewManager
* guest_manager
=
43 static_cast<GuestViewManager
*>(context
->GetUserData(
44 guestview::kGuestViewManagerKeyName
));
47 guest_manager
= factory_
->CreateGuestViewManager(context
);
49 guest_manager
= new GuestViewManager(context
);
51 context
->SetUserData(guestview::kGuestViewManagerKeyName
, guest_manager
);
57 GuestViewManager
* GuestViewManager::FromBrowserContextIfAvailable(
58 BrowserContext
* context
) {
59 return static_cast<GuestViewManager
*>(context
->GetUserData(
60 guestview::kGuestViewManagerKeyName
));
63 content::WebContents
* GuestViewManager::GetGuestByInstanceIDSafely(
64 int guest_instance_id
,
65 int embedder_render_process_id
) {
66 if (!CanEmbedderAccessInstanceIDMaybeKill(embedder_render_process_id
,
70 return GetGuestByInstanceID(guest_instance_id
);
73 void GuestViewManager::AttachGuest(
74 int embedder_render_process_id
,
75 int embedder_routing_id
,
76 int element_instance_id
,
77 int guest_instance_id
,
78 const base::DictionaryValue
& attach_params
) {
79 content::WebContents
* guest_web_contents
=
80 GetGuestByInstanceIDSafely(guest_instance_id
, embedder_render_process_id
);
81 if (!guest_web_contents
)
84 auto guest_view
= GuestViewBase::FromWebContents(guest_web_contents
);
87 auto rvh
= content::RenderViewHost::FromID(embedder_render_process_id
,
89 // We need to check that rvh is not nullptr because there may be a race
90 // between AttachGuest and destroying the embedder (i.e. when the embedder is
91 // destroyed immediately after the guest is created).
94 auto owner_web_contents
= content::WebContents::FromRenderViewHost(rvh
);
95 if (!owner_web_contents
)
97 ElementInstanceKey
key(owner_web_contents
, element_instance_id
);
99 auto it
= instance_id_map_
.find(key
);
100 if (it
!= instance_id_map_
.end()) {
101 int old_guest_instance_id
= it
->second
;
102 // Reattachment to the same guest is not currently supported.
103 if (old_guest_instance_id
== guest_instance_id
)
106 auto old_guest_web_contents
=
107 GetGuestByInstanceIDSafely(old_guest_instance_id
,
108 embedder_render_process_id
);
109 if (!old_guest_web_contents
)
112 auto old_guest_view
=
113 GuestViewBase::FromWebContents(old_guest_web_contents
);
115 old_guest_view
->Destroy();
117 instance_id_map_
[key
] = guest_instance_id
;
118 reverse_instance_id_map_
[guest_instance_id
] = key
;
119 guest_view
->SetAttachParams(attach_params
);
122 void GuestViewManager::DetachGuest(GuestViewBase
* guest
,
123 int element_instance_id
) {
124 if (!guest
->attached())
127 ElementInstanceKey
key(guest
->owner_web_contents(), element_instance_id
);
128 auto it
= instance_id_map_
.find(key
);
129 // There's nothing to do if this key does not exist in the map.
130 if (it
== instance_id_map_
.end())
133 int guest_instance_id
= it
->second
;
134 instance_id_map_
.erase(key
);
136 auto reverse_it
= reverse_instance_id_map_
.find(guest
->guest_instance_id());
137 DCHECK(reverse_it
!= reverse_instance_id_map_
.end());
138 DCHECK(reverse_it
->second
== key
);
139 reverse_instance_id_map_
.erase(guest_instance_id
);
142 int GuestViewManager::GetNextInstanceID() {
143 return ++current_instance_id_
;
146 void GuestViewManager::CreateGuest(const std::string
& view_type
,
147 content::WebContents
* owner_web_contents
,
148 const base::DictionaryValue
& create_params
,
149 const WebContentsCreatedCallback
& callback
) {
150 auto guest
= GuestViewBase::Create(owner_web_contents
, view_type
);
152 callback
.Run(nullptr);
155 guest
->Init(create_params
, callback
);
158 content::WebContents
* GuestViewManager::CreateGuestWithWebContentsParams(
159 const std::string
& view_type
,
160 content::WebContents
* owner_web_contents
,
161 const content::WebContents::CreateParams
& create_params
) {
162 auto guest
= GuestViewBase::Create(owner_web_contents
, view_type
);
165 content::WebContents::CreateParams
guest_create_params(create_params
);
166 guest_create_params
.guest_delegate
= guest
;
167 auto guest_web_contents
= WebContents::Create(guest_create_params
);
168 guest
->InitWithWebContents(base::DictionaryValue(), guest_web_contents
);
169 return guest_web_contents
;
172 content::WebContents
* GuestViewManager::GetGuestByInstanceID(
173 content::WebContents
* owner_web_contents
,
174 int element_instance_id
) {
175 int guest_instance_id
= GetGuestInstanceIDForElementID(owner_web_contents
,
176 element_instance_id
);
177 if (guest_instance_id
== guestview::kInstanceIDNone
)
180 return GetGuestByInstanceID(guest_instance_id
);
183 int GuestViewManager::GetGuestInstanceIDForElementID(
184 content::WebContents
* owner_web_contents
,
185 int element_instance_id
) {
186 auto iter
= instance_id_map_
.find(
187 ElementInstanceKey(owner_web_contents
, element_instance_id
));
188 if (iter
== instance_id_map_
.end())
189 return guestview::kInstanceIDNone
;
193 SiteInstance
* GuestViewManager::GetGuestSiteInstance(
194 const GURL
& guest_site
) {
195 for (const auto& guest
: guest_web_contents_by_instance_id_
) {
196 if (guest
.second
->GetSiteInstance()->GetSiteURL() == guest_site
)
197 return guest
.second
->GetSiteInstance();
202 bool GuestViewManager::ForEachGuest(WebContents
* owner_web_contents
,
203 const GuestCallback
& callback
) {
204 for (const auto& guest
: guest_web_contents_by_instance_id_
) {
205 auto guest_view
= GuestViewBase::FromWebContents(guest
.second
);
206 if (guest_view
->owner_web_contents() != owner_web_contents
)
209 if (callback
.Run(guest_view
->web_contents()))
215 void GuestViewManager::AddGuest(int guest_instance_id
,
216 WebContents
* guest_web_contents
) {
217 CHECK(!ContainsKey(guest_web_contents_by_instance_id_
, guest_instance_id
));
218 CHECK(CanUseGuestInstanceID(guest_instance_id
));
219 guest_web_contents_by_instance_id_
[guest_instance_id
] = guest_web_contents
;
222 void GuestViewManager::RemoveGuest(int guest_instance_id
) {
223 auto it
= guest_web_contents_by_instance_id_
.find(guest_instance_id
);
224 DCHECK(it
!= guest_web_contents_by_instance_id_
.end());
225 guest_web_contents_by_instance_id_
.erase(it
);
227 auto id_iter
= reverse_instance_id_map_
.find(guest_instance_id
);
228 if (id_iter
!= reverse_instance_id_map_
.end()) {
229 const ElementInstanceKey
& instance_id_key
= id_iter
->second
;
230 instance_id_map_
.erase(instance_id_map_
.find(instance_id_key
));
231 reverse_instance_id_map_
.erase(id_iter
);
234 // All the instance IDs that lie within [0, last_instance_id_removed_]
236 // The remaining sparse invalid IDs are kept in |removed_instance_ids_| set.
237 // The following code compacts the set by incrementing
238 // |last_instance_id_removed_|.
239 if (guest_instance_id
== last_instance_id_removed_
+ 1) {
240 ++last_instance_id_removed_
;
242 auto iter
= removed_instance_ids_
.begin();
243 while (iter
!= removed_instance_ids_
.end()) {
244 int instance_id
= *iter
;
245 // The sparse invalid IDs must not lie within
246 // [0, last_instance_id_removed_]
247 DCHECK(instance_id
> last_instance_id_removed_
);
248 if (instance_id
!= last_instance_id_removed_
+ 1)
250 ++last_instance_id_removed_
;
251 removed_instance_ids_
.erase(iter
++);
254 removed_instance_ids_
.insert(guest_instance_id
);
258 content::WebContents
* GuestViewManager::GetGuestByInstanceID(
259 int guest_instance_id
) {
260 auto it
= guest_web_contents_by_instance_id_
.find(guest_instance_id
);
261 if (it
== guest_web_contents_by_instance_id_
.end())
266 bool GuestViewManager::CanEmbedderAccessInstanceIDMaybeKill(
267 int embedder_render_process_id
,
268 int guest_instance_id
) {
269 if (!CanEmbedderAccessInstanceID(embedder_render_process_id
,
270 guest_instance_id
)) {
271 // The embedder process is trying to access a guest it does not own.
272 content::RecordAction(
273 base::UserMetricsAction("BadMessageTerminate_BPGM"));
274 content::RenderProcessHost::FromID(embedder_render_process_id
)
275 ->Shutdown(content::RESULT_CODE_KILLED_BAD_MESSAGE
, false);
281 bool GuestViewManager::CanUseGuestInstanceID(int guest_instance_id
) {
282 if (guest_instance_id
<= last_instance_id_removed_
)
284 return !ContainsKey(removed_instance_ids_
, guest_instance_id
);
287 bool GuestViewManager::CanEmbedderAccessInstanceID(
288 int embedder_render_process_id
,
289 int guest_instance_id
) {
290 // The embedder is trying to access a guest with a negative or zero
292 if (guest_instance_id
<= guestview::kInstanceIDNone
)
295 // The embedder is trying to access an instance ID that has not yet been
296 // allocated by GuestViewManager. This could cause instance ID
297 // collisions in the future, and potentially give one embedder access to a
298 // guest it does not own.
299 if (guest_instance_id
> current_instance_id_
)
302 // We might get some late arriving messages at tear down. Let's let the
303 // embedder tear down in peace.
304 auto it
= guest_web_contents_by_instance_id_
.find(guest_instance_id
);
305 if (it
== guest_web_contents_by_instance_id_
.end())
308 auto guest_view
= GuestViewBase::FromWebContents(it
->second
);
312 return embedder_render_process_id
==
313 guest_view
->owner_web_contents()->GetRenderProcessHost()->GetID();
316 } // namespace extensions