Upstream version 9.37.197.0
[platform/framework/web/crosswalk.git] / src / chrome / 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 "chrome/browser/guest_view/guest_view_manager.h"
6
7 #include "base/strings/stringprintf.h"
8 #include "chrome/browser/extensions/extension_service.h"
9 #include "chrome/browser/guest_view/guest_view_base.h"
10 #include "chrome/browser/guest_view/guest_view_constants.h"
11 #include "chrome/browser/guest_view/guest_view_manager_factory.h"
12 #include "chrome/browser/guest_view/web_view/web_view_guest.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "content/public/browser/browser_context.h"
15 #include "content/public/browser/render_process_host.h"
16 #include "content/public/browser/user_metrics.h"
17 #include "content/public/browser/web_contents_observer.h"
18 #include "content/public/common/result_codes.h"
19 #include "content/public/common/url_constants.h"
20 #include "extensions/browser/extension_system.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
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, embedder_render_process_id);
62 }
63
64 int GuestViewManager::GetNextInstanceID() {
65   return ++current_instance_id_;
66 }
67
68 content::WebContents* GuestViewManager::CreateGuest(
69     content::SiteInstance* embedder_site_instance,
70     int instance_id,
71     scoped_ptr<base::DictionaryValue> extra_params) {
72   std::string storage_partition_id;
73   bool persist_storage = false;
74   std::string storage_partition_string;
75   WebViewGuest::ParsePartitionParam(
76       extra_params.get(), &storage_partition_id, &persist_storage);
77
78   content::RenderProcessHost* embedder_process_host =
79       embedder_site_instance->GetProcess();
80   // Validate that the partition id coming from the renderer is valid UTF-8,
81   // since we depend on this in other parts of the code, such as FilePath
82   // creation. If the validation fails, treat it as a bad message and kill the
83   // renderer process.
84   if (!base::IsStringUTF8(storage_partition_id)) {
85     content::RecordAction(
86         base::UserMetricsAction("BadMessageTerminate_BPGM"));
87     base::KillProcess(
88         embedder_process_host->GetHandle(),
89         content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
90     return NULL;
91   }
92
93   const GURL& embedder_site_url = embedder_site_instance->GetSiteURL();
94   const std::string& host = embedder_site_url.host();
95
96   std::string url_encoded_partition = net::EscapeQueryParamValue(
97       storage_partition_id, false);
98   // The SiteInstance of a given webview tag is based on the fact that it's
99   // a guest process in addition to which platform application the tag
100   // belongs to and what storage partition is in use, rather than the URL
101   // that the tag is being navigated to.
102   GURL guest_site(base::StringPrintf("%s://%s/%s?%s",
103                                      content::kGuestScheme,
104                                      host.c_str(),
105                                      persist_storage ? "persist" : "",
106                                      url_encoded_partition.c_str()));
107
108   // If we already have a webview tag in the same app using the same storage
109   // partition, we should use the same SiteInstance so the existing tag and
110   // the new tag can script each other.
111   SiteInstance* guest_site_instance = GetGuestSiteInstance(guest_site);
112   if (!guest_site_instance) {
113     // Create the SiteInstance in a new BrowsingInstance, which will ensure
114     // that webview tags are also not allowed to send messages across
115     // different partitions.
116     guest_site_instance = SiteInstance::CreateForURL(
117         embedder_site_instance->GetBrowserContext(), guest_site);
118   }
119   WebContents::CreateParams create_params(
120       embedder_site_instance->GetBrowserContext(),
121       guest_site_instance);
122   create_params.guest_instance_id = instance_id;
123   create_params.guest_extra_params.reset(extra_params.release());
124   return WebContents::Create(create_params);
125 }
126
127 void GuestViewManager::MaybeGetGuestByInstanceIDOrKill(
128     int guest_instance_id,
129     int embedder_render_process_id,
130     const GuestByInstanceIDCallback& callback) {
131   if (!CanEmbedderAccessInstanceIDMaybeKill(embedder_render_process_id,
132                                             guest_instance_id)) {
133     // If we kill the embedder, then don't bother calling back.
134     return;
135   }
136   content::WebContents* guest_web_contents =
137       GetGuestByInstanceID(guest_instance_id, embedder_render_process_id);
138   callback.Run(guest_web_contents);
139 }
140
141 SiteInstance* GuestViewManager::GetGuestSiteInstance(
142     const GURL& guest_site) {
143   for (GuestInstanceMap::const_iterator it =
144        guest_web_contents_by_instance_id_.begin();
145        it != guest_web_contents_by_instance_id_.end(); ++it) {
146     if (it->second->GetSiteInstance()->GetSiteURL() == guest_site)
147       return it->second->GetSiteInstance();
148   }
149   return NULL;
150 }
151
152 bool GuestViewManager::ForEachGuest(WebContents* embedder_web_contents,
153                                     const GuestCallback& callback) {
154   for (GuestInstanceMap::iterator it =
155            guest_web_contents_by_instance_id_.begin();
156        it != guest_web_contents_by_instance_id_.end(); ++it) {
157     WebContents* guest = it->second;
158     GuestViewBase* guest_view = GuestViewBase::FromWebContents(guest);
159     if (embedder_web_contents != guest_view->embedder_web_contents())
160       continue;
161
162     if (callback.Run(guest))
163       return true;
164   }
165   return false;
166 }
167
168 void GuestViewManager::AddGuest(int guest_instance_id,
169                                 WebContents* guest_web_contents) {
170   CHECK(!ContainsKey(guest_web_contents_by_instance_id_, guest_instance_id));
171   CHECK(CanUseGuestInstanceID(guest_instance_id));
172   guest_web_contents_by_instance_id_[guest_instance_id] = guest_web_contents;
173 }
174
175 void GuestViewManager::RemoveGuest(int guest_instance_id) {
176   GuestInstanceMap::iterator it =
177       guest_web_contents_by_instance_id_.find(guest_instance_id);
178   DCHECK(it != guest_web_contents_by_instance_id_.end());
179   guest_web_contents_by_instance_id_.erase(it);
180
181   // All the instance IDs that lie within [0, last_instance_id_removed_]
182   // are invalid.
183   // The remaining sparse invalid IDs are kept in |removed_instance_ids_| set.
184   // The following code compacts the set by incrementing
185   // |last_instance_id_removed_|.
186   if (guest_instance_id == last_instance_id_removed_ + 1) {
187     ++last_instance_id_removed_;
188     // Compact.
189     std::set<int>::iterator iter = removed_instance_ids_.begin();
190     while (iter != removed_instance_ids_.end()) {
191       int instance_id = *iter;
192       // The sparse invalid IDs must not lie within
193       // [0, last_instance_id_removed_]
194       DCHECK(instance_id > last_instance_id_removed_);
195       if (instance_id != last_instance_id_removed_ + 1)
196         break;
197       ++last_instance_id_removed_;
198       removed_instance_ids_.erase(iter++);
199     }
200   } else {
201     removed_instance_ids_.insert(guest_instance_id);
202   }
203 }
204
205 content::WebContents* GuestViewManager::GetGuestByInstanceID(
206     int guest_instance_id,
207     int embedder_render_process_id) {
208   GuestInstanceMap::const_iterator it =
209       guest_web_contents_by_instance_id_.find(guest_instance_id);
210   if (it == guest_web_contents_by_instance_id_.end())
211     return NULL;
212   return it->second;
213 }
214
215 bool GuestViewManager::CanEmbedderAccessInstanceIDMaybeKill(
216     int embedder_render_process_id,
217     int guest_instance_id) {
218   if (!CanEmbedderAccessInstanceID(embedder_render_process_id,
219                                    guest_instance_id)) {
220     // The embedder process is trying to access a guest it does not own.
221     content::RecordAction(
222         base::UserMetricsAction("BadMessageTerminate_BPGM"));
223     base::KillProcess(
224         content::RenderProcessHost::FromID(embedder_render_process_id)->
225             GetHandle(),
226         content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
227     return false;
228   }
229   return true;
230 }
231
232 bool GuestViewManager::CanUseGuestInstanceID(int guest_instance_id) {
233   if (guest_instance_id <= last_instance_id_removed_)
234     return false;
235   return !ContainsKey(removed_instance_ids_, guest_instance_id);
236 }
237
238 bool GuestViewManager::CanEmbedderAccessInstanceID(
239     int embedder_render_process_id,
240     int guest_instance_id) {
241   // The embedder is trying to access a guest with a negative or zero
242   // instance ID.
243   if (guest_instance_id <= guestview::kInstanceIDNone)
244     return false;
245
246   // The embedder is trying to access an instance ID that has not yet been
247   // allocated by GuestViewManager. This could cause instance ID
248   // collisions in the future, and potentially give one embedder access to a
249   // guest it does not own.
250   if (guest_instance_id > current_instance_id_)
251     return false;
252
253   GuestInstanceMap::const_iterator it =
254       guest_web_contents_by_instance_id_.find(guest_instance_id);
255   if (it == guest_web_contents_by_instance_id_.end())
256     return true;
257
258   GuestViewBase* guest_view = GuestViewBase::FromWebContents(it->second);
259   if (!guest_view)
260     return false;
261
262   return CanEmbedderAccessGuest(embedder_render_process_id, guest_view);
263 }
264
265 bool GuestViewManager::CanEmbedderAccessGuest(int embedder_render_process_id,
266                                               GuestViewBase* guest) {
267   // The embedder can access the guest if it has not been attached and its
268   // opener's embedder lives in the same process as the given embedder.
269   if (!guest->attached()) {
270     if (!guest->GetOpener())
271       return false;
272
273     return embedder_render_process_id ==
274         guest->GetOpener()->embedder_web_contents()->GetRenderProcessHost()->
275             GetID();
276   }
277
278   return embedder_render_process_id ==
279       guest->embedder_web_contents()->GetRenderProcessHost()->GetID();
280 }