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/child_process_host.h"
15 #include "content/public/common/result_codes.h"
16 #include "content/public/common/url_constants.h"
17 #include "extensions/browser/extension_system.h"
18 #include "extensions/browser/guest_view/guest_view_base.h"
19 #include "extensions/browser/guest_view/guest_view_manager_factory.h"
20 #include "extensions/common/extension_messages.h"
21 #include "extensions/common/guest_view/guest_view_constants.h"
22 #include "net/base/escape.h"
25 using content::BrowserContext
;
26 using content::SiteInstance
;
27 using content::WebContents
;
29 namespace extensions
{
32 GuestViewManagerFactory
* GuestViewManager::factory_
= nullptr;
34 GuestViewManager::GuestViewManager(content::BrowserContext
* context
)
35 : current_instance_id_(0), last_instance_id_removed_(0), context_(context
) {
38 GuestViewManager::~GuestViewManager() {}
41 GuestViewManager
* GuestViewManager::FromBrowserContext(
42 BrowserContext
* context
) {
43 GuestViewManager
* guest_manager
=
44 static_cast<GuestViewManager
*>(context
->GetUserData(
45 guestview::kGuestViewManagerKeyName
));
48 guest_manager
= factory_
->CreateGuestViewManager(context
);
50 guest_manager
= new GuestViewManager(context
);
52 context
->SetUserData(guestview::kGuestViewManagerKeyName
, guest_manager
);
58 GuestViewManager
* GuestViewManager::FromBrowserContextIfAvailable(
59 BrowserContext
* context
) {
60 return static_cast<GuestViewManager
*>(context
->GetUserData(
61 guestview::kGuestViewManagerKeyName
));
64 content::WebContents
* GuestViewManager::GetGuestByInstanceIDSafely(
65 int guest_instance_id
,
66 int embedder_render_process_id
) {
67 if (!CanEmbedderAccessInstanceIDMaybeKill(embedder_render_process_id
,
71 return GetGuestByInstanceID(guest_instance_id
);
74 void GuestViewManager::AttachGuest(int embedder_process_id
,
75 int element_instance_id
,
76 int guest_instance_id
,
77 const base::DictionaryValue
& attach_params
) {
78 auto guest_view
= GuestViewBase::From(embedder_process_id
, guest_instance_id
);
82 ElementInstanceKey
key(embedder_process_id
, element_instance_id
);
83 auto it
= instance_id_map_
.find(key
);
84 // If there is an existing guest attached to the element, then destroy the
86 if (it
!= instance_id_map_
.end()) {
87 int old_guest_instance_id
= it
->second
;
88 if (old_guest_instance_id
== guest_instance_id
)
91 auto old_guest_view
= GuestViewBase::From(embedder_process_id
,
92 old_guest_instance_id
);
93 old_guest_view
->Destroy();
95 instance_id_map_
[key
] = guest_instance_id
;
96 reverse_instance_id_map_
[guest_instance_id
] = key
;
97 guest_view
->SetAttachParams(attach_params
);
100 void GuestViewManager::DetachGuest(GuestViewBase
* guest
) {
101 if (!guest
->attached())
104 auto reverse_it
= reverse_instance_id_map_
.find(guest
->guest_instance_id());
105 if (reverse_it
== reverse_instance_id_map_
.end())
108 const ElementInstanceKey
& key
= reverse_it
->second
;
110 auto it
= instance_id_map_
.find(key
);
111 DCHECK(it
!= instance_id_map_
.end());
113 reverse_instance_id_map_
.erase(reverse_it
);
114 instance_id_map_
.erase(it
);
117 int GuestViewManager::GetNextInstanceID() {
118 return ++current_instance_id_
;
121 void GuestViewManager::CreateGuest(const std::string
& view_type
,
122 content::WebContents
* owner_web_contents
,
123 const base::DictionaryValue
& create_params
,
124 const WebContentsCreatedCallback
& callback
) {
125 auto guest
= GuestViewBase::Create(owner_web_contents
, view_type
);
127 callback
.Run(nullptr);
130 guest
->Init(create_params
, callback
);
133 content::WebContents
* GuestViewManager::CreateGuestWithWebContentsParams(
134 const std::string
& view_type
,
135 content::WebContents
* owner_web_contents
,
136 const content::WebContents::CreateParams
& create_params
) {
137 auto guest
= GuestViewBase::Create(owner_web_contents
, view_type
);
140 content::WebContents::CreateParams
guest_create_params(create_params
);
141 guest_create_params
.guest_delegate
= guest
;
142 auto guest_web_contents
= WebContents::Create(guest_create_params
);
143 guest
->InitWithWebContents(base::DictionaryValue(), guest_web_contents
);
144 return guest_web_contents
;
147 content::WebContents
* GuestViewManager::GetGuestByInstanceID(
148 int owner_process_id
,
149 int element_instance_id
) {
150 int guest_instance_id
= GetGuestInstanceIDForElementID(owner_process_id
,
151 element_instance_id
);
152 if (guest_instance_id
== guestview::kInstanceIDNone
)
155 return GetGuestByInstanceID(guest_instance_id
);
158 int GuestViewManager::GetGuestInstanceIDForElementID(int owner_process_id
,
159 int element_instance_id
) {
160 auto iter
= instance_id_map_
.find(
161 ElementInstanceKey(owner_process_id
, element_instance_id
));
162 if (iter
== instance_id_map_
.end())
163 return guestview::kInstanceIDNone
;
167 SiteInstance
* GuestViewManager::GetGuestSiteInstance(
168 const GURL
& guest_site
) {
169 for (const auto& guest
: guest_web_contents_by_instance_id_
) {
170 if (guest
.second
->GetSiteInstance()->GetSiteURL() == guest_site
)
171 return guest
.second
->GetSiteInstance();
176 bool GuestViewManager::ForEachGuest(WebContents
* owner_web_contents
,
177 const GuestCallback
& callback
) {
178 for (const auto& guest
: guest_web_contents_by_instance_id_
) {
179 auto guest_view
= GuestViewBase::FromWebContents(guest
.second
);
180 if (guest_view
->owner_web_contents() != owner_web_contents
)
183 if (callback
.Run(guest_view
->web_contents()))
189 void GuestViewManager::AddGuest(int guest_instance_id
,
190 WebContents
* guest_web_contents
) {
191 CHECK(!ContainsKey(guest_web_contents_by_instance_id_
, guest_instance_id
));
192 CHECK(CanUseGuestInstanceID(guest_instance_id
));
193 guest_web_contents_by_instance_id_
[guest_instance_id
] = guest_web_contents
;
196 void GuestViewManager::RemoveGuest(int guest_instance_id
) {
197 auto it
= guest_web_contents_by_instance_id_
.find(guest_instance_id
);
198 DCHECK(it
!= guest_web_contents_by_instance_id_
.end());
199 guest_web_contents_by_instance_id_
.erase(it
);
201 auto id_iter
= reverse_instance_id_map_
.find(guest_instance_id
);
202 if (id_iter
!= reverse_instance_id_map_
.end()) {
203 const ElementInstanceKey
& instance_id_key
= id_iter
->second
;
204 instance_id_map_
.erase(instance_id_map_
.find(instance_id_key
));
205 reverse_instance_id_map_
.erase(id_iter
);
208 // All the instance IDs that lie within [0, last_instance_id_removed_]
210 // The remaining sparse invalid IDs are kept in |removed_instance_ids_| set.
211 // The following code compacts the set by incrementing
212 // |last_instance_id_removed_|.
213 if (guest_instance_id
== last_instance_id_removed_
+ 1) {
214 ++last_instance_id_removed_
;
216 auto iter
= removed_instance_ids_
.begin();
217 while (iter
!= removed_instance_ids_
.end()) {
218 int instance_id
= *iter
;
219 // The sparse invalid IDs must not lie within
220 // [0, last_instance_id_removed_]
221 DCHECK(instance_id
> last_instance_id_removed_
);
222 if (instance_id
!= last_instance_id_removed_
+ 1)
224 ++last_instance_id_removed_
;
225 removed_instance_ids_
.erase(iter
++);
228 removed_instance_ids_
.insert(guest_instance_id
);
232 content::WebContents
* GuestViewManager::GetGuestByInstanceID(
233 int guest_instance_id
) {
234 auto it
= guest_web_contents_by_instance_id_
.find(guest_instance_id
);
235 if (it
== guest_web_contents_by_instance_id_
.end())
240 bool GuestViewManager::CanEmbedderAccessInstanceIDMaybeKill(
241 int embedder_render_process_id
,
242 int guest_instance_id
) {
243 if (!CanEmbedderAccessInstanceID(embedder_render_process_id
,
244 guest_instance_id
)) {
245 // The embedder process is trying to access a guest it does not own.
246 content::RecordAction(
247 base::UserMetricsAction("BadMessageTerminate_BPGM"));
248 content::RenderProcessHost::FromID(embedder_render_process_id
)
249 ->Shutdown(content::RESULT_CODE_KILLED_BAD_MESSAGE
, false);
255 bool GuestViewManager::CanUseGuestInstanceID(int guest_instance_id
) {
256 if (guest_instance_id
<= last_instance_id_removed_
)
258 return !ContainsKey(removed_instance_ids_
, guest_instance_id
);
261 bool GuestViewManager::CanEmbedderAccessInstanceID(
262 int embedder_render_process_id
,
263 int guest_instance_id
) {
264 // The embedder is trying to access a guest with a negative or zero
266 if (guest_instance_id
<= guestview::kInstanceIDNone
)
269 // The embedder is trying to access an instance ID that has not yet been
270 // allocated by GuestViewManager. This could cause instance ID
271 // collisions in the future, and potentially give one embedder access to a
272 // guest it does not own.
273 if (guest_instance_id
> current_instance_id_
)
276 // We might get some late arriving messages at tear down. Let's let the
277 // embedder tear down in peace.
278 auto it
= guest_web_contents_by_instance_id_
.find(guest_instance_id
);
279 if (it
== guest_web_contents_by_instance_id_
.end())
282 auto guest_view
= GuestViewBase::FromWebContents(it
->second
);
286 return embedder_render_process_id
==
287 guest_view
->owner_web_contents()->GetRenderProcessHost()->GetID();
290 GuestViewManager::ElementInstanceKey::ElementInstanceKey()
291 : embedder_process_id(content::ChildProcessHost::kInvalidUniqueID
),
292 element_instance_id(content::ChildProcessHost::kInvalidUniqueID
) {
295 GuestViewManager::ElementInstanceKey::ElementInstanceKey(
296 int embedder_process_id
,
297 int element_instance_id
)
298 : embedder_process_id(embedder_process_id
),
299 element_instance_id(element_instance_id
) {
302 bool GuestViewManager::ElementInstanceKey::operator<(
303 const GuestViewManager::ElementInstanceKey
& other
) const {
304 if (embedder_process_id
!= other
.embedder_process_id
)
305 return embedder_process_id
< other
.embedder_process_id
;
307 return element_instance_id
< other
.element_instance_id
;
310 bool GuestViewManager::ElementInstanceKey::operator==(
311 const GuestViewManager::ElementInstanceKey
& other
) const {
312 return (embedder_process_id
== other
.embedder_process_id
) &&
313 (element_instance_id
== other
.element_instance_id
);
316 } // namespace extensions