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_process_host.h"
10 #include "content/public/browser/render_view_host.h"
11 #include "content/public/browser/user_metrics.h"
12 #include "content/public/browser/web_contents_observer.h"
13 #include "content/public/common/result_codes.h"
14 #include "content/public/common/url_constants.h"
15 #include "extensions/browser/extension_system.h"
16 #include "extensions/browser/guest_view/guest_view_base.h"
17 #include "extensions/browser/guest_view/guest_view_manager_factory.h"
18 #include "extensions/common/guest_view/guest_view_constants.h"
19 #include "net/base/escape.h"
20 #include "url/gurl.h"
21
22 using content::BrowserContext;
23 using content::SiteInstance;
24 using content::WebContents;
25
26 namespace extensions {
27
28 // static
29 GuestViewManagerFactory* GuestViewManager::factory_ = NULL;
30
GuestViewManager(content::BrowserContext * context)31 GuestViewManager::GuestViewManager(content::BrowserContext* context)
32 : current_instance_id_(0), last_instance_id_removed_(0), context_(context) {
33 }
34
~GuestViewManager()35 GuestViewManager::~GuestViewManager() {}
36
37 // static.
FromBrowserContext(BrowserContext * context)38 GuestViewManager* GuestViewManager::FromBrowserContext(
39 BrowserContext* context) {
40 GuestViewManager* guest_manager =
41 static_cast<GuestViewManager*>(context->GetUserData(
42 guestview::kGuestViewManagerKeyName));
43 if (!guest_manager) {
44 if (factory_) {
45 guest_manager = factory_->CreateGuestViewManager(context);
46 } else {
47 guest_manager = new GuestViewManager(context);
48 }
49 context->SetUserData(guestview::kGuestViewManagerKeyName, guest_manager);
50 }
51 return guest_manager;
52 }
53
GetGuestByInstanceIDSafely(int guest_instance_id,int embedder_render_process_id)54 content::WebContents* GuestViewManager::GetGuestByInstanceIDSafely(
55 int guest_instance_id,
56 int embedder_render_process_id) {
57 if (!CanEmbedderAccessInstanceIDMaybeKill(embedder_render_process_id,
58 guest_instance_id)) {
59 return NULL;
60 }
61 return GetGuestByInstanceID(guest_instance_id);
62 }
63
AttachGuest(int embedder_render_process_id,int embedder_routing_id,int element_instance_id,int guest_instance_id,const base::DictionaryValue & attach_params)64 void GuestViewManager::AttachGuest(
65 int embedder_render_process_id,
66 int embedder_routing_id,
67 int element_instance_id,
68 int guest_instance_id,
69 const base::DictionaryValue& attach_params) {
70 content::WebContents* guest_web_contents =
71 GetGuestByInstanceIDSafely(guest_instance_id, embedder_render_process_id);
72 if (!guest_web_contents)
73 return;
74
75 GuestViewBase* guest_view =
76 GuestViewBase::FromWebContents(guest_web_contents);
77 DCHECK(guest_view);
78
79 content::RenderViewHost* rvh =
80 content::RenderViewHost::FromID(embedder_render_process_id,
81 embedder_routing_id);
82 content::WebContents* embedder_web_contents =
83 content::WebContents::FromRenderViewHost(rvh);
84 if (!embedder_web_contents)
85 return;
86 ElementInstanceKey key(embedder_web_contents, element_instance_id);
87
88 GuestInstanceIDMap::iterator it = instance_id_map_.find(key);
89 if (it != instance_id_map_.end()) {
90 int old_guest_instance_id = it->second;
91 // Reattachment to the same guest is not currently supported.
92 if (old_guest_instance_id == guest_instance_id)
93 return;
94
95 content::WebContents* old_guest_web_contents =
96 GetGuestByInstanceIDSafely(old_guest_instance_id,
97 embedder_render_process_id);
98 if (!old_guest_web_contents)
99 return;
100
101 GuestViewBase* old_guest_view =
102 GuestViewBase::FromWebContents(old_guest_web_contents);
103
104 old_guest_view->Destroy();
105 }
106 instance_id_map_[key] = guest_instance_id;
107 reverse_instance_id_map_.insert(std::make_pair(guest_instance_id, key));
108 guest_view->SetAttachParams(attach_params);
109 }
110
GetNextInstanceID()111 int GuestViewManager::GetNextInstanceID() {
112 return ++current_instance_id_;
113 }
114
CreateGuest(const std::string & view_type,const std::string & embedder_extension_id,content::WebContents * embedder_web_contents,const base::DictionaryValue & create_params,const WebContentsCreatedCallback & callback)115 void GuestViewManager::CreateGuest(const std::string& view_type,
116 const std::string& embedder_extension_id,
117 content::WebContents* embedder_web_contents,
118 const base::DictionaryValue& create_params,
119 const WebContentsCreatedCallback& callback) {
120 int guest_instance_id = GetNextInstanceID();
121 GuestViewBase* guest =
122 GuestViewBase::Create(context_, guest_instance_id, view_type);
123 if (!guest) {
124 callback.Run(NULL);
125 return;
126 }
127 guest->Init(
128 embedder_extension_id, embedder_web_contents, create_params, callback);
129 }
130
CreateGuestWithWebContentsParams(const std::string & view_type,const std::string & embedder_extension_id,int embedder_render_process_id,const content::WebContents::CreateParams & create_params)131 content::WebContents* GuestViewManager::CreateGuestWithWebContentsParams(
132 const std::string& view_type,
133 const std::string& embedder_extension_id,
134 int embedder_render_process_id,
135 const content::WebContents::CreateParams& create_params) {
136 int guest_instance_id = GetNextInstanceID();
137 GuestViewBase* guest =
138 GuestViewBase::Create(context_, guest_instance_id, view_type);
139 if (!guest)
140 return NULL;
141 content::WebContents::CreateParams guest_create_params(create_params);
142 guest_create_params.guest_delegate = guest;
143 content::WebContents* guest_web_contents =
144 WebContents::Create(guest_create_params);
145 guest->InitWithWebContents(embedder_extension_id,
146 embedder_render_process_id,
147 guest_web_contents);
148 return guest_web_contents;
149 }
150
GetGuestByInstanceID(content::WebContents * embedder_web_contents,int element_instance_id)151 content::WebContents* GuestViewManager::GetGuestByInstanceID(
152 content::WebContents* embedder_web_contents,
153 int element_instance_id) {
154 int guest_instance_id = GetGuestInstanceIDForElementID(embedder_web_contents,
155 element_instance_id);
156 if (guest_instance_id == guestview::kInstanceIDNone)
157 return NULL;
158
159 return GetGuestByInstanceID(guest_instance_id);
160 }
161
GetGuestInstanceIDForElementID(content::WebContents * embedder_web_contents,int element_instance_id)162 int GuestViewManager::GetGuestInstanceIDForElementID(
163 content::WebContents* embedder_web_contents,
164 int element_instance_id) {
165 GuestInstanceIDMap::iterator iter = instance_id_map_.find(
166 ElementInstanceKey(embedder_web_contents, element_instance_id));
167 if (iter == instance_id_map_.end())
168 return guestview::kInstanceIDNone;
169 return iter->second;
170 }
171
GetGuestSiteInstance(const GURL & guest_site)172 SiteInstance* GuestViewManager::GetGuestSiteInstance(
173 const GURL& guest_site) {
174 for (GuestInstanceMap::const_iterator it =
175 guest_web_contents_by_instance_id_.begin();
176 it != guest_web_contents_by_instance_id_.end(); ++it) {
177 if (it->second->GetSiteInstance()->GetSiteURL() == guest_site)
178 return it->second->GetSiteInstance();
179 }
180 return NULL;
181 }
182
ForEachGuest(WebContents * embedder_web_contents,const GuestCallback & callback)183 bool GuestViewManager::ForEachGuest(WebContents* embedder_web_contents,
184 const GuestCallback& callback) {
185 for (GuestInstanceMap::iterator it =
186 guest_web_contents_by_instance_id_.begin();
187 it != guest_web_contents_by_instance_id_.end(); ++it) {
188 WebContents* guest = it->second;
189 GuestViewBase* guest_view = GuestViewBase::FromWebContents(guest);
190 if (embedder_web_contents != guest_view->embedder_web_contents())
191 continue;
192
193 if (callback.Run(guest))
194 return true;
195 }
196 return false;
197 }
198
AddGuest(int guest_instance_id,WebContents * guest_web_contents)199 void GuestViewManager::AddGuest(int guest_instance_id,
200 WebContents* guest_web_contents) {
201 CHECK(!ContainsKey(guest_web_contents_by_instance_id_, guest_instance_id));
202 CHECK(CanUseGuestInstanceID(guest_instance_id));
203 guest_web_contents_by_instance_id_[guest_instance_id] = guest_web_contents;
204 }
205
RemoveGuest(int guest_instance_id)206 void GuestViewManager::RemoveGuest(int guest_instance_id) {
207 GuestInstanceMap::iterator it =
208 guest_web_contents_by_instance_id_.find(guest_instance_id);
209 DCHECK(it != guest_web_contents_by_instance_id_.end());
210 guest_web_contents_by_instance_id_.erase(it);
211
212 GuestInstanceIDReverseMap::iterator id_iter =
213 reverse_instance_id_map_.find(guest_instance_id);
214 if (id_iter != reverse_instance_id_map_.end()) {
215 const ElementInstanceKey& instance_id_key = id_iter->second;
216 instance_id_map_.erase(instance_id_map_.find(instance_id_key));
217 reverse_instance_id_map_.erase(id_iter);
218 }
219
220 // All the instance IDs that lie within [0, last_instance_id_removed_]
221 // are invalid.
222 // The remaining sparse invalid IDs are kept in |removed_instance_ids_| set.
223 // The following code compacts the set by incrementing
224 // |last_instance_id_removed_|.
225 if (guest_instance_id == last_instance_id_removed_ + 1) {
226 ++last_instance_id_removed_;
227 // Compact.
228 std::set<int>::iterator iter = removed_instance_ids_.begin();
229 while (iter != removed_instance_ids_.end()) {
230 int instance_id = *iter;
231 // The sparse invalid IDs must not lie within
232 // [0, last_instance_id_removed_]
233 DCHECK(instance_id > last_instance_id_removed_);
234 if (instance_id != last_instance_id_removed_ + 1)
235 break;
236 ++last_instance_id_removed_;
237 removed_instance_ids_.erase(iter++);
238 }
239 } else {
240 removed_instance_ids_.insert(guest_instance_id);
241 }
242 }
243
GetGuestByInstanceID(int guest_instance_id)244 content::WebContents* GuestViewManager::GetGuestByInstanceID(
245 int guest_instance_id) {
246 GuestInstanceMap::const_iterator it =
247 guest_web_contents_by_instance_id_.find(guest_instance_id);
248 if (it == guest_web_contents_by_instance_id_.end())
249 return NULL;
250 return it->second;
251 }
252
CanEmbedderAccessInstanceIDMaybeKill(int embedder_render_process_id,int guest_instance_id)253 bool GuestViewManager::CanEmbedderAccessInstanceIDMaybeKill(
254 int embedder_render_process_id,
255 int guest_instance_id) {
256 if (!CanEmbedderAccessInstanceID(embedder_render_process_id,
257 guest_instance_id)) {
258 // The embedder process is trying to access a guest it does not own.
259 content::RecordAction(
260 base::UserMetricsAction("BadMessageTerminate_BPGM"));
261 base::KillProcess(
262 content::RenderProcessHost::FromID(embedder_render_process_id)->
263 GetHandle(),
264 content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
265 return false;
266 }
267 return true;
268 }
269
CanUseGuestInstanceID(int guest_instance_id)270 bool GuestViewManager::CanUseGuestInstanceID(int guest_instance_id) {
271 if (guest_instance_id <= last_instance_id_removed_)
272 return false;
273 return !ContainsKey(removed_instance_ids_, guest_instance_id);
274 }
275
CanEmbedderAccessInstanceID(int embedder_render_process_id,int guest_instance_id)276 bool GuestViewManager::CanEmbedderAccessInstanceID(
277 int embedder_render_process_id,
278 int guest_instance_id) {
279 // The embedder is trying to access a guest with a negative or zero
280 // instance ID.
281 if (guest_instance_id <= guestview::kInstanceIDNone)
282 return false;
283
284 // The embedder is trying to access an instance ID that has not yet been
285 // allocated by GuestViewManager. This could cause instance ID
286 // collisions in the future, and potentially give one embedder access to a
287 // guest it does not own.
288 if (guest_instance_id > current_instance_id_)
289 return false;
290
291 // We might get some late arriving messages at tear down. Let's let the
292 // embedder tear down in peace.
293 GuestInstanceMap::const_iterator it =
294 guest_web_contents_by_instance_id_.find(guest_instance_id);
295 if (it == guest_web_contents_by_instance_id_.end())
296 return true;
297
298 GuestViewBase* guest_view = GuestViewBase::FromWebContents(it->second);
299 if (!guest_view)
300 return false;
301
302 return embedder_render_process_id == guest_view->embedder_render_process_id();
303 }
304
305 } // namespace extensions
306