Don't preload rarely seen large images
[chromium-blink-merge.git] / components / guest_view / browser / guest_view_manager.cc
blob07da1adf11f022be2079599f915fbe9e41e7c06c
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::SiteInstance;
26 using content::WebContents;
28 namespace guest_view {
30 // static
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),
38 context_(context),
39 delegate_(delegate.Pass()) {
42 GuestViewManager::~GuestViewManager() {}
44 // static
45 GuestViewManager* GuestViewManager::CreateWithDelegate(
46 BrowserContext* context,
47 scoped_ptr<GuestViewManagerDelegate> delegate) {
48 GuestViewManager* guest_manager = FromBrowserContext(context);
49 if (!guest_manager) {
50 if (factory_) {
51 guest_manager =
52 factory_->CreateGuestViewManager(context, delegate.Pass());
53 } else {
54 guest_manager = new GuestViewManager(context, delegate.Pass());
56 context->SetUserData(kGuestViewManagerKeyName, guest_manager);
58 return guest_manager;
61 // static
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,
72 guest_instance_id)) {
73 return nullptr;
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);
83 if (!guest_view)
84 return;
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
89 // existing guest.
90 if (it != instance_id_map_.end()) {
91 int old_guest_instance_id = it->second;
92 if (old_guest_instance_id == guest_instance_id)
93 return;
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())
106 return;
108 auto reverse_it = reverse_instance_id_map_.find(guest->guest_instance_id());
109 if (reverse_it == reverse_instance_id_map_.end())
110 return;
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);
134 if (!guest) {
135 callback.Run(nullptr);
136 return;
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);
146 if (!guest)
147 return nullptr;
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)
161 return nullptr;
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;
172 return iter->second;
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();
181 return nullptr;
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)
189 continue;
191 if (callback.Run(guest_view->web_contents()))
192 return true;
194 return false;
197 WebContents* GuestViewManager::GetFullPageGuest(
198 WebContents* embedder_web_contents) {
199 WebContents* result = nullptr;
200 ForEachGuest(embedder_web_contents,
201 base::Bind(&GuestViewManager::GetFullPageGuestHelper, &result));
202 return 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_]
225 // are invalid.
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_;
231 // Compact.
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)
239 break;
240 ++last_instance_id_removed_;
241 removed_instance_ids_.erase(iter++);
243 } else {
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
250 // destroyed.
251 auto embedder_it = view_destruction_callback_map_.find(embedder_process_id);
252 if (embedder_it == view_destruction_callback_map_.end())
253 return;
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) {
258 callback.Run();
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,
275 view_instance_id,
276 base::Bind(view_it->second.cleanup_function,
277 embedder_process_id,
278 view_instance_id));
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
284 // collected.
285 auto embedder_it = view_destruction_callback_map_.find(embedder_process_id);
286 if (embedder_it == view_destruction_callback_map_.end())
287 return;
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())
291 return;
292 Callbacks& callbacks_for_view = view_it->second;
293 for (auto& callback : callbacks_for_view)
294 callback.Run();
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()) {
306 NOTREACHED();
307 return nullptr;
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,
332 int instance_id) {
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())
342 return nullptr;
343 return it->second;
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);
356 return false;
358 return true;
361 bool GuestViewManager::CanUseGuestInstanceID(int guest_instance_id) {
362 if (guest_instance_id <= last_instance_id_removed_)
363 return false;
364 return !ContainsKey(removed_instance_ids_, guest_instance_id);
367 // static
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;
374 return true;
376 return false;
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
383 // instance ID.
384 if (guest_instance_id <= kInstanceIDNone)
385 return false;
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_)
392 return false;
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())
398 return true;
400 auto guest_view = GuestViewBase::FromWebContents(it->second);
401 if (!guest_view)
402 return false;
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