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
22using content::BrowserContext;
23using content::SiteInstance;
24using content::WebContents;
25
26namespace extensions {
27
28// static
29GuestViewManagerFactory* GuestViewManager::factory_ = NULL;
30
31GuestViewManager::GuestViewManager(content::BrowserContext* context)
32    : current_instance_id_(0), last_instance_id_removed_(0), context_(context) {
33}
34
35GuestViewManager::~GuestViewManager() {}
36
37// static.
38GuestViewManager* 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
54content::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
64void 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
111int GuestViewManager::GetNextInstanceID() {
112  return ++current_instance_id_;
113}
114
115void 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
131content::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
151content::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
162int 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
172SiteInstance* 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
183bool 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
199void 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
206void 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
244content::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
253bool 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
270bool 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
276bool 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