Upstream version 7.36.149.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 "chrome/browser/extensions/extension_service.h"
8 #include "chrome/browser/guest_view/guest_view_base.h"
9 #include "chrome/browser/guest_view/guest_view_constants.h"
10 #include "chrome/browser/profiles/profile.h"
11 #include "content/public/browser/browser_context.h"
12 #include "content/public/browser/render_process_host.h"
13 #include "content/public/browser/user_metrics.h"
14 #include "content/public/browser/web_contents_observer.h"
15 #include "content/public/common/result_codes.h"
16 #include "extensions/browser/extension_system.h"
17 #include "url/gurl.h"
18
19 using content::BrowserContext;
20 using content::SiteInstance;
21 using content::WebContents;
22
23 // A WebContents does not immediately have a RenderProcessHost. It acquires one
24 // on initial navigation. This observer exists until that initial navigation in
25 // order to grab the ID if tis RenderProcessHost so that it can register it as
26 // a guest.
27 class GuestWebContentsObserver
28     : public content::WebContentsObserver {
29  public:
30   explicit GuestWebContentsObserver(WebContents* guest_web_contents)
31       : WebContentsObserver(guest_web_contents) {
32   }
33
34   virtual ~GuestWebContentsObserver() {
35   }
36
37   // WebContentsObserver:
38   virtual void DidStartProvisionalLoadForFrame(
39       int64 frame_id,
40       int64 parent_frame_id,
41       bool is_main_frame,
42       const GURL& validated_url,
43       bool is_error_page,
44       bool is_iframe_srcdoc,
45       content::RenderViewHost* render_view_host) OVERRIDE {
46     GuestViewManager::FromBrowserContext(web_contents()->GetBrowserContext())->
47         AddRenderProcessHostID(web_contents()->GetRenderProcessHost()->GetID());
48     delete this;
49   }
50
51   virtual void WebContentsDestroyed() OVERRIDE {
52     delete this;
53   }
54
55  private:
56   DISALLOW_COPY_AND_ASSIGN(GuestWebContentsObserver);
57 };
58
59 GuestViewManager::GuestViewManager(content::BrowserContext* context)
60     : current_instance_id_(0),
61       context_(context) {}
62
63 GuestViewManager::~GuestViewManager() {}
64
65 // static.
66 GuestViewManager* GuestViewManager::FromBrowserContext(
67     BrowserContext* context) {
68   GuestViewManager* guest_manager =
69       static_cast<GuestViewManager*>(context->GetUserData(
70           guestview::kGuestViewManagerKeyName));
71   if (!guest_manager) {
72     guest_manager = new GuestViewManager(context);
73     context->SetUserData(guestview::kGuestViewManagerKeyName, guest_manager);
74   }
75   return guest_manager;
76 }
77
78 content::WebContents* GuestViewManager::GetGuestByInstanceIDSafely(
79     int guest_instance_id,
80     int embedder_render_process_id) {
81   if (!CanEmbedderAccessInstanceIDMaybeKill(embedder_render_process_id,
82                                             guest_instance_id)) {
83     return NULL;
84   }
85   return GetGuestByInstanceID(guest_instance_id, embedder_render_process_id);
86 }
87
88 int GuestViewManager::GetNextInstanceID() {
89   return ++current_instance_id_;
90 }
91
92 void GuestViewManager::AddGuest(int guest_instance_id,
93                                 WebContents* guest_web_contents) {
94   DCHECK(guest_web_contents_by_instance_id_.find(guest_instance_id) ==
95          guest_web_contents_by_instance_id_.end());
96   guest_web_contents_by_instance_id_[guest_instance_id] = guest_web_contents;
97   // This will add the RenderProcessHost ID when we get one.
98   new GuestWebContentsObserver(guest_web_contents);
99 }
100
101 void GuestViewManager::RemoveGuest(int guest_instance_id) {
102   GuestInstanceMap::iterator it =
103       guest_web_contents_by_instance_id_.find(guest_instance_id);
104   DCHECK(it != guest_web_contents_by_instance_id_.end());
105   render_process_host_id_multiset_.erase(
106       it->second->GetRenderProcessHost()->GetID());
107   guest_web_contents_by_instance_id_.erase(it);
108 }
109
110 void GuestViewManager::MaybeGetGuestByInstanceIDOrKill(
111     int guest_instance_id,
112     int embedder_render_process_id,
113     const GuestByInstanceIDCallback& callback) {
114   if (!CanEmbedderAccessInstanceIDMaybeKill(embedder_render_process_id,
115                                             guest_instance_id)) {
116     // If we kill the embedder, then don't bother calling back.
117     return;
118   }
119   content::WebContents* guest_web_contents =
120       GetGuestByInstanceID(guest_instance_id, embedder_render_process_id);
121   callback.Run(guest_web_contents);
122 }
123
124 SiteInstance* GuestViewManager::GetGuestSiteInstance(
125     const GURL& guest_site) {
126   for (GuestInstanceMap::const_iterator it =
127        guest_web_contents_by_instance_id_.begin();
128        it != guest_web_contents_by_instance_id_.end(); ++it) {
129     if (it->second->GetSiteInstance()->GetSiteURL() == guest_site)
130       return it->second->GetSiteInstance();
131   }
132   return NULL;
133 }
134
135 bool GuestViewManager::ForEachGuest(WebContents* embedder_web_contents,
136                                     const GuestCallback& callback) {
137   for (GuestInstanceMap::iterator it =
138            guest_web_contents_by_instance_id_.begin();
139        it != guest_web_contents_by_instance_id_.end(); ++it) {
140     WebContents* guest = it->second;
141     if (embedder_web_contents != guest->GetEmbedderWebContents())
142       continue;
143
144     if (callback.Run(guest))
145       return true;
146   }
147   return false;
148 }
149
150 void GuestViewManager::AddRenderProcessHostID(int render_process_host_id) {
151   render_process_host_id_multiset_.insert(render_process_host_id);
152 }
153
154 content::WebContents* GuestViewManager::GetGuestByInstanceID(
155     int guest_instance_id,
156     int embedder_render_process_id) {
157   GuestInstanceMap::const_iterator it =
158       guest_web_contents_by_instance_id_.find(guest_instance_id);
159   if (it == guest_web_contents_by_instance_id_.end())
160     return NULL;
161   return it->second;
162 }
163
164 bool GuestViewManager::CanEmbedderAccessInstanceIDMaybeKill(
165     int embedder_render_process_id,
166     int guest_instance_id) {
167   if (!CanEmbedderAccessInstanceID(embedder_render_process_id,
168                                   guest_instance_id)) {
169     // The embedder process is trying to access a guest it does not own.
170     content::RecordAction(
171         base::UserMetricsAction("BadMessageTerminate_BPGM"));
172     base::KillProcess(
173         content::RenderProcessHost::FromID(embedder_render_process_id)->
174             GetHandle(),
175         content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
176     return false;
177   }
178   return true;
179 }
180
181 bool GuestViewManager::CanEmbedderAccessInstanceID(
182     int embedder_render_process_id,
183     int guest_instance_id) {
184   // The embedder is trying to access a guest with a negative or zero
185   // instance ID.
186   if (guest_instance_id <= guestview::kInstanceIDNone)
187     return false;
188
189   // The embedder is trying to access an instance ID that has not yet been
190   // allocated by GuestViewManager. This could cause instance ID
191   // collisions in the future, and potentially give one embedder access to a
192   // guest it does not own.
193   if (guest_instance_id > current_instance_id_)
194     return false;
195
196   GuestInstanceMap::const_iterator it =
197       guest_web_contents_by_instance_id_.find(guest_instance_id);
198   if (it == guest_web_contents_by_instance_id_.end())
199     return true;
200
201   GuestViewBase* guest_view = GuestViewBase::FromWebContents(it->second);
202   if (!guest_view)
203     return false;
204
205   return CanEmbedderAccessGuest(embedder_render_process_id, guest_view);
206 }
207
208 bool GuestViewManager::CanEmbedderAccessGuest(int embedder_render_process_id,
209                                               GuestViewBase* guest) {
210   // The embedder can access the guest if it has not been attached and its
211   // opener's embedder lives in the same process as the given embedder.
212   if (!guest->attached()) {
213     if (!guest->GetOpener())
214       return false;
215
216     return embedder_render_process_id ==
217         guest->GetOpener()->GetEmbedderWebContents()->GetRenderProcessHost()->
218             GetID();
219   }
220
221   return embedder_render_process_id ==
222       guest->embedder_web_contents()->GetRenderProcessHost()->GetID();
223 }