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/web_view/web_view_permission_helper.h" 6 7 #include "content/public/browser/render_process_host.h" 8 #include "content/public/browser/render_view_host.h" 9 #include "content/public/browser/user_metrics.h" 10 #include "extensions/browser/api/extensions_api_client.h" 11 #include "extensions/browser/guest_view/web_view/web_view_constants.h" 12 #include "extensions/browser/guest_view/web_view/web_view_guest.h" 13 #include "extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h" 14 #include "extensions/browser/guest_view/web_view/web_view_permission_types.h" 15 16 using content::BrowserPluginGuestDelegate; 17 using content::RenderViewHost; 18 19 namespace extensions { 20 21 namespace { 22 static std::string PermissionTypeToString(WebViewPermissionType type) { 23 switch (type) { 24 case WEB_VIEW_PERMISSION_TYPE_DOWNLOAD: 25 return webview::kPermissionTypeDownload; 26 case WEB_VIEW_PERMISSION_TYPE_FILESYSTEM: 27 return webview::kPermissionTypeFileSystem; 28 case WEB_VIEW_PERMISSION_TYPE_GEOLOCATION: 29 return webview::kPermissionTypeGeolocation; 30 case WEB_VIEW_PERMISSION_TYPE_JAVASCRIPT_DIALOG: 31 return webview::kPermissionTypeDialog; 32 case WEB_VIEW_PERMISSION_TYPE_LOAD_PLUGIN: 33 return webview::kPermissionTypeLoadPlugin; 34 case WEB_VIEW_PERMISSION_TYPE_MEDIA: 35 return webview::kPermissionTypeMedia; 36 case WEB_VIEW_PERMISSION_TYPE_NEW_WINDOW: 37 return webview::kPermissionTypeNewWindow; 38 case WEB_VIEW_PERMISSION_TYPE_POINTER_LOCK: 39 return webview::kPermissionTypePointerLock; 40 default: 41 NOTREACHED(); 42 return std::string(); 43 } 44 } 45 46 // static 47 void RecordUserInitiatedUMA( 48 const WebViewPermissionHelper::PermissionResponseInfo& info, 49 bool allow) { 50 if (allow) { 51 // Note that |allow| == true means the embedder explicitly allowed the 52 // request. For some requests they might still fail. An example of such 53 // scenario would be: an embedder allows geolocation request but doesn't 54 // have geolocation access on its own. 55 switch (info.permission_type) { 56 case WEB_VIEW_PERMISSION_TYPE_DOWNLOAD: 57 content::RecordAction( 58 UserMetricsAction("WebView.PermissionAllow.Download")); 59 break; 60 case WEB_VIEW_PERMISSION_TYPE_FILESYSTEM: 61 content::RecordAction( 62 UserMetricsAction("WebView.PermissionAllow.FileSystem")); 63 break; 64 case WEB_VIEW_PERMISSION_TYPE_GEOLOCATION: 65 content::RecordAction( 66 UserMetricsAction("WebView.PermissionAllow.Geolocation")); 67 break; 68 case WEB_VIEW_PERMISSION_TYPE_JAVASCRIPT_DIALOG: 69 content::RecordAction( 70 UserMetricsAction("WebView.PermissionAllow.JSDialog")); 71 break; 72 case WEB_VIEW_PERMISSION_TYPE_LOAD_PLUGIN: 73 content::RecordAction( 74 UserMetricsAction("WebView.Guest.PermissionAllow.PluginLoad")); 75 case WEB_VIEW_PERMISSION_TYPE_MEDIA: 76 content::RecordAction( 77 UserMetricsAction("WebView.PermissionAllow.Media")); 78 break; 79 case WEB_VIEW_PERMISSION_TYPE_NEW_WINDOW: 80 content::RecordAction( 81 UserMetricsAction("BrowserPlugin.PermissionAllow.NewWindow")); 82 break; 83 case WEB_VIEW_PERMISSION_TYPE_POINTER_LOCK: 84 content::RecordAction( 85 UserMetricsAction("WebView.PermissionAllow.PointerLock")); 86 break; 87 default: 88 break; 89 } 90 } else { 91 switch (info.permission_type) { 92 case WEB_VIEW_PERMISSION_TYPE_DOWNLOAD: 93 content::RecordAction( 94 UserMetricsAction("WebView.PermissionDeny.Download")); 95 break; 96 case WEB_VIEW_PERMISSION_TYPE_FILESYSTEM: 97 content::RecordAction( 98 UserMetricsAction("WebView.PermissionDeny.FileSystem")); 99 break; 100 case WEB_VIEW_PERMISSION_TYPE_GEOLOCATION: 101 content::RecordAction( 102 UserMetricsAction("WebView.PermissionDeny.Geolocation")); 103 break; 104 case WEB_VIEW_PERMISSION_TYPE_JAVASCRIPT_DIALOG: 105 content::RecordAction( 106 UserMetricsAction("WebView.PermissionDeny.JSDialog")); 107 break; 108 case WEB_VIEW_PERMISSION_TYPE_LOAD_PLUGIN: 109 content::RecordAction( 110 UserMetricsAction("WebView.Guest.PermissionDeny.PluginLoad")); 111 break; 112 case WEB_VIEW_PERMISSION_TYPE_MEDIA: 113 content::RecordAction( 114 UserMetricsAction("WebView.PermissionDeny.Media")); 115 break; 116 case WEB_VIEW_PERMISSION_TYPE_NEW_WINDOW: 117 content::RecordAction( 118 UserMetricsAction("BrowserPlugin.PermissionDeny.NewWindow")); 119 break; 120 case WEB_VIEW_PERMISSION_TYPE_POINTER_LOCK: 121 content::RecordAction( 122 UserMetricsAction("WebView.PermissionDeny.PointerLock")); 123 break; 124 default: 125 break; 126 } 127 } 128 } 129 130 } // namespace 131 132 WebViewPermissionHelper::WebViewPermissionHelper(WebViewGuest* web_view_guest) 133 : content::WebContentsObserver(web_view_guest->web_contents()), 134 next_permission_request_id_(guestview::kInstanceIDNone), 135 web_view_guest_(web_view_guest), 136 weak_factory_(this) { 137 web_view_permission_helper_delegate_.reset( 138 ExtensionsAPIClient::Get()->CreateWebViewPermissionHelperDelegate( 139 this)); 140 } 141 142 WebViewPermissionHelper::~WebViewPermissionHelper() { 143 } 144 145 // static 146 WebViewPermissionHelper* WebViewPermissionHelper::FromFrameID( 147 int render_process_id, 148 int render_frame_id) { 149 WebViewGuest* web_view_guest = WebViewGuest::FromFrameID( 150 render_process_id, render_frame_id); 151 if (!web_view_guest) { 152 return NULL; 153 } 154 return web_view_guest->web_view_permission_helper_.get(); 155 } 156 157 // static 158 WebViewPermissionHelper* WebViewPermissionHelper::FromWebContents( 159 content::WebContents* web_contents) { 160 WebViewGuest* web_view_guest = WebViewGuest::FromWebContents(web_contents); 161 if (!web_view_guest) 162 return NULL; 163 return web_view_guest->web_view_permission_helper_.get(); 164 } 165 166 #if defined(ENABLE_PLUGINS) 167 bool WebViewPermissionHelper::OnMessageReceived( 168 const IPC::Message& message, 169 content::RenderFrameHost* render_frame_host) { 170 return web_view_permission_helper_delegate_->OnMessageReceived( 171 message, render_frame_host); 172 } 173 174 bool WebViewPermissionHelper::OnMessageReceived(const IPC::Message& message) { 175 return web_view_permission_helper_delegate_->OnMessageReceived(message); 176 } 177 #endif // defined(ENABLE_PLUGINS) 178 179 void WebViewPermissionHelper::RequestMediaAccessPermission( 180 content::WebContents* source, 181 const content::MediaStreamRequest& request, 182 const content::MediaResponseCallback& callback) { 183 base::DictionaryValue request_info; 184 request_info.SetString(guestview::kUrl, request.security_origin.spec()); 185 RequestPermission( 186 WEB_VIEW_PERMISSION_TYPE_MEDIA, 187 request_info, 188 base::Bind(&WebViewPermissionHelper::OnMediaPermissionResponse, 189 weak_factory_.GetWeakPtr(), 190 request, 191 callback), 192 false /* allowed_by_default */); 193 } 194 195 bool WebViewPermissionHelper::CheckMediaAccessPermission( 196 content::WebContents* source, 197 const GURL& security_origin, 198 content::MediaStreamType type) { 199 if (!web_view_guest()->attached() || 200 !web_view_guest()->embedder_web_contents()->GetDelegate()) { 201 return false; 202 } 203 return web_view_guest() 204 ->embedder_web_contents() 205 ->GetDelegate() 206 ->CheckMediaAccessPermission( 207 web_view_guest()->embedder_web_contents(), security_origin, type); 208 } 209 210 void WebViewPermissionHelper::OnMediaPermissionResponse( 211 const content::MediaStreamRequest& request, 212 const content::MediaResponseCallback& callback, 213 bool allow, 214 const std::string& user_input) { 215 if (!allow) { 216 callback.Run(content::MediaStreamDevices(), 217 content::MEDIA_DEVICE_PERMISSION_DENIED, 218 scoped_ptr<content::MediaStreamUI>()); 219 return; 220 } 221 if (!web_view_guest()->attached() || 222 !web_view_guest()->embedder_web_contents()->GetDelegate()) { 223 callback.Run(content::MediaStreamDevices(), 224 content::MEDIA_DEVICE_INVALID_STATE, 225 scoped_ptr<content::MediaStreamUI>()); 226 return; 227 } 228 229 web_view_guest() 230 ->embedder_web_contents() 231 ->GetDelegate() 232 ->RequestMediaAccessPermission( 233 web_view_guest()->embedder_web_contents(), request, callback); 234 } 235 236 void WebViewPermissionHelper::CanDownload( 237 content::RenderViewHost* render_view_host, 238 const GURL& url, 239 const std::string& request_method, 240 const base::Callback<void(bool)>& callback) { 241 web_view_permission_helper_delegate_->CanDownload( 242 render_view_host, url, request_method, callback); 243 } 244 245 void WebViewPermissionHelper::RequestPointerLockPermission( 246 bool user_gesture, 247 bool last_unlocked_by_target, 248 const base::Callback<void(bool)>& callback) { 249 web_view_permission_helper_delegate_->RequestPointerLockPermission( 250 user_gesture, last_unlocked_by_target, callback); 251 } 252 253 void WebViewPermissionHelper::RequestGeolocationPermission( 254 int bridge_id, 255 const GURL& requesting_frame, 256 bool user_gesture, 257 const base::Callback<void(bool)>& callback) { 258 web_view_permission_helper_delegate_->RequestGeolocationPermission( 259 bridge_id, requesting_frame, user_gesture, callback); 260 } 261 262 void WebViewPermissionHelper::CancelGeolocationPermissionRequest( 263 int bridge_id) { 264 web_view_permission_helper_delegate_->CancelGeolocationPermissionRequest( 265 bridge_id); 266 } 267 268 void WebViewPermissionHelper::RequestFileSystemPermission( 269 const GURL& url, 270 bool allowed_by_default, 271 const base::Callback<void(bool)>& callback) { 272 web_view_permission_helper_delegate_->RequestFileSystemPermission( 273 url, allowed_by_default, callback); 274 } 275 276 void WebViewPermissionHelper::FileSystemAccessedAsync(int render_process_id, 277 int render_frame_id, 278 int request_id, 279 const GURL& url, 280 bool blocked_by_policy) { 281 web_view_permission_helper_delegate_->FileSystemAccessedAsync( 282 render_process_id, render_frame_id, request_id, url, blocked_by_policy); 283 } 284 285 void WebViewPermissionHelper::FileSystemAccessedSync(int render_process_id, 286 int render_frame_id, 287 const GURL& url, 288 bool blocked_by_policy, 289 IPC::Message* reply_msg) { 290 web_view_permission_helper_delegate_->FileSystemAccessedSync( 291 render_process_id, render_frame_id, url, blocked_by_policy, reply_msg); 292 } 293 294 int WebViewPermissionHelper::RequestPermission( 295 WebViewPermissionType permission_type, 296 const base::DictionaryValue& request_info, 297 const PermissionResponseCallback& callback, 298 bool allowed_by_default) { 299 // If there are too many pending permission requests then reject this request. 300 if (pending_permission_requests_.size() >= 301 webview::kMaxOutstandingPermissionRequests) { 302 // Let the stack unwind before we deny the permission request so that 303 // objects held by the permission request are not destroyed immediately 304 // after creation. This is to allow those same objects to be accessed again 305 // in the same scope without fear of use after freeing. 306 base::MessageLoop::current()->PostTask( 307 FROM_HERE, 308 base::Bind(&PermissionResponseCallback::Run, 309 base::Owned(new PermissionResponseCallback(callback)), 310 allowed_by_default, 311 std::string())); 312 return webview::kInvalidPermissionRequestID; 313 } 314 315 int request_id = next_permission_request_id_++; 316 pending_permission_requests_[request_id] = 317 PermissionResponseInfo(callback, permission_type, allowed_by_default); 318 scoped_ptr<base::DictionaryValue> args(request_info.DeepCopy()); 319 args->SetInteger(webview::kRequestId, request_id); 320 switch (permission_type) { 321 case WEB_VIEW_PERMISSION_TYPE_NEW_WINDOW: { 322 web_view_guest_->DispatchEventToEmbedder( 323 new GuestViewBase::Event(webview::kEventNewWindow, args.Pass())); 324 break; 325 } 326 case WEB_VIEW_PERMISSION_TYPE_JAVASCRIPT_DIALOG: { 327 web_view_guest_->DispatchEventToEmbedder( 328 new GuestViewBase::Event(webview::kEventDialog, args.Pass())); 329 break; 330 } 331 default: { 332 args->SetString(webview::kPermission, 333 PermissionTypeToString(permission_type)); 334 web_view_guest_->DispatchEventToEmbedder(new GuestViewBase::Event( 335 webview::kEventPermissionRequest, 336 args.Pass())); 337 break; 338 } 339 } 340 return request_id; 341 } 342 343 WebViewPermissionHelper::SetPermissionResult 344 WebViewPermissionHelper::SetPermission( 345 int request_id, 346 PermissionResponseAction action, 347 const std::string& user_input) { 348 RequestMap::iterator request_itr = 349 pending_permission_requests_.find(request_id); 350 351 if (request_itr == pending_permission_requests_.end()) 352 return SET_PERMISSION_INVALID; 353 354 const PermissionResponseInfo& info = request_itr->second; 355 bool allow = (action == ALLOW) || 356 ((action == DEFAULT) && info.allowed_by_default); 357 358 info.callback.Run(allow, user_input); 359 360 // Only record user initiated (i.e. non-default) actions. 361 if (action != DEFAULT) 362 RecordUserInitiatedUMA(info, allow); 363 364 pending_permission_requests_.erase(request_itr); 365 366 return allow ? SET_PERMISSION_ALLOWED : SET_PERMISSION_DENIED; 367 } 368 369 void WebViewPermissionHelper::CancelPendingPermissionRequest(int request_id) { 370 RequestMap::iterator request_itr = 371 pending_permission_requests_.find(request_id); 372 373 if (request_itr == pending_permission_requests_.end()) 374 return; 375 376 pending_permission_requests_.erase(request_itr); 377 } 378 379 WebViewPermissionHelper::PermissionResponseInfo::PermissionResponseInfo() 380 : permission_type(WEB_VIEW_PERMISSION_TYPE_UNKNOWN), 381 allowed_by_default(false) { 382 } 383 384 WebViewPermissionHelper::PermissionResponseInfo::PermissionResponseInfo( 385 const PermissionResponseCallback& callback, 386 WebViewPermissionType permission_type, 387 bool allowed_by_default) 388 : callback(callback), 389 permission_type(permission_type), 390 allowed_by_default(allowed_by_default) { 391 } 392 393 WebViewPermissionHelper::PermissionResponseInfo::~PermissionResponseInfo() { 394 } 395 396 } // namespace extensions 397