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     RenderFrameHost* top_frame = i->first;
    134     while (top_frame->GetParent()) {
    135       top_frame = top_frame->GetParent();
    136     }
    137     GetContentClient()->browser()->DidUseGeolocationPermission(
    138         web_contents(),
    139         i->first->GetLastCommittedURL().GetOrigin(),
    140         top_frame->GetLastCommittedURL().GetOrigin());
    141 
    142     i->first->Send(new GeolocationMsg_PositionUpdated(
    143         i->first->GetRoutingID(), geoposition));
    144   }
    145 }
    146 
    147 void GeolocationDispatcherHost::OnRequestPermission(
    148     RenderFrameHost* render_frame_host,
    149     int bridge_id,
    150     const GURL& requesting_frame,
    151     bool user_gesture) {
    152   int render_process_id = render_frame_host->GetProcess()->GetID();
    153   int render_frame_id = render_frame_host->GetRoutingID();
    154 
    155   PendingPermission pending_permission(
    156       render_frame_id, render_process_id, bridge_id);
    157   pending_permissions_.push_back(pending_permission);
    158 
    159   GetContentClient()->browser()->RequestGeolocationPermission(
    160       web_contents(),
    161       bridge_id,
    162       requesting_frame,
    163       user_gesture,
    164       base::Bind(&GeolocationDispatcherHost::SendGeolocationPermissionResponse,
    165                  weak_factory_.GetWeakPtr(),
    166                  render_process_id, render_frame_id, bridge_id),
    167       &pending_permissions_.back().cancel);
    168 }
    169 
    170 void GeolocationDispatcherHost::OnCancelPermissionRequest(
    171     RenderFrameHost* render_frame_host,
    172     int bridge_id,
    173     const GURL& requesting_frame) {
    174   int render_process_id = render_frame_host->GetProcess()->GetID();
    175   int render_frame_id = render_frame_host->GetRoutingID();
    176   for (size_t i = 0; i < pending_permissions_.size(); ++i) {
    177     if (pending_permissions_[i].render_process_id == render_process_id &&
    178         pending_permissions_[i].render_frame_id == render_frame_id &&
    179         pending_permissions_[i].bridge_id == bridge_id) {
    180       if (!pending_permissions_[i].cancel.is_null())
    181         pending_permissions_[i].cancel.Run();
    182       pending_permissions_.erase(pending_permissions_.begin() + i);
    183       return;
    184     }
    185   }
    186 }
    187 
    188 void GeolocationDispatcherHost::OnStartUpdating(
    189     RenderFrameHost* render_frame_host,
    190     const GURL& requesting_frame,
    191     bool enable_high_accuracy) {
    192   // StartUpdating() can be invoked as a result of high-accuracy mode
    193   // being enabled / disabled. No need to record the dispatcher again.
    194   UMA_HISTOGRAM_BOOLEAN(
    195       "Geolocation.GeolocationDispatcherHostImpl.EnableHighAccuracy",
    196       enable_high_accuracy);
    197 
    198   updating_frames_[render_frame_host] = enable_high_accuracy;
    199   RefreshGeolocationOptions();
    200 }
    201 
    202 void GeolocationDispatcherHost::OnStopUpdating(
    203     RenderFrameHost* render_frame_host) {
    204   updating_frames_.erase(render_frame_host);
    205   RefreshGeolocationOptions();
    206 }
    207 
    208 void GeolocationDispatcherHost::PauseOrResume(bool should_pause) {
    209   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    210   paused_ = should_pause;
    211   RefreshGeolocationOptions();
    212 }
    213 
    214 void GeolocationDispatcherHost::RefreshGeolocationOptions() {
    215   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    216 
    217   if (updating_frames_.empty() || paused_) {
    218     geolocation_subscription_.reset();
    219     return;
    220   }
    221 
    222   bool high_accuracy = false;
    223   for (std::map<RenderFrameHost*, bool>::iterator i =
    224             updating_frames_.begin(); i != updating_frames_.end(); ++i) {
    225     if (i->second) {
    226       high_accuracy = true;
    227       break;
    228     }
    229   }
    230   geolocation_subscription_ = GeolocationProvider::GetInstance()->
    231       AddLocationUpdateCallback(
    232           base::Bind(&GeolocationDispatcherHost::OnLocationUpdate,
    233                       base::Unretained(this)),
    234           high_accuracy);
    235 }
    236 
    237 void GeolocationDispatcherHost::SendGeolocationPermissionResponse(
    238     int render_process_id,
    239     int render_frame_id,
    240     int bridge_id,
    241     bool allowed) {
    242   for (size_t i = 0; i < pending_permissions_.size(); ++i) {
    243     if (pending_permissions_[i].render_process_id == render_process_id &&
    244         pending_permissions_[i].render_frame_id == render_frame_id &&
    245         pending_permissions_[i].bridge_id == bridge_id) {
    246       RenderFrameHost* render_frame_host =
    247           RenderFrameHost::FromID(render_process_id, render_frame_id);
    248       if (render_frame_host) {
    249         render_frame_host->Send(new GeolocationMsg_PermissionSet(
    250             render_frame_id, bridge_id, allowed));
    251       }
    252 
    253       if (allowed) {
    254         GeolocationProviderImpl::GetInstance()->
    255             UserDidOptIntoLocationServices();
    256       }
    257 
    258       pending_permissions_.erase(pending_permissions_.begin() + i);
    259       return;
    260     }
    261   }
    262 
    263   NOTREACHED();
    264 }
    265 
    266 }  // namespace content
    267