Home | History | Annotate | Download | only in geolocation
      1 // Copyright (c) 2012 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/geolocation/chrome_geolocation_permission_context.h"
      6 
      7 #include <functional>
      8 #include <string>
      9 #include <vector>
     10 
     11 #include "base/bind.h"
     12 #include "base/strings/utf_string_conversions.h"
     13 #include "chrome/browser/content_settings/host_content_settings_map.h"
     14 #include "chrome/browser/content_settings/permission_request_id.h"
     15 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
     16 #include "chrome/browser/extensions/extension_service.h"
     17 #include "chrome/browser/extensions/extension_system.h"
     18 #include "chrome/browser/extensions/suggest_permission_util.h"
     19 #include "chrome/browser/profiles/profile.h"
     20 #include "chrome/browser/tab_contents/tab_util.h"
     21 #include "chrome/common/extensions/extension.h"
     22 #include "content/public/browser/browser_thread.h"
     23 #include "content/public/browser/render_view_host.h"
     24 #include "content/public/browser/web_contents.h"
     25 #include "extensions/browser/view_type_utils.h"
     26 
     27 using extensions::APIPermission;
     28 
     29 ChromeGeolocationPermissionContext::ChromeGeolocationPermissionContext(
     30     Profile* profile)
     31     : profile_(profile),
     32       shutting_down_(false) {
     33 }
     34 
     35 ChromeGeolocationPermissionContext::~ChromeGeolocationPermissionContext() {
     36   // ChromeGeolocationPermissionContext may be destroyed on either the UI thread
     37   // or the IO thread, but the PermissionQueueController must have been
     38   // destroyed on the UI thread.
     39   DCHECK(!permission_queue_controller_.get());
     40 }
     41 
     42 void ChromeGeolocationPermissionContext::RequestGeolocationPermission(
     43     int render_process_id,
     44     int render_view_id,
     45     int bridge_id,
     46     const GURL& requesting_frame,
     47     base::Callback<void(bool)> callback) {
     48   if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
     49     content::BrowserThread::PostTask(
     50         content::BrowserThread::UI, FROM_HERE,
     51         base::Bind(
     52             &ChromeGeolocationPermissionContext::RequestGeolocationPermission,
     53             this, render_process_id, render_view_id, bridge_id,
     54             requesting_frame, callback));
     55     return;
     56   }
     57 
     58   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
     59   if (shutting_down_)
     60     return;
     61 
     62   content::WebContents* web_contents =
     63       tab_util::GetWebContentsByID(render_process_id, render_view_id);
     64   const PermissionRequestID id(render_process_id, render_view_id,
     65                                           bridge_id);
     66   ExtensionService* extension_service =
     67       extensions::ExtensionSystem::Get(profile_)->extension_service();
     68   if (extension_service) {
     69     const extensions::Extension* extension =
     70         extension_service->extensions()->GetExtensionOrAppByURL(
     71             requesting_frame);
     72     if (IsExtensionWithPermissionOrSuggestInConsole(APIPermission::kGeolocation,
     73                                                     extension,
     74                                                     profile_)) {
     75       // Make sure the extension is in the calling process.
     76       if (extension_service->process_map()->Contains(extension->id(),
     77                                                      id.render_process_id())) {
     78         NotifyPermissionSet(id, requesting_frame, callback, true);
     79         return;
     80       }
     81     }
     82   }
     83 
     84   if (extensions::GetViewType(web_contents) !=
     85       extensions::VIEW_TYPE_TAB_CONTENTS) {
     86     // The tab may have gone away, or the request may not be from a tab at all.
     87     // TODO(mpcomplete): the request could be from a background page or
     88     // extension popup (web_contents will have a different ViewType). But why do
     89     // we care? Shouldn't we still put an infobar up in the current tab?
     90     LOG(WARNING) << "Attempt to use geolocation tabless renderer: "
     91                  << id.ToString()
     92                  << " (can't prompt user without a visible tab)";
     93     NotifyPermissionSet(id, requesting_frame, callback, false);
     94     return;
     95   }
     96 
     97   GURL embedder = web_contents->GetURL();
     98   if (!requesting_frame.is_valid() || !embedder.is_valid()) {
     99     LOG(WARNING) << "Attempt to use geolocation from an invalid URL: "
    100                  << requesting_frame << "," << embedder
    101                  << " (geolocation is not supported in popups)";
    102     NotifyPermissionSet(id, requesting_frame, callback, false);
    103     return;
    104   }
    105 
    106   DecidePermission(id, requesting_frame, embedder, callback);
    107 }
    108 
    109 void ChromeGeolocationPermissionContext::CancelGeolocationPermissionRequest(
    110     int render_process_id,
    111     int render_view_id,
    112     int bridge_id,
    113     const GURL& requesting_frame) {
    114   CancelPendingInfoBarRequest(PermissionRequestID(
    115       render_process_id, render_view_id, bridge_id));
    116 }
    117 
    118 void ChromeGeolocationPermissionContext::DecidePermission(
    119     const PermissionRequestID& id,
    120     const GURL& requesting_frame,
    121     const GURL& embedder,
    122     base::Callback<void(bool)> callback) {
    123   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    124 
    125   ContentSetting content_setting =
    126      profile_->GetHostContentSettingsMap()->GetContentSetting(
    127           requesting_frame, embedder, CONTENT_SETTINGS_TYPE_GEOLOCATION,
    128           std::string());
    129   switch (content_setting) {
    130     case CONTENT_SETTING_BLOCK:
    131       PermissionDecided(id, requesting_frame, embedder, callback, false);
    132       break;
    133     case CONTENT_SETTING_ALLOW:
    134       PermissionDecided(id, requesting_frame, embedder, callback, true);
    135       break;
    136     default:
    137       // setting == ask. Prompt the user.
    138       QueueController()->CreateInfoBarRequest(
    139           id, requesting_frame, embedder, base::Bind(
    140               &ChromeGeolocationPermissionContext::NotifyPermissionSet,
    141               base::Unretained(this), id, requesting_frame, callback));
    142   }
    143 }
    144 
    145 void ChromeGeolocationPermissionContext::ShutdownOnUIThread() {
    146   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    147   permission_queue_controller_.reset();
    148   shutting_down_ = true;
    149 }
    150 
    151 void ChromeGeolocationPermissionContext::PermissionDecided(
    152     const PermissionRequestID& id,
    153     const GURL& requesting_frame,
    154     const GURL& embedder,
    155     base::Callback<void(bool)> callback,
    156     bool allowed) {
    157   NotifyPermissionSet(id, requesting_frame, callback, allowed);
    158 }
    159 
    160 void ChromeGeolocationPermissionContext::NotifyPermissionSet(
    161     const PermissionRequestID& id,
    162     const GURL& requesting_frame,
    163     base::Callback<void(bool)> callback,
    164     bool allowed) {
    165   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    166 
    167   // WebContents may have gone away (or not exists for extension).
    168   TabSpecificContentSettings* content_settings =
    169       TabSpecificContentSettings::Get(id.render_process_id(),
    170                                       id.render_view_id());
    171   if (content_settings) {
    172     content_settings->OnGeolocationPermissionSet(requesting_frame.GetOrigin(),
    173                                                  allowed);
    174   }
    175 
    176   callback.Run(allowed);
    177 }
    178 
    179 PermissionQueueController*
    180     ChromeGeolocationPermissionContext::QueueController() {
    181   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    182   DCHECK(!shutting_down_);
    183   if (!permission_queue_controller_)
    184     permission_queue_controller_.reset(CreateQueueController());
    185   return permission_queue_controller_.get();
    186 }
    187 
    188 PermissionQueueController*
    189     ChromeGeolocationPermissionContext::CreateQueueController() {
    190   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    191   return new PermissionQueueController(profile(),
    192                                        CONTENT_SETTINGS_TYPE_GEOLOCATION);
    193 }
    194 
    195 void ChromeGeolocationPermissionContext::CancelPendingInfoBarRequest(
    196     const PermissionRequestID& id) {
    197   if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
    198     content::BrowserThread::PostTask(
    199         content::BrowserThread::UI, FROM_HERE,
    200         base::Bind(
    201             &ChromeGeolocationPermissionContext::CancelPendingInfoBarRequest,
    202             this, id));
    203      return;
    204   }
    205   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    206   if (shutting_down_)
    207     return;
    208   QueueController()->CancelInfoBarRequest(id);
    209 }
    210