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/geolocation_permission_context.h"
      6 
      7 #include <functional>
      8 #include <string>
      9 #include <vector>
     10 
     11 #include "base/bind.h"
     12 #include "base/prefs/pref_service.h"
     13 #include "base/strings/utf_string_conversions.h"
     14 #include "chrome/browser/content_settings/host_content_settings_map.h"
     15 #include "chrome/browser/content_settings/permission_request_id.h"
     16 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
     17 #include "chrome/browser/profiles/profile.h"
     18 #include "chrome/browser/tab_contents/tab_util.h"
     19 #include "chrome/browser/ui/website_settings/permission_bubble_manager.h"
     20 #include "chrome/browser/ui/website_settings/permission_bubble_request.h"
     21 #include "chrome/common/pref_names.h"
     22 #include "content/public/browser/browser_thread.h"
     23 #include "content/public/browser/render_process_host.h"
     24 #include "content/public/browser/render_view_host.h"
     25 #include "content/public/browser/web_contents.h"
     26 #include "grit/generated_resources.h"
     27 #include "grit/theme_resources.h"
     28 #include "net/base/net_util.h"
     29 #include "ui/base/l10n/l10n_util.h"
     30 
     31 class GeolocationPermissionRequest : public PermissionBubbleRequest {
     32  public:
     33   GeolocationPermissionRequest(GeolocationPermissionContext* context,
     34                                const PermissionRequestID& id,
     35                                const GURL& requesting_frame,
     36                                bool user_gesture,
     37                                base::Callback<void(bool)> callback,
     38                                const std::string& display_languages);
     39   virtual ~GeolocationPermissionRequest();
     40 
     41   // PermissionBubbleDelegate:
     42   virtual int GetIconID() const OVERRIDE;
     43   virtual base::string16 GetMessageText() const OVERRIDE;
     44   virtual base::string16 GetMessageTextFragment() const OVERRIDE;
     45   virtual bool HasUserGesture() const OVERRIDE;
     46   virtual GURL GetRequestingHostname() const OVERRIDE;
     47   virtual void PermissionGranted() OVERRIDE;
     48   virtual void PermissionDenied() OVERRIDE;
     49   virtual void Cancelled() OVERRIDE;
     50   virtual void RequestFinished() OVERRIDE;
     51 
     52  private:
     53   GeolocationPermissionContext* context_;
     54   PermissionRequestID id_;
     55   GURL requesting_frame_;
     56   bool user_gesture_;
     57   base::Callback<void(bool)> callback_;
     58   std::string display_languages_;
     59 };
     60 
     61 GeolocationPermissionRequest::GeolocationPermissionRequest(
     62     GeolocationPermissionContext* context,
     63     const PermissionRequestID& id,
     64     const GURL& requesting_frame,
     65     bool user_gesture,
     66     base::Callback<void(bool)> callback,
     67     const std::string& display_languages)
     68     : context_(context),
     69       id_(id),
     70       requesting_frame_(requesting_frame),
     71       user_gesture_(user_gesture),
     72       callback_(callback),
     73       display_languages_(display_languages) {}
     74 
     75 GeolocationPermissionRequest::~GeolocationPermissionRequest() {}
     76 
     77 int GeolocationPermissionRequest::GetIconID() const {
     78   return IDR_INFOBAR_GEOLOCATION;
     79 }
     80 
     81 base::string16 GeolocationPermissionRequest::GetMessageText() const {
     82   return l10n_util::GetStringFUTF16(IDS_GEOLOCATION_INFOBAR_QUESTION,
     83       net::FormatUrl(requesting_frame_, display_languages_));
     84 }
     85 
     86 base::string16 GeolocationPermissionRequest::GetMessageTextFragment() const {
     87   return l10n_util::GetStringUTF16(IDS_GEOLOCATION_INFOBAR_PERMISSION_FRAGMENT);
     88 }
     89 
     90 bool GeolocationPermissionRequest::HasUserGesture() const {
     91   return user_gesture_;
     92 }
     93 
     94 GURL GeolocationPermissionRequest::GetRequestingHostname() const {
     95   return requesting_frame_;
     96 }
     97 
     98 void GeolocationPermissionRequest::PermissionGranted() {
     99   context_->NotifyPermissionSet(id_, requesting_frame_, callback_, true);
    100 }
    101 
    102 void GeolocationPermissionRequest::PermissionDenied() {
    103   context_->NotifyPermissionSet(id_, requesting_frame_, callback_, false);
    104 }
    105 
    106 void GeolocationPermissionRequest::Cancelled() {
    107 }
    108 
    109 void GeolocationPermissionRequest::RequestFinished() {
    110   // Deletes 'this'.
    111   context_->RequestFinished(this);
    112 }
    113 
    114 
    115 GeolocationPermissionContext::GeolocationPermissionContext(
    116     Profile* profile)
    117     : profile_(profile),
    118       shutting_down_(false),
    119       extensions_context_(profile) {
    120 }
    121 
    122 GeolocationPermissionContext::~GeolocationPermissionContext() {
    123   // GeolocationPermissionContext may be destroyed on either the UI thread
    124   // or the IO thread, but the PermissionQueueController must have been
    125   // destroyed on the UI thread.
    126   DCHECK(!permission_queue_controller_.get());
    127 }
    128 
    129 void GeolocationPermissionContext::RequestGeolocationPermission(
    130     content::WebContents* web_contents,
    131     int bridge_id,
    132     const GURL& requesting_frame,
    133     bool user_gesture,
    134     base::Callback<void(bool)> result_callback,
    135     base::Closure* cancel_callback) {
    136   GURL requesting_frame_origin = requesting_frame.GetOrigin();
    137   if (shutting_down_)
    138     return;
    139 
    140   int render_process_id = web_contents->GetRenderProcessHost()->GetID();
    141   int render_view_id = web_contents->GetRenderViewHost()->GetRoutingID();
    142   if (cancel_callback) {
    143     *cancel_callback = base::Bind(
    144         &GeolocationPermissionContext::CancelGeolocationPermissionRequest,
    145         this, render_process_id, render_view_id, bridge_id);
    146   }
    147 
    148   const PermissionRequestID id(
    149       render_process_id, render_view_id, bridge_id, GURL());
    150 
    151   bool permission_set;
    152   bool new_permission;
    153   if (extensions_context_.RequestPermission(
    154           web_contents, id, bridge_id, requesting_frame, user_gesture,
    155           result_callback, &permission_set, &new_permission)) {
    156     if (permission_set) {
    157       NotifyPermissionSet(id, requesting_frame_origin, result_callback,
    158                           new_permission);
    159     }
    160     return;
    161   }
    162 
    163   GURL embedder = web_contents->GetLastCommittedURL().GetOrigin();
    164   if (!requesting_frame_origin.is_valid() || !embedder.is_valid()) {
    165     LOG(WARNING) << "Attempt to use geolocation from an invalid URL: "
    166                  << requesting_frame_origin << "," << embedder
    167                  << " (geolocation is not supported in popups)";
    168     NotifyPermissionSet(id, requesting_frame_origin, result_callback, false);
    169     return;
    170   }
    171 
    172   DecidePermission(web_contents, id, requesting_frame_origin, user_gesture,
    173                    embedder, "", result_callback);
    174 }
    175 
    176 void GeolocationPermissionContext::CancelGeolocationPermissionRequest(
    177     int render_process_id,
    178     int render_view_id,
    179     int bridge_id) {
    180   content::WebContents* web_contents = tab_util::GetWebContentsByID(
    181         render_process_id, render_view_id);
    182   if (extensions_context_.CancelPermissionRequest(web_contents, bridge_id))
    183     return;
    184 
    185   CancelPendingInfobarRequest(PermissionRequestID(
    186       render_process_id, render_view_id, bridge_id, GURL()));
    187 }
    188 
    189 void GeolocationPermissionContext::DecidePermission(
    190     content::WebContents* web_contents,
    191     const PermissionRequestID& id,
    192     const GURL& requesting_frame,
    193     bool user_gesture,
    194     const GURL& embedder,
    195     const std::string& accept_button_label,
    196     base::Callback<void(bool)> callback) {
    197   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    198 
    199   ContentSetting content_setting =
    200      profile_->GetHostContentSettingsMap()->GetContentSetting(
    201           requesting_frame, embedder, CONTENT_SETTINGS_TYPE_GEOLOCATION,
    202           std::string());
    203   switch (content_setting) {
    204     case CONTENT_SETTING_BLOCK:
    205       PermissionDecided(id, requesting_frame, embedder, callback, false);
    206       break;
    207     case CONTENT_SETTING_ALLOW:
    208       PermissionDecided(id, requesting_frame, embedder, callback, true);
    209       break;
    210     default:
    211       if (PermissionBubbleManager::Enabled()) {
    212         PermissionBubbleManager* mgr =
    213             PermissionBubbleManager::FromWebContents(web_contents);
    214         if (mgr) {
    215           scoped_ptr<GeolocationPermissionRequest> request_ptr(
    216               new GeolocationPermissionRequest(
    217                   this, id, requesting_frame, user_gesture, callback,
    218                   profile_->GetPrefs()->GetString(prefs::kAcceptLanguages)));
    219           GeolocationPermissionRequest* request = request_ptr.get();
    220           pending_requests_.add(id.ToString(), request_ptr.Pass());
    221           mgr->AddRequest(request);
    222         }
    223       } else {
    224         // setting == ask. Prompt the user.
    225         QueueController()->CreateInfoBarRequest(
    226             id, requesting_frame, embedder, accept_button_label,
    227                 base::Bind(
    228                     &GeolocationPermissionContext::NotifyPermissionSet,
    229                 base::Unretained(this), id, requesting_frame, callback));
    230       }
    231   }
    232 }
    233 
    234 void GeolocationPermissionContext::CreateInfoBarRequest(
    235     const PermissionRequestID& id,
    236     const GURL& requesting_frame,
    237     const GURL& embedder,
    238     const std::string accept_button_label,
    239     base::Callback<void(bool)> callback) {
    240     QueueController()->CreateInfoBarRequest(
    241         id, requesting_frame, embedder, accept_button_label, base::Bind(
    242             &GeolocationPermissionContext::NotifyPermissionSet,
    243             base::Unretained(this), id, requesting_frame, callback));
    244 }
    245 
    246 void GeolocationPermissionContext::RequestFinished(
    247     GeolocationPermissionRequest* request) {
    248   base::ScopedPtrHashMap<std::string,
    249                          GeolocationPermissionRequest>::iterator it;
    250   for (it = pending_requests_.begin(); it != pending_requests_.end(); ++it) {
    251     if (it->second == request) {
    252       pending_requests_.take_and_erase(it);
    253       return;
    254     }
    255   }
    256 }
    257 
    258 
    259 void GeolocationPermissionContext::ShutdownOnUIThread() {
    260   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    261   permission_queue_controller_.reset();
    262   shutting_down_ = true;
    263 }
    264 
    265 void GeolocationPermissionContext::PermissionDecided(
    266     const PermissionRequestID& id,
    267     const GURL& requesting_frame,
    268     const GURL& embedder,
    269     base::Callback<void(bool)> callback,
    270     bool allowed) {
    271   NotifyPermissionSet(id, requesting_frame, callback, allowed);
    272 }
    273 
    274 void GeolocationPermissionContext::NotifyPermissionSet(
    275     const PermissionRequestID& id,
    276     const GURL& requesting_frame,
    277     base::Callback<void(bool)> callback,
    278     bool allowed) {
    279   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    280 
    281   // WebContents may have gone away (or not exists for extension).
    282   TabSpecificContentSettings* content_settings =
    283       TabSpecificContentSettings::Get(id.render_process_id(),
    284                                       id.render_view_id());
    285   if (content_settings) {
    286     content_settings->OnGeolocationPermissionSet(requesting_frame.GetOrigin(),
    287                                                  allowed);
    288   }
    289 
    290   callback.Run(allowed);
    291 }
    292 
    293 PermissionQueueController*
    294     GeolocationPermissionContext::QueueController() {
    295   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    296   DCHECK(!shutting_down_);
    297   if (!permission_queue_controller_)
    298     permission_queue_controller_.reset(CreateQueueController());
    299   return permission_queue_controller_.get();
    300 }
    301 
    302 PermissionQueueController*
    303     GeolocationPermissionContext::CreateQueueController() {
    304   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    305   return new PermissionQueueController(profile(),
    306                                        CONTENT_SETTINGS_TYPE_GEOLOCATION);
    307 }
    308 
    309 void GeolocationPermissionContext::CancelPendingInfobarRequest(
    310     const PermissionRequestID& id) {
    311   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    312   if (shutting_down_)
    313     return;
    314 
    315   if (PermissionBubbleManager::Enabled()) {
    316     GeolocationPermissionRequest* cancelling =
    317         pending_requests_.get(id.ToString());
    318     content::WebContents* web_contents = tab_util::GetWebContentsByID(
    319         id.render_process_id(), id.render_view_id());
    320     if (cancelling != NULL && web_contents != NULL &&
    321         PermissionBubbleManager::FromWebContents(web_contents) != NULL) {
    322       PermissionBubbleManager::FromWebContents(web_contents)->
    323           CancelRequest(cancelling);
    324     }
    325     return;
    326   }
    327 
    328   QueueController()->CancelInfoBarRequest(id);
    329 }
    330