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 "components/guest_view/browser/guest_view_manager.h"
7 #include "base/macros.h"
8 #include "base/strings/stringprintf.h"
9 #include "components/guest_view/browser/guest_view_base.h"
10 #include "components/guest_view/browser/guest_view_manager_delegate.h"
11 #include "components/guest_view/browser/guest_view_manager_factory.h"
12 #include "components/guest_view/common/guest_view_constants.h"
13 #include "content/public/browser/browser_context.h"
14 #include "content/public/browser/render_frame_host.h"
15 #include "content/public/browser/render_process_host.h"
16 #include "content/public/browser/render_view_host.h"
17 #include "content/public/browser/user_metrics.h"
18 #include "content/public/browser/web_contents_observer.h"
19 #include "content/public/common/child_process_host.h"
20 #include "content/public/common/result_codes.h"
21 #include "content/public/common/url_constants.h"
24 using content::BrowserContext
;
25 using content::SiteInstance
;
26 using content::WebContents
;
28 namespace guest_view
{
31 GuestViewManagerFactory
* GuestViewManager::factory_
= nullptr;
33 GuestViewManager::GuestViewManager(
34 content::BrowserContext
* context
,
35 scoped_ptr
<GuestViewManagerDelegate
> delegate
)
36 : current_instance_id_(0),
37 last_instance_id_removed_(0),
39 delegate_(delegate
.Pass()) {
42 GuestViewManager::~GuestViewManager() {}
45 GuestViewManager
* GuestViewManager::CreateWithDelegate(
46 BrowserContext
* context
,
47 scoped_ptr
<GuestViewManagerDelegate
> delegate
) {
48 GuestViewManager
* guest_manager
= FromBrowserContext(context
);
52 factory_
->CreateGuestViewManager(context
, delegate
.Pass());
54 guest_manager
= new GuestViewManager(context
, delegate
.Pass());
56 context
->SetUserData(kGuestViewManagerKeyName
, guest_manager
);
62 GuestViewManager
* GuestViewManager::FromBrowserContext(
63 BrowserContext
* context
) {
64 return static_cast<GuestViewManager
*>(context
->GetUserData(
65 kGuestViewManagerKeyName
));
68 content::WebContents
* GuestViewManager::GetGuestByInstanceIDSafely(
69 int guest_instance_id
,
70 int embedder_render_process_id
) {
71 if (!CanEmbedderAccessInstanceIDMaybeKill(embedder_render_process_id
,
75 return GetGuestByInstanceID(guest_instance_id
);
78 void GuestViewManager::AttachGuest(int embedder_process_id
,
79 int element_instance_id
,
80 int guest_instance_id
,
81 const base::DictionaryValue
& attach_params
) {
82 auto guest_view
= GuestViewBase::From(embedder_process_id
, guest_instance_id
);
86 ElementInstanceKey
key(embedder_process_id
, element_instance_id
);
87 auto it
= instance_id_map_
.find(key
);
88 // If there is an existing guest attached to the element, then destroy the
90 if (it
!= instance_id_map_
.end()) {
91 int old_guest_instance_id
= it
->second
;
92 if (old_guest_instance_id
== guest_instance_id
)
95 auto old_guest_view
= GuestViewBase::From(embedder_process_id
,
96 old_guest_instance_id
);
97 old_guest_view
->Destroy();
99 instance_id_map_
[key
] = guest_instance_id
;
100 reverse_instance_id_map_
[guest_instance_id
] = key
;
101 guest_view
->SetAttachParams(attach_params
);
104 void GuestViewManager::DetachGuest(GuestViewBase
* guest
) {
105 if (!guest
->attached())
108 auto reverse_it
= reverse_instance_id_map_
.find(guest
->guest_instance_id());
109 if (reverse_it
== reverse_instance_id_map_
.end())
112 const ElementInstanceKey
& key
= reverse_it
->second
;
114 auto it
= instance_id_map_
.find(key
);
115 DCHECK(it
!= instance_id_map_
.end());
117 reverse_instance_id_map_
.erase(reverse_it
);
118 instance_id_map_
.erase(it
);
121 bool GuestViewManager::IsOwnedByExtension(GuestViewBase
* guest
) {
122 return delegate_
->IsOwnedByExtension(guest
);
125 int GuestViewManager::GetNextInstanceID() {
126 return ++current_instance_id_
;
129 void GuestViewManager::CreateGuest(const std::string
& view_type
,
130 content::WebContents
* owner_web_contents
,
131 const base::DictionaryValue
& create_params
,
132 const WebContentsCreatedCallback
& callback
) {
133 GuestViewBase
* guest
= CreateGuestInternal(owner_web_contents
, view_type
);
135 callback
.Run(nullptr);
138 guest
->Init(create_params
, callback
);
141 content::WebContents
* GuestViewManager::CreateGuestWithWebContentsParams(
142 const std::string
& view_type
,
143 content::WebContents
* owner_web_contents
,
144 const content::WebContents::CreateParams
& create_params
) {
145 auto guest
= CreateGuestInternal(owner_web_contents
, view_type
);
148 content::WebContents::CreateParams
guest_create_params(create_params
);
149 guest_create_params
.guest_delegate
= guest
;
150 auto guest_web_contents
= WebContents::Create(guest_create_params
);
151 guest
->InitWithWebContents(base::DictionaryValue(), guest_web_contents
);
152 return guest_web_contents
;
155 content::WebContents
* GuestViewManager::GetGuestByInstanceID(
156 int owner_process_id
,
157 int element_instance_id
) {
158 int guest_instance_id
= GetGuestInstanceIDForElementID(owner_process_id
,
159 element_instance_id
);
160 if (guest_instance_id
== kInstanceIDNone
)
163 return GetGuestByInstanceID(guest_instance_id
);
166 int GuestViewManager::GetGuestInstanceIDForElementID(int owner_process_id
,
167 int element_instance_id
) {
168 auto iter
= instance_id_map_
.find(
169 ElementInstanceKey(owner_process_id
, element_instance_id
));
170 if (iter
== instance_id_map_
.end())
171 return kInstanceIDNone
;
175 SiteInstance
* GuestViewManager::GetGuestSiteInstance(
176 const GURL
& guest_site
) {
177 for (const auto& guest
: guest_web_contents_by_instance_id_
) {
178 if (guest
.second
->GetSiteInstance()->GetSiteURL() == guest_site
)
179 return guest
.second
->GetSiteInstance();
184 bool GuestViewManager::ForEachGuest(WebContents
* owner_web_contents
,
185 const GuestCallback
& callback
) {
186 for (const auto& guest
: guest_web_contents_by_instance_id_
) {
187 auto guest_view
= GuestViewBase::FromWebContents(guest
.second
);
188 if (guest_view
->owner_web_contents() != owner_web_contents
)
191 if (callback
.Run(guest_view
->web_contents()))
197 WebContents
* GuestViewManager::GetFullPageGuest(
198 WebContents
* embedder_web_contents
) {
199 WebContents
* result
= nullptr;
200 ForEachGuest(embedder_web_contents
,
201 base::Bind(&GuestViewManager::GetFullPageGuestHelper
, &result
));
205 void GuestViewManager::AddGuest(int guest_instance_id
,
206 WebContents
* guest_web_contents
) {
207 CHECK(!ContainsKey(guest_web_contents_by_instance_id_
, guest_instance_id
));
208 CHECK(CanUseGuestInstanceID(guest_instance_id
));
209 guest_web_contents_by_instance_id_
[guest_instance_id
] = guest_web_contents
;
212 void GuestViewManager::RemoveGuest(int guest_instance_id
) {
213 auto it
= guest_web_contents_by_instance_id_
.find(guest_instance_id
);
214 DCHECK(it
!= guest_web_contents_by_instance_id_
.end());
215 guest_web_contents_by_instance_id_
.erase(it
);
217 auto id_iter
= reverse_instance_id_map_
.find(guest_instance_id
);
218 if (id_iter
!= reverse_instance_id_map_
.end()) {
219 const ElementInstanceKey
& instance_id_key
= id_iter
->second
;
220 instance_id_map_
.erase(instance_id_map_
.find(instance_id_key
));
221 reverse_instance_id_map_
.erase(id_iter
);
224 // All the instance IDs that lie within [0, last_instance_id_removed_]
226 // The remaining sparse invalid IDs are kept in |removed_instance_ids_| set.
227 // The following code compacts the set by incrementing
228 // |last_instance_id_removed_|.
229 if (guest_instance_id
== last_instance_id_removed_
+ 1) {
230 ++last_instance_id_removed_
;
232 auto iter
= removed_instance_ids_
.begin();
233 while (iter
!= removed_instance_ids_
.end()) {
234 int instance_id
= *iter
;
235 // The sparse invalid IDs must not lie within
236 // [0, last_instance_id_removed_]
237 DCHECK(instance_id
> last_instance_id_removed_
);
238 if (instance_id
!= last_instance_id_removed_
+ 1)
240 ++last_instance_id_removed_
;
241 removed_instance_ids_
.erase(iter
++);
244 removed_instance_ids_
.insert(guest_instance_id
);
248 GuestViewBase
* GuestViewManager::CreateGuestInternal(
249 content::WebContents
* owner_web_contents
,
250 const std::string
& view_type
) {
251 if (guest_view_registry_
.empty())
252 RegisterGuestViewTypes();
254 auto it
= guest_view_registry_
.find(view_type
);
255 if (it
== guest_view_registry_
.end()) {
260 return it
->second
.Run(owner_web_contents
);
263 void GuestViewManager::RegisterGuestViewTypes() {
264 delegate_
->RegisterAdditionalGuestViewTypes();
267 bool GuestViewManager::IsGuestAvailableToContext(GuestViewBase
* guest
) {
268 return delegate_
->IsGuestAvailableToContext(guest
);
271 void GuestViewManager::DispatchEvent(const std::string
& event_name
,
272 scoped_ptr
<base::DictionaryValue
> args
,
273 GuestViewBase
* guest
,
275 // TODO(fsamuel): GuestViewManager should probably do something more useful
276 // here like log an error if the event could not be dispatched.
277 delegate_
->DispatchEvent(event_name
, args
.Pass(), guest
, instance_id
);
280 content::WebContents
* GuestViewManager::GetGuestByInstanceID(
281 int guest_instance_id
) {
282 auto it
= guest_web_contents_by_instance_id_
.find(guest_instance_id
);
283 if (it
== guest_web_contents_by_instance_id_
.end())
288 bool GuestViewManager::CanEmbedderAccessInstanceIDMaybeKill(
289 int embedder_render_process_id
,
290 int guest_instance_id
) {
291 if (!CanEmbedderAccessInstanceID(embedder_render_process_id
,
292 guest_instance_id
)) {
293 // The embedder process is trying to access a guest it does not own.
294 content::RecordAction(
295 base::UserMetricsAction("BadMessageTerminate_BPGM"));
296 content::RenderProcessHost::FromID(embedder_render_process_id
)
297 ->Shutdown(content::RESULT_CODE_KILLED_BAD_MESSAGE
, false);
303 bool GuestViewManager::CanUseGuestInstanceID(int guest_instance_id
) {
304 if (guest_instance_id
<= last_instance_id_removed_
)
306 return !ContainsKey(removed_instance_ids_
, guest_instance_id
);
310 bool GuestViewManager::GetFullPageGuestHelper(
311 content::WebContents
** result
,
312 content::WebContents
* guest_web_contents
) {
313 auto guest_view
= GuestViewBase::FromWebContents(guest_web_contents
);
314 if (guest_view
&& guest_view
->is_full_page_plugin()) {
315 *result
= guest_web_contents
;
321 bool GuestViewManager::CanEmbedderAccessInstanceID(
322 int embedder_render_process_id
,
323 int guest_instance_id
) {
324 // The embedder is trying to access a guest with a negative or zero
326 if (guest_instance_id
<= kInstanceIDNone
)
329 // The embedder is trying to access an instance ID that has not yet been
330 // allocated by GuestViewManager. This could cause instance ID
331 // collisions in the future, and potentially give one embedder access to a
332 // guest it does not own.
333 if (guest_instance_id
> current_instance_id_
)
336 // We might get some late arriving messages at tear down. Let's let the
337 // embedder tear down in peace.
338 auto it
= guest_web_contents_by_instance_id_
.find(guest_instance_id
);
339 if (it
== guest_web_contents_by_instance_id_
.end())
342 auto guest_view
= GuestViewBase::FromWebContents(it
->second
);
346 return embedder_render_process_id
==
347 guest_view
->owner_web_contents()->GetRenderProcessHost()->GetID();
350 GuestViewManager::ElementInstanceKey::ElementInstanceKey()
351 : embedder_process_id(content::ChildProcessHost::kInvalidUniqueID
),
352 element_instance_id(content::ChildProcessHost::kInvalidUniqueID
) {
355 GuestViewManager::ElementInstanceKey::ElementInstanceKey(
356 int embedder_process_id
,
357 int element_instance_id
)
358 : embedder_process_id(embedder_process_id
),
359 element_instance_id(element_instance_id
) {
362 bool GuestViewManager::ElementInstanceKey::operator<(
363 const GuestViewManager::ElementInstanceKey
& other
) const {
364 if (embedder_process_id
!= other
.embedder_process_id
)
365 return embedder_process_id
< other
.embedder_process_id
;
367 return element_instance_id
< other
.element_instance_id
;
370 bool GuestViewManager::ElementInstanceKey::operator==(
371 const GuestViewManager::ElementInstanceKey
& other
) const {
372 return (embedder_process_id
== other
.embedder_process_id
) &&
373 (element_instance_id
== other
.element_instance_id
);
376 } // namespace guest_view