Linux: Depend on liberation-fonts package for RPMs.
[chromium-blink-merge.git] / components / guest_view / browser / guest_view_manager.cc
blob8fdc082ab66ea17b05184b0aacbc292a4ee57710
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"
22 #include "url/gurl.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 {
35 public:
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_);
46 if (rph)
47 rph->RemoveObserver(this);
50 void RenderProcessHostDestroyed(RenderProcessHost* host) override {
51 if (guest_view_manager_.get())
52 guest_view_manager_->EmbedderProcessDestroyed(id_);
53 delete this;
56 private:
57 base::WeakPtr<GuestViewManager> guest_view_manager_;
58 int id_;
61 // static
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),
69 context_(context),
70 delegate_(delegate.Pass()),
71 weak_ptr_factory_(this) {
74 GuestViewManager::~GuestViewManager() {}
76 // static
77 GuestViewManager* GuestViewManager::CreateWithDelegate(
78 BrowserContext* context,
79 scoped_ptr<GuestViewManagerDelegate> delegate) {
80 GuestViewManager* guest_manager = FromBrowserContext(context);
81 if (!guest_manager) {
82 if (factory_) {
83 guest_manager =
84 factory_->CreateGuestViewManager(context, delegate.Pass());
85 } else {
86 guest_manager = new GuestViewManager(context, delegate.Pass());
88 context->SetUserData(kGuestViewManagerKeyName, guest_manager);
90 return guest_manager;
93 // static
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)) {
105 return nullptr;
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);
115 if (!guest_view)
116 return;
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
121 // existing guest.
122 if (it != instance_id_map_.end()) {
123 int old_guest_instance_id = it->second;
124 if (old_guest_instance_id == guest_instance_id)
125 return;
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())
138 return;
140 auto reverse_it = reverse_instance_id_map_.find(guest->guest_instance_id());
141 if (reverse_it == reverse_instance_id_map_.end())
142 return;
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);
166 if (!guest) {
167 callback.Run(nullptr);
168 return;
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);
178 if (!guest)
179 return nullptr;
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)
193 return nullptr;
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;
204 return iter->second;
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();
213 return nullptr;
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)
221 continue;
223 if (callback.Run(guest_view->web_contents()))
224 return true;
226 return false;
229 WebContents* GuestViewManager::GetFullPageGuest(
230 WebContents* embedder_web_contents) {
231 WebContents* result = nullptr;
232 ForEachGuest(embedder_web_contents,
233 base::Bind(&GuestViewManager::GetFullPageGuestHelper, &result));
234 return 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_]
260 // are invalid.
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_;
266 // Compact.
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)
274 break;
275 ++last_instance_id_removed_;
276 removed_instance_ids_.erase(iter++);
278 } else {
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,
299 view_instance_id,
300 base::Bind(view_it->second.cleanup_function,
301 context_,
302 embedder_process_id,
303 view_instance_id));
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())
316 return;
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)
326 callback.Run();
328 view_destruction_callback_map_.erase(embedder_it);
329 return;
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())
336 return;
337 Callbacks& callbacks_for_view = view_it->second;
338 for (auto& callback : callbacks_for_view)
339 callback.Run();
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()) {
356 NOTREACHED();
357 return nullptr;
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,
390 int instance_id) {
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())
400 return nullptr;
401 return it->second;
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);
414 return false;
416 return true;
419 bool GuestViewManager::CanUseGuestInstanceID(int guest_instance_id) {
420 if (guest_instance_id <= last_instance_id_removed_)
421 return false;
422 return !ContainsKey(removed_instance_ids_, guest_instance_id);
425 // static
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;
432 return true;
434 return false;
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
441 // instance ID.
442 if (guest_instance_id <= kInstanceIDNone)
443 return false;
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_)
450 return false;
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())
456 return true;
458 auto guest_view = GuestViewBase::FromWebContents(it->second);
459 if (!guest_view)
460 return false;
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