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.
5 #include "extensions/browser/guest_view/web_view/web_view_permission_helper.h"
7 #include "components/guest_view/browser/guest_view_event.h"
8 #include "content/public/browser/render_process_host.h"
9 #include "content/public/browser/render_view_host.h"
10 #include "content/public/browser/user_metrics.h"
11 #include "extensions/browser/api/extensions_api_client.h"
12 #include "extensions/browser/guest_view/web_view/web_view_constants.h"
13 #include "extensions/browser/guest_view/web_view/web_view_guest.h"
14 #include "extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h"
15 #include "extensions/browser/guest_view/web_view/web_view_permission_types.h"
17 using content::BrowserPluginGuestDelegate
;
18 using content::RenderViewHost
;
19 using guest_view::GuestViewEvent
;
21 namespace extensions
{
24 static std::string
PermissionTypeToString(WebViewPermissionType type
) {
26 case WEB_VIEW_PERMISSION_TYPE_DOWNLOAD
:
27 return webview::kPermissionTypeDownload
;
28 case WEB_VIEW_PERMISSION_TYPE_FILESYSTEM
:
29 return webview::kPermissionTypeFileSystem
;
30 case WEB_VIEW_PERMISSION_TYPE_FULLSCREEN
:
31 return webview::kPermissionTypeFullscreen
;
32 case WEB_VIEW_PERMISSION_TYPE_GEOLOCATION
:
33 return webview::kPermissionTypeGeolocation
;
34 case WEB_VIEW_PERMISSION_TYPE_JAVASCRIPT_DIALOG
:
35 return webview::kPermissionTypeDialog
;
36 case WEB_VIEW_PERMISSION_TYPE_LOAD_PLUGIN
:
37 return webview::kPermissionTypeLoadPlugin
;
38 case WEB_VIEW_PERMISSION_TYPE_MEDIA
:
39 return webview::kPermissionTypeMedia
;
40 case WEB_VIEW_PERMISSION_TYPE_NEW_WINDOW
:
41 return webview::kPermissionTypeNewWindow
;
42 case WEB_VIEW_PERMISSION_TYPE_POINTER_LOCK
:
43 return webview::kPermissionTypePointerLock
;
51 void RecordUserInitiatedUMA(
52 const WebViewPermissionHelper::PermissionResponseInfo
& info
,
55 // Note that |allow| == true means the embedder explicitly allowed the
56 // request. For some requests they might still fail. An example of such
57 // scenario would be: an embedder allows geolocation request but doesn't
58 // have geolocation access on its own.
59 switch (info
.permission_type
) {
60 case WEB_VIEW_PERMISSION_TYPE_DOWNLOAD
:
61 content::RecordAction(
62 UserMetricsAction("WebView.PermissionAllow.Download"));
64 case WEB_VIEW_PERMISSION_TYPE_FILESYSTEM
:
65 content::RecordAction(
66 UserMetricsAction("WebView.PermissionAllow.FileSystem"));
68 case WEB_VIEW_PERMISSION_TYPE_FULLSCREEN
:
69 content::RecordAction(
70 UserMetricsAction("WebView.PermissionAllow.Fullscreen"));
72 case WEB_VIEW_PERMISSION_TYPE_GEOLOCATION
:
73 content::RecordAction(
74 UserMetricsAction("WebView.PermissionAllow.Geolocation"));
76 case WEB_VIEW_PERMISSION_TYPE_JAVASCRIPT_DIALOG
:
77 content::RecordAction(
78 UserMetricsAction("WebView.PermissionAllow.JSDialog"));
80 case WEB_VIEW_PERMISSION_TYPE_LOAD_PLUGIN
:
81 content::RecordAction(
82 UserMetricsAction("WebView.Guest.PermissionAllow.PluginLoad"));
83 case WEB_VIEW_PERMISSION_TYPE_MEDIA
:
84 content::RecordAction(
85 UserMetricsAction("WebView.PermissionAllow.Media"));
87 case WEB_VIEW_PERMISSION_TYPE_NEW_WINDOW
:
88 content::RecordAction(
89 UserMetricsAction("BrowserPlugin.PermissionAllow.NewWindow"));
91 case WEB_VIEW_PERMISSION_TYPE_POINTER_LOCK
:
92 content::RecordAction(
93 UserMetricsAction("WebView.PermissionAllow.PointerLock"));
99 switch (info
.permission_type
) {
100 case WEB_VIEW_PERMISSION_TYPE_DOWNLOAD
:
101 content::RecordAction(
102 UserMetricsAction("WebView.PermissionDeny.Download"));
104 case WEB_VIEW_PERMISSION_TYPE_FILESYSTEM
:
105 content::RecordAction(
106 UserMetricsAction("WebView.PermissionDeny.FileSystem"));
108 case WEB_VIEW_PERMISSION_TYPE_FULLSCREEN
:
109 content::RecordAction(
110 UserMetricsAction("WebView.PermissionDeny.Fullscreen"));
112 case WEB_VIEW_PERMISSION_TYPE_GEOLOCATION
:
113 content::RecordAction(
114 UserMetricsAction("WebView.PermissionDeny.Geolocation"));
116 case WEB_VIEW_PERMISSION_TYPE_JAVASCRIPT_DIALOG
:
117 content::RecordAction(
118 UserMetricsAction("WebView.PermissionDeny.JSDialog"));
120 case WEB_VIEW_PERMISSION_TYPE_LOAD_PLUGIN
:
121 content::RecordAction(
122 UserMetricsAction("WebView.Guest.PermissionDeny.PluginLoad"));
124 case WEB_VIEW_PERMISSION_TYPE_MEDIA
:
125 content::RecordAction(
126 UserMetricsAction("WebView.PermissionDeny.Media"));
128 case WEB_VIEW_PERMISSION_TYPE_NEW_WINDOW
:
129 content::RecordAction(
130 UserMetricsAction("BrowserPlugin.PermissionDeny.NewWindow"));
132 case WEB_VIEW_PERMISSION_TYPE_POINTER_LOCK
:
133 content::RecordAction(
134 UserMetricsAction("WebView.PermissionDeny.PointerLock"));
144 WebViewPermissionHelper::WebViewPermissionHelper(WebViewGuest
* web_view_guest
)
145 : content::WebContentsObserver(web_view_guest
->web_contents()),
146 next_permission_request_id_(guest_view::kInstanceIDNone
),
147 web_view_guest_(web_view_guest
),
148 weak_factory_(this) {
149 web_view_permission_helper_delegate_
.reset(
150 ExtensionsAPIClient::Get()->CreateWebViewPermissionHelperDelegate(
154 WebViewPermissionHelper::~WebViewPermissionHelper() {
158 WebViewPermissionHelper
* WebViewPermissionHelper::FromFrameID(
159 int render_process_id
,
160 int render_frame_id
) {
161 WebViewGuest
* web_view_guest
= WebViewGuest::FromFrameID(
162 render_process_id
, render_frame_id
);
163 if (!web_view_guest
) {
166 return web_view_guest
->web_view_permission_helper_
.get();
170 WebViewPermissionHelper
* WebViewPermissionHelper::FromWebContents(
171 content::WebContents
* web_contents
) {
172 WebViewGuest
* web_view_guest
= WebViewGuest::FromWebContents(web_contents
);
175 return web_view_guest
->web_view_permission_helper_
.get();
178 #if defined(ENABLE_PLUGINS)
179 bool WebViewPermissionHelper::OnMessageReceived(
180 const IPC::Message
& message
,
181 content::RenderFrameHost
* render_frame_host
) {
182 return web_view_permission_helper_delegate_
->OnMessageReceived(
183 message
, render_frame_host
);
186 bool WebViewPermissionHelper::OnMessageReceived(const IPC::Message
& message
) {
187 return web_view_permission_helper_delegate_
->OnMessageReceived(message
);
189 #endif // defined(ENABLE_PLUGINS)
191 void WebViewPermissionHelper::RequestMediaAccessPermission(
192 content::WebContents
* source
,
193 const content::MediaStreamRequest
& request
,
194 const content::MediaResponseCallback
& callback
) {
195 base::DictionaryValue request_info
;
196 request_info
.SetString(guest_view::kUrl
, request
.security_origin
.spec());
198 WEB_VIEW_PERMISSION_TYPE_MEDIA
,
200 base::Bind(&WebViewPermissionHelper::OnMediaPermissionResponse
,
201 weak_factory_
.GetWeakPtr(),
204 false /* allowed_by_default */);
207 bool WebViewPermissionHelper::CheckMediaAccessPermission(
208 content::WebContents
* source
,
209 const GURL
& security_origin
,
210 content::MediaStreamType type
) {
211 if (!web_view_guest()->attached() ||
212 !web_view_guest()->embedder_web_contents()->GetDelegate()) {
215 return web_view_guest()
216 ->embedder_web_contents()
218 ->CheckMediaAccessPermission(
219 web_view_guest()->embedder_web_contents(), security_origin
, type
);
222 void WebViewPermissionHelper::OnMediaPermissionResponse(
223 const content::MediaStreamRequest
& request
,
224 const content::MediaResponseCallback
& callback
,
226 const std::string
& user_input
) {
228 callback
.Run(content::MediaStreamDevices(),
229 content::MEDIA_DEVICE_PERMISSION_DENIED
,
230 scoped_ptr
<content::MediaStreamUI
>());
233 if (!web_view_guest()->attached() ||
234 !web_view_guest()->embedder_web_contents()->GetDelegate()) {
235 callback
.Run(content::MediaStreamDevices(),
236 content::MEDIA_DEVICE_INVALID_STATE
,
237 scoped_ptr
<content::MediaStreamUI
>());
242 ->embedder_web_contents()
244 ->RequestMediaAccessPermission(
245 web_view_guest()->embedder_web_contents(), request
, callback
);
248 void WebViewPermissionHelper::CanDownload(
250 const std::string
& request_method
,
251 const base::Callback
<void(bool)>& callback
) {
252 web_view_permission_helper_delegate_
->CanDownload(url
, request_method
,
256 void WebViewPermissionHelper::RequestPointerLockPermission(
258 bool last_unlocked_by_target
,
259 const base::Callback
<void(bool)>& callback
) {
260 web_view_permission_helper_delegate_
->RequestPointerLockPermission(
261 user_gesture
, last_unlocked_by_target
, callback
);
264 void WebViewPermissionHelper::RequestGeolocationPermission(
266 const GURL
& requesting_frame
,
268 const base::Callback
<void(bool)>& callback
) {
269 web_view_permission_helper_delegate_
->RequestGeolocationPermission(
270 bridge_id
, requesting_frame
, user_gesture
, callback
);
273 void WebViewPermissionHelper::CancelGeolocationPermissionRequest(
275 web_view_permission_helper_delegate_
->CancelGeolocationPermissionRequest(
279 void WebViewPermissionHelper::RequestFileSystemPermission(
281 bool allowed_by_default
,
282 const base::Callback
<void(bool)>& callback
) {
283 web_view_permission_helper_delegate_
->RequestFileSystemPermission(
284 url
, allowed_by_default
, callback
);
287 void WebViewPermissionHelper::FileSystemAccessedAsync(int render_process_id
,
291 bool blocked_by_policy
) {
292 web_view_permission_helper_delegate_
->FileSystemAccessedAsync(
293 render_process_id
, render_frame_id
, request_id
, url
, blocked_by_policy
);
296 void WebViewPermissionHelper::FileSystemAccessedSync(int render_process_id
,
299 bool blocked_by_policy
,
300 IPC::Message
* reply_msg
) {
301 web_view_permission_helper_delegate_
->FileSystemAccessedSync(
302 render_process_id
, render_frame_id
, url
, blocked_by_policy
, reply_msg
);
305 int WebViewPermissionHelper::RequestPermission(
306 WebViewPermissionType permission_type
,
307 const base::DictionaryValue
& request_info
,
308 const PermissionResponseCallback
& callback
,
309 bool allowed_by_default
) {
310 // If there are too many pending permission requests then reject this request.
311 if (pending_permission_requests_
.size() >=
312 webview::kMaxOutstandingPermissionRequests
) {
313 // Let the stack unwind before we deny the permission request so that
314 // objects held by the permission request are not destroyed immediately
315 // after creation. This is to allow those same objects to be accessed again
316 // in the same scope without fear of use after freeing.
317 base::MessageLoop::current()->PostTask(
319 base::Bind(&PermissionResponseCallback::Run
,
320 base::Owned(new PermissionResponseCallback(callback
)),
323 return webview::kInvalidPermissionRequestID
;
326 int request_id
= next_permission_request_id_
++;
327 pending_permission_requests_
[request_id
] =
328 PermissionResponseInfo(callback
, permission_type
, allowed_by_default
);
329 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
330 args
->Set(webview::kRequestInfo
, request_info
.DeepCopy());
331 args
->SetInteger(webview::kRequestId
, request_id
);
332 switch (permission_type
) {
333 case WEB_VIEW_PERMISSION_TYPE_NEW_WINDOW
: {
334 web_view_guest_
->DispatchEventToView(
335 new GuestViewEvent(webview::kEventNewWindow
, args
.Pass()));
338 case WEB_VIEW_PERMISSION_TYPE_JAVASCRIPT_DIALOG
: {
339 web_view_guest_
->DispatchEventToView(
340 new GuestViewEvent(webview::kEventDialog
, args
.Pass()));
344 args
->SetString(webview::kPermission
,
345 PermissionTypeToString(permission_type
));
346 web_view_guest_
->DispatchEventToView(new GuestViewEvent(
347 webview::kEventPermissionRequest
, args
.Pass()));
354 WebViewPermissionHelper::SetPermissionResult
355 WebViewPermissionHelper::SetPermission(
357 PermissionResponseAction action
,
358 const std::string
& user_input
) {
359 RequestMap::iterator request_itr
=
360 pending_permission_requests_
.find(request_id
);
362 if (request_itr
== pending_permission_requests_
.end())
363 return SET_PERMISSION_INVALID
;
365 const PermissionResponseInfo
& info
= request_itr
->second
;
366 bool allow
= (action
== ALLOW
) ||
367 ((action
== DEFAULT
) && info
.allowed_by_default
);
369 info
.callback
.Run(allow
, user_input
);
371 // Only record user initiated (i.e. non-default) actions.
372 if (action
!= DEFAULT
)
373 RecordUserInitiatedUMA(info
, allow
);
375 pending_permission_requests_
.erase(request_itr
);
377 return allow
? SET_PERMISSION_ALLOWED
: SET_PERMISSION_DENIED
;
380 void WebViewPermissionHelper::CancelPendingPermissionRequest(int request_id
) {
381 RequestMap::iterator request_itr
=
382 pending_permission_requests_
.find(request_id
);
384 if (request_itr
== pending_permission_requests_
.end())
387 pending_permission_requests_
.erase(request_itr
);
390 WebViewPermissionHelper::PermissionResponseInfo::PermissionResponseInfo()
391 : permission_type(WEB_VIEW_PERMISSION_TYPE_UNKNOWN
),
392 allowed_by_default(false) {
395 WebViewPermissionHelper::PermissionResponseInfo::PermissionResponseInfo(
396 const PermissionResponseCallback
& callback
,
397 WebViewPermissionType permission_type
,
398 bool allowed_by_default
)
399 : callback(callback
),
400 permission_type(permission_type
),
401 allowed_by_default(allowed_by_default
) {
404 WebViewPermissionHelper::PermissionResponseInfo::~PermissionResponseInfo() {
407 } // namespace extensions