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 void GuestViewManager::EmbedderWillBeDestroyed(int embedder_process_id
) {
249 // Find and call any callbacks associated with the embedder that is being
251 auto embedder_it
= view_destruction_callback_map_
.find(embedder_process_id
);
252 if (embedder_it
== view_destruction_callback_map_
.end())
254 CallbacksForEachViewID
& callbacks_for_embedder
= embedder_it
->second
;
255 for (auto& view_pair
: callbacks_for_embedder
) {
256 Callbacks
& callbacks_for_view
= view_pair
.second
;
257 for (auto& callback
: callbacks_for_view
) {
261 view_destruction_callback_map_
.erase(embedder_it
);
264 void GuestViewManager::ViewCreated(int embedder_process_id
,
265 int view_instance_id
,
266 const std::string
& view_type
) {
267 if (guest_view_registry_
.empty())
268 RegisterGuestViewTypes();
269 auto view_it
= guest_view_registry_
.find(view_type
);
270 CHECK(view_it
!= guest_view_registry_
.end())
271 << "Invalid GuestView created of type \"" << view_type
<< "\"";
273 // Register the cleanup callback for when this view is destroyed.
274 RegisterViewDestructionCallback(embedder_process_id
,
276 base::Bind(view_it
->second
.cleanup_function
,
281 void GuestViewManager::ViewGarbageCollected(int embedder_process_id
,
282 int view_instance_id
) {
283 // Find and call any callbacks associated with the view that has been garbage
285 auto embedder_it
= view_destruction_callback_map_
.find(embedder_process_id
);
286 if (embedder_it
== view_destruction_callback_map_
.end())
288 CallbacksForEachViewID
& callbacks_for_embedder
= embedder_it
->second
;
289 auto view_it
= callbacks_for_embedder
.find(view_instance_id
);
290 if (view_it
== callbacks_for_embedder
.end())
292 Callbacks
& callbacks_for_view
= view_it
->second
;
293 for (auto& callback
: callbacks_for_view
)
295 callbacks_for_embedder
.erase(view_it
);
298 GuestViewBase
* GuestViewManager::CreateGuestInternal(
299 content::WebContents
* owner_web_contents
,
300 const std::string
& view_type
) {
301 if (guest_view_registry_
.empty())
302 RegisterGuestViewTypes();
304 auto it
= guest_view_registry_
.find(view_type
);
305 if (it
== guest_view_registry_
.end()) {
310 return it
->second
.create_function
.Run(owner_web_contents
);
313 void GuestViewManager::RegisterGuestViewTypes() {
314 delegate_
->RegisterAdditionalGuestViewTypes();
317 void GuestViewManager::RegisterViewDestructionCallback(
318 int embedder_process_id
,
319 int view_instance_id
,
320 const base::Closure
& callback
) {
321 view_destruction_callback_map_
[embedder_process_id
][view_instance_id
]
322 .push_back(callback
);
325 bool GuestViewManager::IsGuestAvailableToContext(GuestViewBase
* guest
) {
326 return delegate_
->IsGuestAvailableToContext(guest
);
329 void GuestViewManager::DispatchEvent(const std::string
& event_name
,
330 scoped_ptr
<base::DictionaryValue
> args
,
331 GuestViewBase
* guest
,
333 // TODO(fsamuel): GuestViewManager should probably do something more useful
334 // here like log an error if the event could not be dispatched.
335 delegate_
->DispatchEvent(event_name
, args
.Pass(), guest
, instance_id
);
338 content::WebContents
* GuestViewManager::GetGuestByInstanceID(
339 int guest_instance_id
) {
340 auto it
= guest_web_contents_by_instance_id_
.find(guest_instance_id
);
341 if (it
== guest_web_contents_by_instance_id_
.end())
346 bool GuestViewManager::CanEmbedderAccessInstanceIDMaybeKill(
347 int embedder_render_process_id
,
348 int guest_instance_id
) {
349 if (!CanEmbedderAccessInstanceID(embedder_render_process_id
,
350 guest_instance_id
)) {
351 // The embedder process is trying to access a guest it does not own.
352 content::RecordAction(
353 base::UserMetricsAction("BadMessageTerminate_BPGM"));
354 content::RenderProcessHost::FromID(embedder_render_process_id
)
355 ->Shutdown(content::RESULT_CODE_KILLED_BAD_MESSAGE
, false);
361 bool GuestViewManager::CanUseGuestInstanceID(int guest_instance_id
) {
362 if (guest_instance_id
<= last_instance_id_removed_
)
364 return !ContainsKey(removed_instance_ids_
, guest_instance_id
);
368 bool GuestViewManager::GetFullPageGuestHelper(
369 content::WebContents
** result
,
370 content::WebContents
* guest_web_contents
) {
371 auto guest_view
= GuestViewBase::FromWebContents(guest_web_contents
);
372 if (guest_view
&& guest_view
->is_full_page_plugin()) {
373 *result
= guest_web_contents
;
379 bool GuestViewManager::CanEmbedderAccessInstanceID(
380 int embedder_render_process_id
,
381 int guest_instance_id
) {
382 // The embedder is trying to access a guest with a negative or zero
384 if (guest_instance_id
<= kInstanceIDNone
)
387 // The embedder is trying to access an instance ID that has not yet been
388 // allocated by GuestViewManager. This could cause instance ID
389 // collisions in the future, and potentially give one embedder access to a
390 // guest it does not own.
391 if (guest_instance_id
> current_instance_id_
)
394 // We might get some late arriving messages at tear down. Let's let the
395 // embedder tear down in peace.
396 auto it
= guest_web_contents_by_instance_id_
.find(guest_instance_id
);
397 if (it
== guest_web_contents_by_instance_id_
.end())
400 auto guest_view
= GuestViewBase::FromWebContents(it
->second
);
404 return embedder_render_process_id
==
405 guest_view
->owner_web_contents()->GetRenderProcessHost()->GetID();
408 GuestViewManager::ElementInstanceKey::ElementInstanceKey()
409 : embedder_process_id(content::ChildProcessHost::kInvalidUniqueID
),
410 element_instance_id(content::ChildProcessHost::kInvalidUniqueID
) {
413 GuestViewManager::ElementInstanceKey::ElementInstanceKey(
414 int embedder_process_id
,
415 int element_instance_id
)
416 : embedder_process_id(embedder_process_id
),
417 element_instance_id(element_instance_id
) {
420 bool GuestViewManager::ElementInstanceKey::operator<(
421 const GuestViewManager::ElementInstanceKey
& other
) const {
422 if (embedder_process_id
!= other
.embedder_process_id
)
423 return embedder_process_id
< other
.embedder_process_id
;
425 return element_instance_id
< other
.element_instance_id
;
428 bool GuestViewManager::ElementInstanceKey::operator==(
429 const GuestViewManager::ElementInstanceKey
& other
) const {
430 return (embedder_process_id
== other
.embedder_process_id
) &&
431 (element_instance_id
== other
.element_instance_id
);
434 GuestViewManager::GuestViewData::GuestViewData(
435 const GuestViewCreateFunction
& create_function
,
436 const GuestViewCleanUpFunction
& cleanup_function
)
437 : create_function(create_function
), cleanup_function(cleanup_function
) {}
439 GuestViewManager::GuestViewData::~GuestViewData() {}
441 } // namespace guest_view