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::RenderProcessHost
;
26 using content::SiteInstance
;
27 using content::WebContents
;
29 namespace guest_view
{
31 // This observer observes the RenderProcessHosts of GuestView embedders, and
32 // notifies the GuestViewManager when they are destroyed.
33 class GuestViewManager::EmbedderRenderProcessHostObserver
34 : public content::RenderProcessHostObserver
{
36 EmbedderRenderProcessHostObserver(
37 base::WeakPtr
<GuestViewManager
> guest_view_manager
,
38 int embedder_process_id
)
39 : guest_view_manager_(guest_view_manager
), id_(embedder_process_id
) {
40 RenderProcessHost
* rph
= RenderProcessHost::FromID(id_
);
41 rph
->AddObserver(this);
44 ~EmbedderRenderProcessHostObserver() override
{
45 RenderProcessHost
* rph
= RenderProcessHost::FromID(id_
);
47 rph
->RemoveObserver(this);
50 void RenderProcessHostDestroyed(RenderProcessHost
* host
) override
{
51 if (guest_view_manager_
.get())
52 guest_view_manager_
->EmbedderProcessDestroyed(id_
);
57 base::WeakPtr
<GuestViewManager
> guest_view_manager_
;
62 GuestViewManagerFactory
* GuestViewManager::factory_
= nullptr;
64 GuestViewManager::GuestViewManager(
65 content::BrowserContext
* context
,
66 scoped_ptr
<GuestViewManagerDelegate
> delegate
)
67 : current_instance_id_(0),
68 last_instance_id_removed_(0),
70 delegate_(delegate
.Pass()),
71 weak_ptr_factory_(this) {
74 GuestViewManager::~GuestViewManager() {}
77 GuestViewManager
* GuestViewManager::CreateWithDelegate(
78 BrowserContext
* context
,
79 scoped_ptr
<GuestViewManagerDelegate
> delegate
) {
80 GuestViewManager
* guest_manager
= FromBrowserContext(context
);
84 factory_
->CreateGuestViewManager(context
, delegate
.Pass());
86 guest_manager
= new GuestViewManager(context
, delegate
.Pass());
88 context
->SetUserData(kGuestViewManagerKeyName
, guest_manager
);
94 GuestViewManager
* GuestViewManager::FromBrowserContext(
95 BrowserContext
* context
) {
96 return static_cast<GuestViewManager
*>(context
->GetUserData(
97 kGuestViewManagerKeyName
));
100 content::WebContents
* GuestViewManager::GetGuestByInstanceIDSafely(
101 int guest_instance_id
,
102 int embedder_render_process_id
) {
103 if (!CanEmbedderAccessInstanceIDMaybeKill(embedder_render_process_id
,
104 guest_instance_id
)) {
107 return GetGuestByInstanceID(guest_instance_id
);
110 void GuestViewManager::AttachGuest(int embedder_process_id
,
111 int element_instance_id
,
112 int guest_instance_id
,
113 const base::DictionaryValue
& attach_params
) {
114 auto guest_view
= GuestViewBase::From(embedder_process_id
, guest_instance_id
);
118 ElementInstanceKey
key(embedder_process_id
, element_instance_id
);
119 auto it
= instance_id_map_
.find(key
);
120 // If there is an existing guest attached to the element, then destroy the
122 if (it
!= instance_id_map_
.end()) {
123 int old_guest_instance_id
= it
->second
;
124 if (old_guest_instance_id
== guest_instance_id
)
127 auto old_guest_view
= GuestViewBase::From(embedder_process_id
,
128 old_guest_instance_id
);
129 old_guest_view
->Destroy();
131 instance_id_map_
[key
] = guest_instance_id
;
132 reverse_instance_id_map_
[guest_instance_id
] = key
;
133 guest_view
->SetAttachParams(attach_params
);
136 void GuestViewManager::DetachGuest(GuestViewBase
* guest
) {
137 if (!guest
->attached())
140 auto reverse_it
= reverse_instance_id_map_
.find(guest
->guest_instance_id());
141 if (reverse_it
== reverse_instance_id_map_
.end())
144 const ElementInstanceKey
& key
= reverse_it
->second
;
146 auto it
= instance_id_map_
.find(key
);
147 DCHECK(it
!= instance_id_map_
.end());
149 reverse_instance_id_map_
.erase(reverse_it
);
150 instance_id_map_
.erase(it
);
153 bool GuestViewManager::IsOwnedByExtension(GuestViewBase
* guest
) {
154 return delegate_
->IsOwnedByExtension(guest
);
157 int GuestViewManager::GetNextInstanceID() {
158 return ++current_instance_id_
;
161 void GuestViewManager::CreateGuest(const std::string
& view_type
,
162 content::WebContents
* owner_web_contents
,
163 const base::DictionaryValue
& create_params
,
164 const WebContentsCreatedCallback
& callback
) {
165 GuestViewBase
* guest
= CreateGuestInternal(owner_web_contents
, view_type
);
167 callback
.Run(nullptr);
170 guest
->Init(create_params
, callback
);
173 content::WebContents
* GuestViewManager::CreateGuestWithWebContentsParams(
174 const std::string
& view_type
,
175 content::WebContents
* owner_web_contents
,
176 const content::WebContents::CreateParams
& create_params
) {
177 auto guest
= CreateGuestInternal(owner_web_contents
, view_type
);
180 content::WebContents::CreateParams
guest_create_params(create_params
);
181 guest_create_params
.guest_delegate
= guest
;
182 auto guest_web_contents
= WebContents::Create(guest_create_params
);
183 guest
->InitWithWebContents(base::DictionaryValue(), guest_web_contents
);
184 return guest_web_contents
;
187 content::WebContents
* GuestViewManager::GetGuestByInstanceID(
188 int owner_process_id
,
189 int element_instance_id
) {
190 int guest_instance_id
= GetGuestInstanceIDForElementID(owner_process_id
,
191 element_instance_id
);
192 if (guest_instance_id
== kInstanceIDNone
)
195 return GetGuestByInstanceID(guest_instance_id
);
198 int GuestViewManager::GetGuestInstanceIDForElementID(int owner_process_id
,
199 int element_instance_id
) {
200 auto iter
= instance_id_map_
.find(
201 ElementInstanceKey(owner_process_id
, element_instance_id
));
202 if (iter
== instance_id_map_
.end())
203 return kInstanceIDNone
;
207 SiteInstance
* GuestViewManager::GetGuestSiteInstance(
208 const GURL
& guest_site
) {
209 for (const auto& guest
: guest_web_contents_by_instance_id_
) {
210 if (guest
.second
->GetSiteInstance()->GetSiteURL() == guest_site
)
211 return guest
.second
->GetSiteInstance();
216 bool GuestViewManager::ForEachGuest(WebContents
* owner_web_contents
,
217 const GuestCallback
& callback
) {
218 for (const auto& guest
: guest_web_contents_by_instance_id_
) {
219 auto guest_view
= GuestViewBase::FromWebContents(guest
.second
);
220 if (guest_view
->owner_web_contents() != owner_web_contents
)
223 if (callback
.Run(guest_view
->web_contents()))
229 WebContents
* GuestViewManager::GetFullPageGuest(
230 WebContents
* embedder_web_contents
) {
231 WebContents
* result
= nullptr;
232 ForEachGuest(embedder_web_contents
,
233 base::Bind(&GuestViewManager::GetFullPageGuestHelper
, &result
));
237 void GuestViewManager::AddGuest(int guest_instance_id
,
238 WebContents
* guest_web_contents
) {
239 CHECK(!ContainsKey(guest_web_contents_by_instance_id_
, guest_instance_id
));
240 CHECK(CanUseGuestInstanceID(guest_instance_id
));
241 guest_web_contents_by_instance_id_
[guest_instance_id
] = guest_web_contents
;
244 void GuestViewManager::RemoveGuest(int guest_instance_id
) {
245 auto it
= guest_web_contents_by_instance_id_
.find(guest_instance_id
);
246 DCHECK(it
!= guest_web_contents_by_instance_id_
.end());
247 guest_web_contents_by_instance_id_
.erase(it
);
249 auto id_iter
= reverse_instance_id_map_
.find(guest_instance_id
);
250 if (id_iter
!= reverse_instance_id_map_
.end()) {
251 const ElementInstanceKey
& instance_id_key
= id_iter
->second
;
252 instance_id_map_
.erase(instance_id_map_
.find(instance_id_key
));
253 reverse_instance_id_map_
.erase(id_iter
);
256 // All the instance IDs that lie within [0, last_instance_id_removed_]
258 // The remaining sparse invalid IDs are kept in |removed_instance_ids_| set.
259 // The following code compacts the set by incrementing
260 // |last_instance_id_removed_|.
261 if (guest_instance_id
== last_instance_id_removed_
+ 1) {
262 ++last_instance_id_removed_
;
264 auto iter
= removed_instance_ids_
.begin();
265 while (iter
!= removed_instance_ids_
.end()) {
266 int instance_id
= *iter
;
267 // The sparse invalid IDs must not lie within
268 // [0, last_instance_id_removed_]
269 DCHECK(instance_id
> last_instance_id_removed_
);
270 if (instance_id
!= last_instance_id_removed_
+ 1)
272 ++last_instance_id_removed_
;
273 removed_instance_ids_
.erase(iter
++);
276 removed_instance_ids_
.insert(guest_instance_id
);
280 void GuestViewManager::EmbedderProcessDestroyed(int embedder_process_id
) {
281 embedders_observed_
.erase(embedder_process_id
);
282 CallViewDestructionCallbacks(embedder_process_id
);
285 void GuestViewManager::ViewCreated(int embedder_process_id
,
286 int view_instance_id
,
287 const std::string
& view_type
) {
288 if (guest_view_registry_
.empty())
289 RegisterGuestViewTypes();
290 auto view_it
= guest_view_registry_
.find(view_type
);
291 CHECK(view_it
!= guest_view_registry_
.end())
292 << "Invalid GuestView created of type \"" << view_type
<< "\"";
294 // Register the cleanup callback for when this view is destroyed.
295 RegisterViewDestructionCallback(embedder_process_id
,
297 base::Bind(view_it
->second
.cleanup_function
,
303 void GuestViewManager::ViewGarbageCollected(int embedder_process_id
,
304 int view_instance_id
) {
305 CallViewDestructionCallbacks(embedder_process_id
, view_instance_id
);
308 void GuestViewManager::CallViewDestructionCallbacks(int embedder_process_id
,
309 int view_instance_id
) {
310 // Find the callbacks for the embedder with ID |embedder_process_id|.
311 auto embedder_it
= view_destruction_callback_map_
.find(embedder_process_id
);
312 if (embedder_it
== view_destruction_callback_map_
.end())
314 CallbacksForEachViewID
& callbacks_for_embedder
= embedder_it
->second
;
316 // If |view_instance_id| is guest_view::kInstanceIDNone, then all callbacks
317 // for this embedder should be called.
318 if (view_instance_id
== guest_view::kInstanceIDNone
) {
319 // Call all callbacks for the embedder with ID |embedder_process_id|.
320 for (auto& view_pair
: callbacks_for_embedder
) {
321 Callbacks
& callbacks_for_view
= view_pair
.second
;
322 for (auto& callback
: callbacks_for_view
)
325 view_destruction_callback_map_
.erase(embedder_it
);
329 // Otherwise, call the callbacks only for the specific view with ID
330 // |view_instance_id|.
331 auto view_it
= callbacks_for_embedder
.find(view_instance_id
);
332 if (view_it
== callbacks_for_embedder
.end())
334 Callbacks
& callbacks_for_view
= view_it
->second
;
335 for (auto& callback
: callbacks_for_view
)
337 callbacks_for_embedder
.erase(view_it
);
340 void GuestViewManager::CallViewDestructionCallbacks(int embedder_process_id
) {
341 CallViewDestructionCallbacks(embedder_process_id
,
342 guest_view::kInstanceIDNone
);
345 GuestViewBase
* GuestViewManager::CreateGuestInternal(
346 content::WebContents
* owner_web_contents
,
347 const std::string
& view_type
) {
348 if (guest_view_registry_
.empty())
349 RegisterGuestViewTypes();
351 auto it
= guest_view_registry_
.find(view_type
);
352 if (it
== guest_view_registry_
.end()) {
357 return it
->second
.create_function
.Run(owner_web_contents
);
360 void GuestViewManager::RegisterGuestViewTypes() {
361 delegate_
->RegisterAdditionalGuestViewTypes();
364 void GuestViewManager::RegisterViewDestructionCallback(
365 int embedder_process_id
,
366 int view_instance_id
,
367 const base::Closure
& callback
) {
368 // When an embedder is registered for the first time, create an observer to
369 // watch for its destruction.
370 if (!embedders_observed_
.count(embedder_process_id
)) {
371 embedders_observed_
.insert(embedder_process_id
);
372 /*new EmbedderRenderProcessHostObserver(weak_ptr_factory_.GetWeakPtr(),
373 embedder_process_id);*/
376 view_destruction_callback_map_
[embedder_process_id
][view_instance_id
]
377 .push_back(callback
);
380 bool GuestViewManager::IsGuestAvailableToContext(GuestViewBase
* guest
) {
381 return delegate_
->IsGuestAvailableToContext(guest
);
384 void GuestViewManager::DispatchEvent(const std::string
& event_name
,
385 scoped_ptr
<base::DictionaryValue
> args
,
386 GuestViewBase
* guest
,
388 // TODO(fsamuel): GuestViewManager should probably do something more useful
389 // here like log an error if the event could not be dispatched.
390 delegate_
->DispatchEvent(event_name
, args
.Pass(), guest
, instance_id
);
393 content::WebContents
* GuestViewManager::GetGuestByInstanceID(
394 int guest_instance_id
) {
395 auto it
= guest_web_contents_by_instance_id_
.find(guest_instance_id
);
396 if (it
== guest_web_contents_by_instance_id_
.end())
401 bool GuestViewManager::CanEmbedderAccessInstanceIDMaybeKill(
402 int embedder_render_process_id
,
403 int guest_instance_id
) {
404 if (!CanEmbedderAccessInstanceID(embedder_render_process_id
,
405 guest_instance_id
)) {
406 // The embedder process is trying to access a guest it does not own.
407 content::RecordAction(
408 base::UserMetricsAction("BadMessageTerminate_BPGM"));
409 content::RenderProcessHost::FromID(embedder_render_process_id
)
410 ->Shutdown(content::RESULT_CODE_KILLED_BAD_MESSAGE
, false);
416 bool GuestViewManager::CanUseGuestInstanceID(int guest_instance_id
) {
417 if (guest_instance_id
<= last_instance_id_removed_
)
419 return !ContainsKey(removed_instance_ids_
, guest_instance_id
);
423 bool GuestViewManager::GetFullPageGuestHelper(
424 content::WebContents
** result
,
425 content::WebContents
* guest_web_contents
) {
426 auto guest_view
= GuestViewBase::FromWebContents(guest_web_contents
);
427 if (guest_view
&& guest_view
->is_full_page_plugin()) {
428 *result
= guest_web_contents
;
434 bool GuestViewManager::CanEmbedderAccessInstanceID(
435 int embedder_render_process_id
,
436 int guest_instance_id
) {
437 // The embedder is trying to access a guest with a negative or zero
439 if (guest_instance_id
<= kInstanceIDNone
)
442 // The embedder is trying to access an instance ID that has not yet been
443 // allocated by GuestViewManager. This could cause instance ID
444 // collisions in the future, and potentially give one embedder access to a
445 // guest it does not own.
446 if (guest_instance_id
> current_instance_id_
)
449 // We might get some late arriving messages at tear down. Let's let the
450 // embedder tear down in peace.
451 auto it
= guest_web_contents_by_instance_id_
.find(guest_instance_id
);
452 if (it
== guest_web_contents_by_instance_id_
.end())
455 auto guest_view
= GuestViewBase::FromWebContents(it
->second
);
459 return embedder_render_process_id
==
460 guest_view
->owner_web_contents()->GetRenderProcessHost()->GetID();
463 GuestViewManager::ElementInstanceKey::ElementInstanceKey()
464 : embedder_process_id(content::ChildProcessHost::kInvalidUniqueID
),
465 element_instance_id(content::ChildProcessHost::kInvalidUniqueID
) {
468 GuestViewManager::ElementInstanceKey::ElementInstanceKey(
469 int embedder_process_id
,
470 int element_instance_id
)
471 : embedder_process_id(embedder_process_id
),
472 element_instance_id(element_instance_id
) {
475 bool GuestViewManager::ElementInstanceKey::operator<(
476 const GuestViewManager::ElementInstanceKey
& other
) const {
477 if (embedder_process_id
!= other
.embedder_process_id
)
478 return embedder_process_id
< other
.embedder_process_id
;
480 return element_instance_id
< other
.element_instance_id
;
483 bool GuestViewManager::ElementInstanceKey::operator==(
484 const GuestViewManager::ElementInstanceKey
& other
) const {
485 return (embedder_process_id
== other
.embedder_process_id
) &&
486 (element_instance_id
== other
.element_instance_id
);
489 GuestViewManager::GuestViewData::GuestViewData(
490 const GuestViewCreateFunction
& create_function
,
491 const GuestViewCleanUpFunction
& cleanup_function
)
492 : create_function(create_function
), cleanup_function(cleanup_function
) {}
494 GuestViewManager::GuestViewData::~GuestViewData() {}
496 } // namespace guest_view