[ServiceWorker] Implement WebServiceWorkerContextClient::openWindow().
[chromium-blink-merge.git] / extensions / browser / guest_view / guest_view_manager.cc
blob8f6b0dc0bfd27a03ed5d412b3f431aa0adf106a7
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 "extensions/browser/guest_view/guest_view_manager.h"
7 #include "base/strings/stringprintf.h"
8 #include "content/public/browser/browser_context.h"
9 #include "content/public/browser/render_frame_host.h"
10 #include "content/public/browser/render_process_host.h"
11 #include "content/public/browser/render_view_host.h"
12 #include "content/public/browser/user_metrics.h"
13 #include "content/public/browser/web_contents_observer.h"
14 #include "content/public/common/result_codes.h"
15 #include "content/public/common/url_constants.h"
16 #include "extensions/browser/extension_system.h"
17 #include "extensions/browser/guest_view/guest_view_base.h"
18 #include "extensions/browser/guest_view/guest_view_manager_factory.h"
19 #include "extensions/common/extension_messages.h"
20 #include "extensions/common/guest_view/guest_view_constants.h"
21 #include "net/base/escape.h"
22 #include "url/gurl.h"
24 using content::BrowserContext;
25 using content::SiteInstance;
26 using content::WebContents;
28 namespace extensions {
30 // static
31 GuestViewManagerFactory* GuestViewManager::factory_ = nullptr;
33 GuestViewManager::GuestViewManager(content::BrowserContext* context)
34 : current_instance_id_(0), last_instance_id_removed_(0), context_(context) {
37 GuestViewManager::~GuestViewManager() {}
39 // static
40 GuestViewManager* GuestViewManager::FromBrowserContext(
41 BrowserContext* context) {
42 GuestViewManager* guest_manager =
43 static_cast<GuestViewManager*>(context->GetUserData(
44 guestview::kGuestViewManagerKeyName));
45 if (!guest_manager) {
46 if (factory_) {
47 guest_manager = factory_->CreateGuestViewManager(context);
48 } else {
49 guest_manager = new GuestViewManager(context);
51 context->SetUserData(guestview::kGuestViewManagerKeyName, guest_manager);
53 return guest_manager;
56 // static
57 GuestViewManager* GuestViewManager::FromBrowserContextIfAvailable(
58 BrowserContext* context) {
59 return static_cast<GuestViewManager*>(context->GetUserData(
60 guestview::kGuestViewManagerKeyName));
63 content::WebContents* GuestViewManager::GetGuestByInstanceIDSafely(
64 int guest_instance_id,
65 int embedder_render_process_id) {
66 if (!CanEmbedderAccessInstanceIDMaybeKill(embedder_render_process_id,
67 guest_instance_id)) {
68 return nullptr;
70 return GetGuestByInstanceID(guest_instance_id);
73 void GuestViewManager::AttachGuest(
74 int embedder_render_process_id,
75 int embedder_routing_id,
76 int element_instance_id,
77 int guest_instance_id,
78 const base::DictionaryValue& attach_params) {
79 content::WebContents* guest_web_contents =
80 GetGuestByInstanceIDSafely(guest_instance_id, embedder_render_process_id);
81 if (!guest_web_contents)
82 return;
84 auto guest_view = GuestViewBase::FromWebContents(guest_web_contents);
85 DCHECK(guest_view);
87 auto rvh = content::RenderViewHost::FromID(embedder_render_process_id,
88 embedder_routing_id);
89 // We need to check that rvh is not nullptr because there may be a race
90 // between AttachGuest and destroying the embedder (i.e. when the embedder is
91 // destroyed immediately after the guest is created).
92 if (!rvh)
93 return;
94 auto owner_web_contents = content::WebContents::FromRenderViewHost(rvh);
95 if (!owner_web_contents)
96 return;
97 ElementInstanceKey key(owner_web_contents, element_instance_id);
99 auto it = instance_id_map_.find(key);
100 if (it != instance_id_map_.end()) {
101 int old_guest_instance_id = it->second;
102 // Reattachment to the same guest is not currently supported.
103 if (old_guest_instance_id == guest_instance_id)
104 return;
106 auto old_guest_web_contents =
107 GetGuestByInstanceIDSafely(old_guest_instance_id,
108 embedder_render_process_id);
109 if (!old_guest_web_contents)
110 return;
112 auto old_guest_view =
113 GuestViewBase::FromWebContents(old_guest_web_contents);
115 old_guest_view->Destroy();
117 instance_id_map_[key] = guest_instance_id;
118 reverse_instance_id_map_[guest_instance_id] = key;
119 guest_view->SetAttachParams(attach_params);
122 void GuestViewManager::DetachGuest(GuestViewBase* guest,
123 int element_instance_id) {
124 if (!guest->attached())
125 return;
127 ElementInstanceKey key(guest->owner_web_contents(), element_instance_id);
128 auto it = instance_id_map_.find(key);
129 // There's nothing to do if this key does not exist in the map.
130 if (it == instance_id_map_.end())
131 return;
133 int guest_instance_id = it->second;
134 instance_id_map_.erase(key);
136 auto reverse_it = reverse_instance_id_map_.find(guest->guest_instance_id());
137 DCHECK(reverse_it != reverse_instance_id_map_.end());
138 DCHECK(reverse_it->second == key);
139 reverse_instance_id_map_.erase(guest_instance_id);
142 int GuestViewManager::GetNextInstanceID() {
143 return ++current_instance_id_;
146 void GuestViewManager::CreateGuest(const std::string& view_type,
147 content::WebContents* owner_web_contents,
148 const base::DictionaryValue& create_params,
149 const WebContentsCreatedCallback& callback) {
150 auto guest = GuestViewBase::Create(owner_web_contents, view_type);
151 if (!guest) {
152 callback.Run(nullptr);
153 return;
155 guest->Init(create_params, callback);
158 content::WebContents* GuestViewManager::CreateGuestWithWebContentsParams(
159 const std::string& view_type,
160 content::WebContents* owner_web_contents,
161 const content::WebContents::CreateParams& create_params) {
162 auto guest = GuestViewBase::Create(owner_web_contents, view_type);
163 if (!guest)
164 return nullptr;
165 content::WebContents::CreateParams guest_create_params(create_params);
166 guest_create_params.guest_delegate = guest;
167 auto guest_web_contents = WebContents::Create(guest_create_params);
168 guest->InitWithWebContents(base::DictionaryValue(), guest_web_contents);
169 return guest_web_contents;
172 content::WebContents* GuestViewManager::GetGuestByInstanceID(
173 content::WebContents* owner_web_contents,
174 int element_instance_id) {
175 int guest_instance_id = GetGuestInstanceIDForElementID(owner_web_contents,
176 element_instance_id);
177 if (guest_instance_id == guestview::kInstanceIDNone)
178 return nullptr;
180 return GetGuestByInstanceID(guest_instance_id);
183 int GuestViewManager::GetGuestInstanceIDForElementID(
184 content::WebContents* owner_web_contents,
185 int element_instance_id) {
186 auto iter = instance_id_map_.find(
187 ElementInstanceKey(owner_web_contents, element_instance_id));
188 if (iter == instance_id_map_.end())
189 return guestview::kInstanceIDNone;
190 return iter->second;
193 SiteInstance* GuestViewManager::GetGuestSiteInstance(
194 const GURL& guest_site) {
195 for (const auto& guest : guest_web_contents_by_instance_id_) {
196 if (guest.second->GetSiteInstance()->GetSiteURL() == guest_site)
197 return guest.second->GetSiteInstance();
199 return nullptr;
202 bool GuestViewManager::ForEachGuest(WebContents* owner_web_contents,
203 const GuestCallback& callback) {
204 for (const auto& guest : guest_web_contents_by_instance_id_) {
205 auto guest_view = GuestViewBase::FromWebContents(guest.second);
206 if (guest_view->owner_web_contents() != owner_web_contents)
207 continue;
209 if (callback.Run(guest_view->web_contents()))
210 return true;
212 return false;
215 void GuestViewManager::AddGuest(int guest_instance_id,
216 WebContents* guest_web_contents) {
217 CHECK(!ContainsKey(guest_web_contents_by_instance_id_, guest_instance_id));
218 CHECK(CanUseGuestInstanceID(guest_instance_id));
219 guest_web_contents_by_instance_id_[guest_instance_id] = guest_web_contents;
222 void GuestViewManager::RemoveGuest(int guest_instance_id) {
223 auto it = guest_web_contents_by_instance_id_.find(guest_instance_id);
224 DCHECK(it != guest_web_contents_by_instance_id_.end());
225 guest_web_contents_by_instance_id_.erase(it);
227 auto id_iter = reverse_instance_id_map_.find(guest_instance_id);
228 if (id_iter != reverse_instance_id_map_.end()) {
229 const ElementInstanceKey& instance_id_key = id_iter->second;
230 instance_id_map_.erase(instance_id_map_.find(instance_id_key));
231 reverse_instance_id_map_.erase(id_iter);
234 // All the instance IDs that lie within [0, last_instance_id_removed_]
235 // are invalid.
236 // The remaining sparse invalid IDs are kept in |removed_instance_ids_| set.
237 // The following code compacts the set by incrementing
238 // |last_instance_id_removed_|.
239 if (guest_instance_id == last_instance_id_removed_ + 1) {
240 ++last_instance_id_removed_;
241 // Compact.
242 auto iter = removed_instance_ids_.begin();
243 while (iter != removed_instance_ids_.end()) {
244 int instance_id = *iter;
245 // The sparse invalid IDs must not lie within
246 // [0, last_instance_id_removed_]
247 DCHECK(instance_id > last_instance_id_removed_);
248 if (instance_id != last_instance_id_removed_ + 1)
249 break;
250 ++last_instance_id_removed_;
251 removed_instance_ids_.erase(iter++);
253 } else {
254 removed_instance_ids_.insert(guest_instance_id);
258 content::WebContents* GuestViewManager::GetGuestByInstanceID(
259 int guest_instance_id) {
260 auto it = guest_web_contents_by_instance_id_.find(guest_instance_id);
261 if (it == guest_web_contents_by_instance_id_.end())
262 return nullptr;
263 return it->second;
266 bool GuestViewManager::CanEmbedderAccessInstanceIDMaybeKill(
267 int embedder_render_process_id,
268 int guest_instance_id) {
269 if (!CanEmbedderAccessInstanceID(embedder_render_process_id,
270 guest_instance_id)) {
271 // The embedder process is trying to access a guest it does not own.
272 content::RecordAction(
273 base::UserMetricsAction("BadMessageTerminate_BPGM"));
274 content::RenderProcessHost::FromID(embedder_render_process_id)
275 ->Shutdown(content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
276 return false;
278 return true;
281 bool GuestViewManager::CanUseGuestInstanceID(int guest_instance_id) {
282 if (guest_instance_id <= last_instance_id_removed_)
283 return false;
284 return !ContainsKey(removed_instance_ids_, guest_instance_id);
287 bool GuestViewManager::CanEmbedderAccessInstanceID(
288 int embedder_render_process_id,
289 int guest_instance_id) {
290 // The embedder is trying to access a guest with a negative or zero
291 // instance ID.
292 if (guest_instance_id <= guestview::kInstanceIDNone)
293 return false;
295 // The embedder is trying to access an instance ID that has not yet been
296 // allocated by GuestViewManager. This could cause instance ID
297 // collisions in the future, and potentially give one embedder access to a
298 // guest it does not own.
299 if (guest_instance_id > current_instance_id_)
300 return false;
302 // We might get some late arriving messages at tear down. Let's let the
303 // embedder tear down in peace.
304 auto it = guest_web_contents_by_instance_id_.find(guest_instance_id);
305 if (it == guest_web_contents_by_instance_id_.end())
306 return true;
308 auto guest_view = GuestViewBase::FromWebContents(it->second);
309 if (!guest_view)
310 return false;
312 return embedder_render_process_id ==
313 guest_view->owner_web_contents()->GetRenderProcessHost()->GetID();
316 } // namespace extensions