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 "content/browser/geolocation/geolocation_dispatcher_host.h"
      6 
      7 #include <utility>
      8 
      9 #include "base/bind.h"
     10 #include "base/metrics/histogram.h"
     11 #include "content/browser/frame_host/render_frame_host_impl.h"
     12 #include "content/browser/renderer_host/render_message_filter.h"
     13 #include "content/browser/renderer_host/render_process_host_impl.h"
     14 #include "content/browser/renderer_host/render_view_host_impl.h"
     15 #include "content/browser/web_contents/web_contents_impl.h"
     16 #include "content/public/browser/browser_context.h"
     17 #include "content/public/browser/content_browser_client.h"
     18 #include "content/public/common/geoposition.h"
     19 #include "content/common/geolocation_messages.h"
     20 
     21 namespace content {
     22 namespace {
     23 
     24 // Geoposition error codes for reporting in UMA.
     25 enum GeopositionErrorCode {
     26   // NOTE: Do not renumber these as that would confuse interpretation of
     27   // previously logged data. When making changes, also update the enum list
     28   // in tools/metrics/histograms/histograms.xml to keep it in sync.
     29 
     30   // There was no error.
     31   GEOPOSITION_ERROR_CODE_NONE = 0,
     32 
     33   // User denied use of geolocation.
     34   GEOPOSITION_ERROR_CODE_PERMISSION_DENIED = 1,
     35 
     36   // Geoposition could not be determined.
     37   GEOPOSITION_ERROR_CODE_POSITION_UNAVAILABLE = 2,
     38 
     39   // Timeout.
     40   GEOPOSITION_ERROR_CODE_TIMEOUT = 3,
     41 
     42   // NOTE: Add entries only immediately above this line.
     43   GEOPOSITION_ERROR_CODE_COUNT = 4
     44 };
     45 
     46 void RecordGeopositionErrorCode(Geoposition::ErrorCode error_code) {
     47   GeopositionErrorCode code = GEOPOSITION_ERROR_CODE_NONE;
     48   switch (error_code) {
     49     case Geoposition::ERROR_CODE_NONE:
     50       code = GEOPOSITION_ERROR_CODE_NONE;
     51       break;
     52     case Geoposition::ERROR_CODE_PERMISSION_DENIED:
     53       code = GEOPOSITION_ERROR_CODE_PERMISSION_DENIED;
     54       break;
     55     case Geoposition::ERROR_CODE_POSITION_UNAVAILABLE:
     56       code = GEOPOSITION_ERROR_CODE_POSITION_UNAVAILABLE;
     57       break;
     58     case Geoposition::ERROR_CODE_TIMEOUT:
     59       code = GEOPOSITION_ERROR_CODE_TIMEOUT;
     60       break;
     61   }
     62   UMA_HISTOGRAM_ENUMERATION("Geolocation.LocationUpdate.ErrorCode",
     63                             code,
     64                             GEOPOSITION_ERROR_CODE_COUNT);
     65 }
     66 
     67 }  // namespace
     68 
     69 GeolocationDispatcherHost::PendingPermission::PendingPermission(
     70     int render_frame_id,
     71     int render_process_id,
     72     int bridge_id)
     73     : render_frame_id(render_frame_id),
     74       render_process_id(render_process_id),
     75       bridge_id(bridge_id) {
     76 }
     77 
     78 GeolocationDispatcherHost::PendingPermission::~PendingPermission() {
     79 }
     80 
     81 GeolocationDispatcherHost::GeolocationDispatcherHost(
     82     WebContents* web_contents)
     83     : WebContentsObserver(web_contents),
     84       paused_(false),
     85       weak_factory_(this) {
     86   // This is initialized by WebContentsImpl. Do not add any non-trivial
     87   // initialization here, defer to OnStartUpdating which is triggered whenever
     88   // a javascript geolocation object is actually initialized.
     89 }
     90 
     91 GeolocationDispatcherHost::~GeolocationDispatcherHost() {
     92 }
     93 
     94 void GeolocationDispatcherHost::RenderFrameDeleted(
     95     RenderFrameHost* render_frame_host) {
     96   OnStopUpdating(render_frame_host);
     97 }
     98 
     99 void GeolocationDispatcherHost::RenderViewHostChanged(
    100     RenderViewHost* old_host,
    101     RenderViewHost* new_host) {
    102   updating_frames_.clear();
    103   paused_ = false;
    104   geolocation_subscription_.reset();
    105 }
    106 
    107 bool GeolocationDispatcherHost::OnMessageReceived(
    108     const IPC::Message& msg, RenderFrameHost* render_frame_host) {
    109   bool handled = true;
    110   IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(GeolocationDispatcherHost, msg,
    111                                    render_frame_host)
    112     IPC_MESSAGE_HANDLER(GeolocationHostMsg_RequestPermission,
    113                         OnRequestPermission)
    114     IPC_MESSAGE_HANDLER(GeolocationHostMsg_CancelPermissionRequest,
    115                         OnCancelPermissionRequest)
    116     IPC_MESSAGE_HANDLER(GeolocationHostMsg_StartUpdating, OnStartUpdating)
    117     IPC_MESSAGE_HANDLER(GeolocationHostMsg_StopUpdating, OnStopUpdating)
    118     IPC_MESSAGE_UNHANDLED(handled = false)
    119   IPC_END_MESSAGE_MAP()
    120   return handled;
    121 }
    122 
    123 void GeolocationDispatcherHost::OnLocationUpdate(
    124     const Geoposition& geoposition) {
    125   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    126 
    127   RecordGeopositionErrorCode(geoposition.error_code);
    128   if (paused_)
    129     return;
    130 
    131   for (std::map<RenderFrameHost*, bool>::iterator i = updating_frames_.begin();
    132        i != updating_frames_.end(); ++i) {
    133     i->first->Send(new GeolocationMsg_PositionUpdated(
    134         i->first->GetRoutingID(), geoposition));
    135   }
    136 }
    137 
    138 void GeolocationDispatcherHost::OnRequestPermission(
    139     RenderFrameHost* render_frame_host,
    140     int bridge_id,
    141     const GURL& requesting_frame,
    142     bool user_gesture) {
    143   int render_process_id = render_frame_host->GetProcess()->GetID();
    144   int render_frame_id = render_frame_host->GetRoutingID();
    145 
    146   PendingPermission pending_permission(
    147       render_frame_id, render_process_id, bridge_id);
    148   pending_permissions_.push_back(pending_permission);
    149 
    150   GetContentClient()->browser()->RequestGeolocationPermission(
    151       web_contents(),
    152       bridge_id,
    153       requesting_frame,
    154       user_gesture,
    155       base::Bind(&GeolocationDispatcherHost::SendGeolocationPermissionResponse,
    156                  weak_factory_.GetWeakPtr(),
    157                  render_process_id, render_frame_id, bridge_id),
    158       &pending_permissions_.back().cancel);
    159 }
    160 
    161 void GeolocationDispatcherHost::OnCancelPermissionRequest(
    162     RenderFrameHost* render_frame_host,
    163     int bridge_id,
    164     const GURL& requesting_frame) {
    165   int render_process_id = render_frame_host->GetProcess()->GetID();
    166   int render_frame_id = render_frame_host->GetRoutingID();
    167   for (size_t i = 0; i < pending_permissions_.size(); ++i) {
    168     if (pending_permissions_[i].render_process_id == render_process_id &&
    169         pending_permissions_[i].render_frame_id == render_frame_id &&
    170         pending_permissions_[i].bridge_id == bridge_id) {
    171       if (!pending_permissions_[i].cancel.is_null())
    172         pending_permissions_[i].cancel.Run();
    173       pending_permissions_.erase(pending_permissions_.begin() + i);
    174       return;
    175     }
    176   }
    177 }
    178 
    179 void GeolocationDispatcherHost::OnStartUpdating(
    180     RenderFrameHost* render_frame_host,
    181     const GURL& requesting_frame,
    182     bool enable_high_accuracy) {
    183   // StartUpdating() can be invoked as a result of high-accuracy mode
    184   // being enabled / disabled. No need to record the dispatcher again.
    185   UMA_HISTOGRAM_BOOLEAN(
    186       "Geolocation.GeolocationDispatcherHostImpl.EnableHighAccuracy",
    187       enable_high_accuracy);
    188 
    189   updating_frames_[render_frame_host] = enable_high_accuracy;
    190   RefreshGeolocationOptions();
    191 }
    192 
    193 void GeolocationDispatcherHost::OnStopUpdating(
    194     RenderFrameHost* render_frame_host) {
    195   updating_frames_.erase(render_frame_host);
    196   RefreshGeolocationOptions();
    197 }
    198 
    199 void GeolocationDispatcherHost::PauseOrResume(bool should_pause) {
    200   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    201   paused_ = should_pause;
    202   RefreshGeolocationOptions();
    203 }
    204 
    205 void GeolocationDispatcherHost::RefreshGeolocationOptions() {
    206   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    207 
    208   if (updating_frames_.empty() || paused_) {
    209     geolocation_subscription_.reset();
    210     return;
    211   }
    212 
    213   bool high_accuracy = false;
    214   for (std::map<RenderFrameHost*, bool>::iterator i =
    215             updating_frames_.begin(); i != updating_frames_.end(); ++i) {
    216     if (i->second) {
    217       high_accuracy = true;
    218       break;
    219     }
    220   }
    221   geolocation_subscription_ = GeolocationProvider::GetInstance()->
    222       AddLocationUpdateCallback(
    223           base::Bind(&GeolocationDispatcherHost::OnLocationUpdate,
    224                       base::Unretained(this)),
    225           high_accuracy);
    226 }
    227 
    228 void GeolocationDispatcherHost::SendGeolocationPermissionResponse(
    229     int render_process_id,
    230     int render_frame_id,
    231     int bridge_id,
    232     bool allowed) {
    233   for (size_t i = 0; i < pending_permissions_.size(); ++i) {
    234     if (pending_permissions_[i].render_process_id == render_process_id &&
    235         pending_permissions_[i].render_frame_id == render_frame_id &&
    236         pending_permissions_[i].bridge_id == bridge_id) {
    237       RenderFrameHost* render_frame_host =
    238           RenderFrameHost::FromID(render_process_id, render_frame_id);
    239       if (render_frame_host) {
    240         render_frame_host->Send(new GeolocationMsg_PermissionSet(
    241             render_frame_id, bridge_id, allowed));
    242       }
    243 
    244       if (allowed) {
    245         GeolocationProviderImpl::GetInstance()->
    246             UserDidOptIntoLocationServices();
    247       }
    248 
    249       pending_permissions_.erase(pending_permissions_.begin() + i);
    250       return;
    251     }
    252   }
    253 
    254   NOTREACHED();
    255 }
    256 
    257 }  // namespace content
    258