Home | History | Annotate | Download | only in web_view
      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