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
;
243 // Make |guest_web_contents| show up in the task manager.
244 delegate_
->AttachTaskManagerGuestTag(guest_web_contents
);
247 void GuestViewManager::RemoveGuest(int guest_instance_id
) {
248 auto it
= guest_web_contents_by_instance_id_
.find(guest_instance_id
);
249 DCHECK(it
!= guest_web_contents_by_instance_id_
.end());
250 guest_web_contents_by_instance_id_
.erase(it
);
252 auto id_iter
= reverse_instance_id_map_
.find(guest_instance_id
);
253 if (id_iter
!= reverse_instance_id_map_
.end()) {
254 const ElementInstanceKey
& instance_id_key
= id_iter
->second
;
255 instance_id_map_
.erase(instance_id_map_
.find(instance_id_key
));
256 reverse_instance_id_map_
.erase(id_iter
);
259 // All the instance IDs that lie within [0, last_instance_id_removed_]
261 // The remaining sparse invalid IDs are kept in |removed_instance_ids_| set.
262 // The following code compacts the set by incrementing
263 // |last_instance_id_removed_|.
264 if (guest_instance_id
== last_instance_id_removed_
+ 1) {
265 ++last_instance_id_removed_
;
267 auto iter
= removed_instance_ids_
.begin();
268 while (iter
!= removed_instance_ids_
.end()) {
269 int instance_id
= *iter
;
270 // The sparse invalid IDs must not lie within
271 // [0, last_instance_id_removed_]
272 DCHECK(instance_id
> last_instance_id_removed_
);
273 if (instance_id
!= last_instance_id_removed_
+ 1)
275 ++last_instance_id_removed_
;
276 removed_instance_ids_
.erase(iter
++);
279 removed_instance_ids_
.insert(guest_instance_id
);
283 void GuestViewManager::EmbedderProcessDestroyed(int embedder_process_id
) {
284 embedders_observed_
.erase(embedder_process_id
);
285 CallViewDestructionCallbacks(embedder_process_id
);
288 void GuestViewManager::ViewCreated(int embedder_process_id
,
289 int view_instance_id
,
290 const std::string
& view_type
) {
291 if (guest_view_registry_
.empty())
292 RegisterGuestViewTypes();
293 auto view_it
= guest_view_registry_
.find(view_type
);
294 CHECK(view_it
!= guest_view_registry_
.end())
295 << "Invalid GuestView created of type \"" << view_type
<< "\"";
297 // Register the cleanup callback for when this view is destroyed.
298 RegisterViewDestructionCallback(embedder_process_id
,
300 base::Bind(view_it
->second
.cleanup_function
,
306 void GuestViewManager::ViewGarbageCollected(int embedder_process_id
,
307 int view_instance_id
) {
308 CallViewDestructionCallbacks(embedder_process_id
, view_instance_id
);
311 void GuestViewManager::CallViewDestructionCallbacks(int embedder_process_id
,
312 int view_instance_id
) {
313 // Find the callbacks for the embedder with ID |embedder_process_id|.
314 auto embedder_it
= view_destruction_callback_map_
.find(embedder_process_id
);
315 if (embedder_it
== view_destruction_callback_map_
.end())
317 CallbacksForEachViewID
& callbacks_for_embedder
= embedder_it
->second
;
319 // If |view_instance_id| is guest_view::kInstanceIDNone, then all callbacks
320 // for this embedder should be called.
321 if (view_instance_id
== guest_view::kInstanceIDNone
) {
322 // Call all callbacks for the embedder with ID |embedder_process_id|.
323 for (auto& view_pair
: callbacks_for_embedder
) {
324 Callbacks
& callbacks_for_view
= view_pair
.second
;
325 for (auto& callback
: callbacks_for_view
)
328 view_destruction_callback_map_
.erase(embedder_it
);
332 // Otherwise, call the callbacks only for the specific view with ID
333 // |view_instance_id|.
334 auto view_it
= callbacks_for_embedder
.find(view_instance_id
);
335 if (view_it
== callbacks_for_embedder
.end())
337 Callbacks
& callbacks_for_view
= view_it
->second
;
338 for (auto& callback
: callbacks_for_view
)
340 callbacks_for_embedder
.erase(view_it
);
343 void GuestViewManager::CallViewDestructionCallbacks(int embedder_process_id
) {
344 CallViewDestructionCallbacks(embedder_process_id
,
345 guest_view::kInstanceIDNone
);
348 GuestViewBase
* GuestViewManager::CreateGuestInternal(
349 content::WebContents
* owner_web_contents
,
350 const std::string
& view_type
) {
351 if (guest_view_registry_
.empty())
352 RegisterGuestViewTypes();
354 auto it
= guest_view_registry_
.find(view_type
);
355 if (it
== guest_view_registry_
.end()) {
360 return it
->second
.create_function
.Run(owner_web_contents
);
363 void GuestViewManager::RegisterGuestViewTypes() {
364 delegate_
->RegisterAdditionalGuestViewTypes();
367 void GuestViewManager::RegisterViewDestructionCallback(
368 int embedder_process_id
,
369 int view_instance_id
,
370 const base::Closure
& callback
) {
371 // When an embedder is registered for the first time, create an observer to
372 // watch for its destruction.
373 if (!embedders_observed_
.count(embedder_process_id
)) {
374 embedders_observed_
.insert(embedder_process_id
);
375 /*new EmbedderRenderProcessHostObserver(weak_ptr_factory_.GetWeakPtr(),
376 embedder_process_id);*/
379 view_destruction_callback_map_
[embedder_process_id
][view_instance_id
]
380 .push_back(callback
);
383 bool GuestViewManager::IsGuestAvailableToContext(GuestViewBase
* guest
) {
384 return delegate_
->IsGuestAvailableToContext(guest
);
387 void GuestViewManager::DispatchEvent(const std::string
& event_name
,
388 scoped_ptr
<base::DictionaryValue
> args
,
389 GuestViewBase
* guest
,
391 // TODO(fsamuel): GuestViewManager should probably do something more useful
392 // here like log an error if the event could not be dispatched.
393 delegate_
->DispatchEvent(event_name
, args
.Pass(), guest
, instance_id
);
396 content::WebContents
* GuestViewManager::GetGuestByInstanceID(
397 int guest_instance_id
) {
398 auto it
= guest_web_contents_by_instance_id_
.find(guest_instance_id
);
399 if (it
== guest_web_contents_by_instance_id_
.end())
404 bool GuestViewManager::CanEmbedderAccessInstanceIDMaybeKill(
405 int embedder_render_process_id
,
406 int guest_instance_id
) {
407 if (!CanEmbedderAccessInstanceID(embedder_render_process_id
,
408 guest_instance_id
)) {
409 // The embedder process is trying to access a guest it does not own.
410 content::RecordAction(
411 base::UserMetricsAction("BadMessageTerminate_BPGM"));
412 content::RenderProcessHost::FromID(embedder_render_process_id
)
413 ->Shutdown(content::RESULT_CODE_KILLED_BAD_MESSAGE
, false);
419 bool GuestViewManager::CanUseGuestInstanceID(int guest_instance_id
) {
420 if (guest_instance_id
<= last_instance_id_removed_
)
422 return !ContainsKey(removed_instance_ids_
, guest_instance_id
);
426 bool GuestViewManager::GetFullPageGuestHelper(
427 content::WebContents
** result
,
428 content::WebContents
* guest_web_contents
) {
429 auto guest_view
= GuestViewBase::FromWebContents(guest_web_contents
);
430 if (guest_view
&& guest_view
->is_full_page_plugin()) {
431 *result
= guest_web_contents
;
437 bool GuestViewManager::CanEmbedderAccessInstanceID(
438 int embedder_render_process_id
,
439 int guest_instance_id
) {
440 // The embedder is trying to access a guest with a negative or zero
442 if (guest_instance_id
<= kInstanceIDNone
)
445 // The embedder is trying to access an instance ID that has not yet been
446 // allocated by GuestViewManager. This could cause instance ID
447 // collisions in the future, and potentially give one embedder access to a
448 // guest it does not own.
449 if (guest_instance_id
> current_instance_id_
)
452 // We might get some late arriving messages at tear down. Let's let the
453 // embedder tear down in peace.
454 auto it
= guest_web_contents_by_instance_id_
.find(guest_instance_id
);
455 if (it
== guest_web_contents_by_instance_id_
.end())
458 auto guest_view
= GuestViewBase::FromWebContents(it
->second
);
462 return embedder_render_process_id
==
463 guest_view
->owner_web_contents()->GetRenderProcessHost()->GetID();
466 GuestViewManager::ElementInstanceKey::ElementInstanceKey()
467 : embedder_process_id(content::ChildProcessHost::kInvalidUniqueID
),
468 element_instance_id(content::ChildProcessHost::kInvalidUniqueID
) {
471 GuestViewManager::ElementInstanceKey::ElementInstanceKey(
472 int embedder_process_id
,
473 int element_instance_id
)
474 : embedder_process_id(embedder_process_id
),
475 element_instance_id(element_instance_id
) {
478 bool GuestViewManager::ElementInstanceKey::operator<(
479 const GuestViewManager::ElementInstanceKey
& other
) const {
480 if (embedder_process_id
!= other
.embedder_process_id
)
481 return embedder_process_id
< other
.embedder_process_id
;
483 return element_instance_id
< other
.element_instance_id
;
486 bool GuestViewManager::ElementInstanceKey::operator==(
487 const GuestViewManager::ElementInstanceKey
& other
) const {
488 return (embedder_process_id
== other
.embedder_process_id
) &&
489 (element_instance_id
== other
.element_instance_id
);
492 GuestViewManager::GuestViewData::GuestViewData(
493 const GuestViewCreateFunction
& create_function
,
494 const GuestViewCleanUpFunction
& cleanup_function
)
495 : create_function(create_function
), cleanup_function(cleanup_function
) {}
497 GuestViewManager::GuestViewData::~GuestViewData() {}
499 } // namespace guest_view