Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / extensions / browser / guest_view / guest_view_manager.cc
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
31 GuestViewManager::GuestViewManager(content::BrowserContext* context)
32     : current_instance_id_(0), last_instance_id_removed_(0), context_(context) {
33 }
34
35 GuestViewManager::~GuestViewManager() {}
36
37 // static.
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
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
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
111 int GuestViewManager::GetNextInstanceID() {
112   return ++current_instance_id_;
113 }
114
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
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
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
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
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
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
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
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
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
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
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
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