Stack sampling profiler: add fire-and-forget interface
[chromium-blink-merge.git] / components / guest_view / browser / guest_view_manager.cc
blobfa40a7652b3fa6f9483763abedc6829a68921509
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;
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_]
257 // are invalid.
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_;
263 // Compact.
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)
271 break;
272 ++last_instance_id_removed_;
273 removed_instance_ids_.erase(iter++);
275 } else {
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,
296 view_instance_id,
297 base::Bind(view_it->second.cleanup_function,
298 context_,
299 embedder_process_id,
300 view_instance_id));
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())
313 return;
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)
323 callback.Run();
325 view_destruction_callback_map_.erase(embedder_it);
326 return;
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())
333 return;
334 Callbacks& callbacks_for_view = view_it->second;
335 for (auto& callback : callbacks_for_view)
336 callback.Run();
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()) {
353 NOTREACHED();
354 return nullptr;
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,
387 int instance_id) {
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())
397 return nullptr;
398 return it->second;
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);
411 return false;
413 return true;
416 bool GuestViewManager::CanUseGuestInstanceID(int guest_instance_id) {
417 if (guest_instance_id <= last_instance_id_removed_)
418 return false;
419 return !ContainsKey(removed_instance_ids_, guest_instance_id);
422 // static
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;
429 return true;
431 return false;
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
438 // instance ID.
439 if (guest_instance_id <= kInstanceIDNone)
440 return false;
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_)
447 return false;
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())
453 return true;
455 auto guest_view = GuestViewBase::FromWebContents(it->second);
456 if (!guest_view)
457 return false;
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