OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "extensions/browser/guest_view/guest_view_manager.h" | |
6 | |
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/child_process_host.h" | |
15 #include "content/public/common/result_codes.h" | |
16 #include "content/public/common/url_constants.h" | |
17 #include "extensions/browser/guest_view/guest_view_base.h" | |
18 #include "extensions/browser/guest_view/guest_view_manager_delegate.h" | |
19 #include "extensions/browser/guest_view/guest_view_manager_factory.h" | |
20 #include "extensions/common/guest_view/guest_view_constants.h" | |
21 #include "net/base/escape.h" | |
22 #include "url/gurl.h" | |
23 | |
24 using content::BrowserContext; | |
25 using content::SiteInstance; | |
26 using content::WebContents; | |
27 using guestview::GuestViewManagerDelegate; | |
28 | |
29 namespace extensions { | |
30 | |
31 // static | |
32 GuestViewManagerFactory* GuestViewManager::factory_ = nullptr; | |
33 | |
34 GuestViewManager::GuestViewManager( | |
35 content::BrowserContext* context, | |
36 scoped_ptr<GuestViewManagerDelegate> delegate) | |
37 : current_instance_id_(0), | |
38 last_instance_id_removed_(0), | |
39 context_(context), | |
40 delegate_(delegate.Pass()) { | |
41 } | |
42 | |
43 GuestViewManager::~GuestViewManager() {} | |
44 | |
45 // static | |
46 GuestViewManager* GuestViewManager::CreateWithDelegate( | |
47 BrowserContext* context, | |
48 scoped_ptr<GuestViewManagerDelegate> delegate) { | |
49 GuestViewManager* guest_manager = FromBrowserContext(context); | |
50 if (!guest_manager) { | |
51 if (factory_) { | |
52 guest_manager = | |
53 factory_->CreateGuestViewManager(context, delegate.Pass()); | |
54 } else { | |
55 guest_manager = new GuestViewManager(context, delegate.Pass()); | |
56 } | |
57 context->SetUserData(guestview::kGuestViewManagerKeyName, guest_manager); | |
58 } | |
59 return guest_manager; | |
60 } | |
61 | |
62 // static | |
63 GuestViewManager* GuestViewManager::FromBrowserContext( | |
64 BrowserContext* context) { | |
65 return static_cast<GuestViewManager*>(context->GetUserData( | |
66 guestview::kGuestViewManagerKeyName)); | |
67 } | |
68 | |
69 content::WebContents* GuestViewManager::GetGuestByInstanceIDSafely( | |
70 int guest_instance_id, | |
71 int embedder_render_process_id) { | |
72 if (!CanEmbedderAccessInstanceIDMaybeKill(embedder_render_process_id, | |
73 guest_instance_id)) { | |
74 return nullptr; | |
75 } | |
76 return GetGuestByInstanceID(guest_instance_id); | |
77 } | |
78 | |
79 void GuestViewManager::AttachGuest(int embedder_process_id, | |
80 int element_instance_id, | |
81 int guest_instance_id, | |
82 const base::DictionaryValue& attach_params) { | |
83 auto guest_view = GuestViewBase::From(embedder_process_id, guest_instance_id); | |
84 if (!guest_view) | |
85 return; | |
86 | |
87 ElementInstanceKey key(embedder_process_id, element_instance_id); | |
88 auto it = instance_id_map_.find(key); | |
89 // If there is an existing guest attached to the element, then destroy the | |
90 // existing guest. | |
91 if (it != instance_id_map_.end()) { | |
92 int old_guest_instance_id = it->second; | |
93 if (old_guest_instance_id == guest_instance_id) | |
94 return; | |
95 | |
96 auto old_guest_view = GuestViewBase::From(embedder_process_id, | |
97 old_guest_instance_id); | |
98 old_guest_view->Destroy(); | |
99 } | |
100 instance_id_map_[key] = guest_instance_id; | |
101 reverse_instance_id_map_[guest_instance_id] = key; | |
102 guest_view->SetAttachParams(attach_params); | |
103 } | |
104 | |
105 void GuestViewManager::DetachGuest(GuestViewBase* guest) { | |
106 if (!guest->attached()) | |
107 return; | |
108 | |
109 auto reverse_it = reverse_instance_id_map_.find(guest->guest_instance_id()); | |
110 if (reverse_it == reverse_instance_id_map_.end()) | |
111 return; | |
112 | |
113 const ElementInstanceKey& key = reverse_it->second; | |
114 | |
115 auto it = instance_id_map_.find(key); | |
116 DCHECK(it != instance_id_map_.end()); | |
117 | |
118 reverse_instance_id_map_.erase(reverse_it); | |
119 instance_id_map_.erase(it); | |
120 } | |
121 | |
122 bool GuestViewManager::IsOwnedByExtension(GuestViewBase* guest) { | |
123 return delegate_->IsOwnedByExtension(guest); | |
124 } | |
125 | |
126 int GuestViewManager::GetNextInstanceID() { | |
127 return ++current_instance_id_; | |
128 } | |
129 | |
130 void GuestViewManager::CreateGuest(const std::string& view_type, | |
131 content::WebContents* owner_web_contents, | |
132 const base::DictionaryValue& create_params, | |
133 const WebContentsCreatedCallback& callback) { | |
134 GuestViewBase* guest = CreateGuestInternal(owner_web_contents, view_type); | |
135 if (!guest) { | |
136 callback.Run(nullptr); | |
137 return; | |
138 } | |
139 guest->Init(create_params, callback); | |
140 } | |
141 | |
142 content::WebContents* GuestViewManager::CreateGuestWithWebContentsParams( | |
143 const std::string& view_type, | |
144 content::WebContents* owner_web_contents, | |
145 const content::WebContents::CreateParams& create_params) { | |
146 auto guest = CreateGuestInternal(owner_web_contents, view_type); | |
147 if (!guest) | |
148 return nullptr; | |
149 content::WebContents::CreateParams guest_create_params(create_params); | |
150 guest_create_params.guest_delegate = guest; | |
151 auto guest_web_contents = WebContents::Create(guest_create_params); | |
152 guest->InitWithWebContents(base::DictionaryValue(), guest_web_contents); | |
153 return guest_web_contents; | |
154 } | |
155 | |
156 content::WebContents* GuestViewManager::GetGuestByInstanceID( | |
157 int owner_process_id, | |
158 int element_instance_id) { | |
159 int guest_instance_id = GetGuestInstanceIDForElementID(owner_process_id, | |
160 element_instance_id); | |
161 if (guest_instance_id == guestview::kInstanceIDNone) | |
162 return nullptr; | |
163 | |
164 return GetGuestByInstanceID(guest_instance_id); | |
165 } | |
166 | |
167 int GuestViewManager::GetGuestInstanceIDForElementID(int owner_process_id, | |
168 int element_instance_id) { | |
169 auto iter = instance_id_map_.find( | |
170 ElementInstanceKey(owner_process_id, element_instance_id)); | |
171 if (iter == instance_id_map_.end()) | |
172 return guestview::kInstanceIDNone; | |
173 return iter->second; | |
174 } | |
175 | |
176 SiteInstance* GuestViewManager::GetGuestSiteInstance( | |
177 const GURL& guest_site) { | |
178 for (const auto& guest : guest_web_contents_by_instance_id_) { | |
179 if (guest.second->GetSiteInstance()->GetSiteURL() == guest_site) | |
180 return guest.second->GetSiteInstance(); | |
181 } | |
182 return nullptr; | |
183 } | |
184 | |
185 bool GuestViewManager::ForEachGuest(WebContents* owner_web_contents, | |
186 const GuestCallback& callback) { | |
187 for (const auto& guest : guest_web_contents_by_instance_id_) { | |
188 auto guest_view = GuestViewBase::FromWebContents(guest.second); | |
189 if (guest_view->owner_web_contents() != owner_web_contents) | |
190 continue; | |
191 | |
192 if (callback.Run(guest_view->web_contents())) | |
193 return true; | |
194 } | |
195 return false; | |
196 } | |
197 | |
198 WebContents* GuestViewManager::GetFullPageGuest( | |
199 WebContents* embedder_web_contents) { | |
200 WebContents* result = nullptr; | |
201 ForEachGuest(embedder_web_contents, | |
202 base::Bind(&GuestViewManager::GetFullPageGuestHelper, &result)); | |
203 return result; | |
204 } | |
205 | |
206 void GuestViewManager::AddGuest(int guest_instance_id, | |
207 WebContents* guest_web_contents) { | |
208 CHECK(!ContainsKey(guest_web_contents_by_instance_id_, guest_instance_id)); | |
209 CHECK(CanUseGuestInstanceID(guest_instance_id)); | |
210 guest_web_contents_by_instance_id_[guest_instance_id] = guest_web_contents; | |
211 } | |
212 | |
213 void GuestViewManager::RemoveGuest(int guest_instance_id) { | |
214 auto it = guest_web_contents_by_instance_id_.find(guest_instance_id); | |
215 DCHECK(it != guest_web_contents_by_instance_id_.end()); | |
216 guest_web_contents_by_instance_id_.erase(it); | |
217 | |
218 auto id_iter = reverse_instance_id_map_.find(guest_instance_id); | |
219 if (id_iter != reverse_instance_id_map_.end()) { | |
220 const ElementInstanceKey& instance_id_key = id_iter->second; | |
221 instance_id_map_.erase(instance_id_map_.find(instance_id_key)); | |
222 reverse_instance_id_map_.erase(id_iter); | |
223 } | |
224 | |
225 // All the instance IDs that lie within [0, last_instance_id_removed_] | |
226 // are invalid. | |
227 // The remaining sparse invalid IDs are kept in |removed_instance_ids_| set. | |
228 // The following code compacts the set by incrementing | |
229 // |last_instance_id_removed_|. | |
230 if (guest_instance_id == last_instance_id_removed_ + 1) { | |
231 ++last_instance_id_removed_; | |
232 // Compact. | |
233 auto iter = removed_instance_ids_.begin(); | |
234 while (iter != removed_instance_ids_.end()) { | |
235 int instance_id = *iter; | |
236 // The sparse invalid IDs must not lie within | |
237 // [0, last_instance_id_removed_] | |
238 DCHECK(instance_id > last_instance_id_removed_); | |
239 if (instance_id != last_instance_id_removed_ + 1) | |
240 break; | |
241 ++last_instance_id_removed_; | |
242 removed_instance_ids_.erase(iter++); | |
243 } | |
244 } else { | |
245 removed_instance_ids_.insert(guest_instance_id); | |
246 } | |
247 } | |
248 | |
249 GuestViewBase* GuestViewManager::CreateGuestInternal( | |
250 content::WebContents* owner_web_contents, | |
251 const std::string& view_type) { | |
252 if (guest_view_registry_.empty()) | |
253 RegisterGuestViewTypes(); | |
254 | |
255 auto it = guest_view_registry_.find(view_type); | |
256 if (it == guest_view_registry_.end()) { | |
257 NOTREACHED(); | |
258 return nullptr; | |
259 } | |
260 | |
261 return it->second.Run(owner_web_contents); | |
262 } | |
263 | |
264 void GuestViewManager::RegisterGuestViewTypes() { | |
265 delegate_->RegisterAdditionalGuestViewTypes(); | |
266 } | |
267 | |
268 bool GuestViewManager::IsGuestAvailableToContext(GuestViewBase* guest) { | |
269 return delegate_->IsGuestAvailableToContext(guest); | |
270 } | |
271 | |
272 void GuestViewManager::DispatchEvent(const std::string& event_name, | |
273 scoped_ptr<base::DictionaryValue> args, | |
274 GuestViewBase* guest, | |
275 int instance_id) { | |
276 delegate_->DispatchEvent(event_name, args.Pass(), guest, instance_id); | |
277 } | |
278 | |
279 content::WebContents* GuestViewManager::GetGuestByInstanceID( | |
280 int guest_instance_id) { | |
281 auto it = guest_web_contents_by_instance_id_.find(guest_instance_id); | |
282 if (it == guest_web_contents_by_instance_id_.end()) | |
283 return nullptr; | |
284 return it->second; | |
285 } | |
286 | |
287 bool GuestViewManager::CanEmbedderAccessInstanceIDMaybeKill( | |
288 int embedder_render_process_id, | |
289 int guest_instance_id) { | |
290 if (!CanEmbedderAccessInstanceID(embedder_render_process_id, | |
291 guest_instance_id)) { | |
292 // The embedder process is trying to access a guest it does not own. | |
293 content::RecordAction( | |
294 base::UserMetricsAction("BadMessageTerminate_BPGM")); | |
295 content::RenderProcessHost::FromID(embedder_render_process_id) | |
296 ->Shutdown(content::RESULT_CODE_KILLED_BAD_MESSAGE, false); | |
297 return false; | |
298 } | |
299 return true; | |
300 } | |
301 | |
302 bool GuestViewManager::CanUseGuestInstanceID(int guest_instance_id) { | |
303 if (guest_instance_id <= last_instance_id_removed_) | |
304 return false; | |
305 return !ContainsKey(removed_instance_ids_, guest_instance_id); | |
306 } | |
307 | |
308 // static | |
309 bool GuestViewManager::GetFullPageGuestHelper( | |
310 content::WebContents** result, | |
311 content::WebContents* guest_web_contents) { | |
312 auto guest_view = GuestViewBase::FromWebContents(guest_web_contents); | |
313 if (guest_view && guest_view->is_full_page_plugin()) { | |
314 *result = guest_web_contents; | |
315 return true; | |
316 } | |
317 return false; | |
318 } | |
319 | |
320 bool GuestViewManager::CanEmbedderAccessInstanceID( | |
321 int embedder_render_process_id, | |
322 int guest_instance_id) { | |
323 // The embedder is trying to access a guest with a negative or zero | |
324 // instance ID. | |
325 if (guest_instance_id <= guestview::kInstanceIDNone) | |
326 return false; | |
327 | |
328 // The embedder is trying to access an instance ID that has not yet been | |
329 // allocated by GuestViewManager. This could cause instance ID | |
330 // collisions in the future, and potentially give one embedder access to a | |
331 // guest it does not own. | |
332 if (guest_instance_id > current_instance_id_) | |
333 return false; | |
334 | |
335 // We might get some late arriving messages at tear down. Let's let the | |
336 // embedder tear down in peace. | |
337 auto it = guest_web_contents_by_instance_id_.find(guest_instance_id); | |
338 if (it == guest_web_contents_by_instance_id_.end()) | |
339 return true; | |
340 | |
341 auto guest_view = GuestViewBase::FromWebContents(it->second); | |
342 if (!guest_view) | |
343 return false; | |
344 | |
345 return embedder_render_process_id == | |
346 guest_view->owner_web_contents()->GetRenderProcessHost()->GetID(); | |
347 } | |
348 | |
349 GuestViewManager::ElementInstanceKey::ElementInstanceKey() | |
350 : embedder_process_id(content::ChildProcessHost::kInvalidUniqueID), | |
351 element_instance_id(content::ChildProcessHost::kInvalidUniqueID) { | |
352 } | |
353 | |
354 GuestViewManager::ElementInstanceKey::ElementInstanceKey( | |
355 int embedder_process_id, | |
356 int element_instance_id) | |
357 : embedder_process_id(embedder_process_id), | |
358 element_instance_id(element_instance_id) { | |
359 } | |
360 | |
361 bool GuestViewManager::ElementInstanceKey::operator<( | |
362 const GuestViewManager::ElementInstanceKey& other) const { | |
363 if (embedder_process_id != other.embedder_process_id) | |
364 return embedder_process_id < other.embedder_process_id; | |
365 | |
366 return element_instance_id < other.element_instance_id; | |
367 } | |
368 | |
369 bool GuestViewManager::ElementInstanceKey::operator==( | |
370 const GuestViewManager::ElementInstanceKey& other) const { | |
371 return (embedder_process_id == other.embedder_process_id) && | |
372 (element_instance_id == other.element_instance_id); | |
373 } | |
374 | |
375 } // namespace extensions | |
OLD | NEW |