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 <map>
      8 #include <set>
      9 #include <utility>
     10 
     11 #include "base/bind.h"
     12 #include "base/metrics/histogram.h"
     13 #include "content/browser/geolocation/geolocation_provider_impl.h"
     14 #include "content/browser/renderer_host/render_message_filter.h"
     15 #include "content/browser/renderer_host/render_process_host_impl.h"
     16 #include "content/browser/renderer_host/render_view_host_impl.h"
     17 #include "content/public/browser/geolocation_permission_context.h"
     18 #include "content/public/common/geoposition.h"
     19 #include "content/common/geolocation_messages.h"
     20 
     21 namespace content {
     22 namespace {
     23 
     24 void NotifyGeolocationProviderPermissionGranted() {
     25   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     26   GeolocationProviderImpl::GetInstance()->UserDidOptIntoLocationServices();
     27 }
     28 
     29 void SendGeolocationPermissionResponse(int render_process_id,
     30                                        int render_view_id,
     31                                        int bridge_id,
     32                                        bool allowed) {
     33   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     34   RenderViewHostImpl* render_view_host =
     35       RenderViewHostImpl::FromID(render_process_id, render_view_id);
     36   if (!render_view_host)
     37     return;
     38   render_view_host->Send(
     39       new GeolocationMsg_PermissionSet(render_view_id, bridge_id, allowed));
     40 
     41   if (allowed) {
     42     BrowserThread::PostTask(
     43         BrowserThread::IO, FROM_HERE,
     44         base::Bind(&NotifyGeolocationProviderPermissionGranted));
     45   }
     46 }
     47 
     48 class GeolocationDispatcherHostImpl : public GeolocationDispatcherHost {
     49  public:
     50   GeolocationDispatcherHostImpl(
     51       int render_process_id,
     52       GeolocationPermissionContext* geolocation_permission_context);
     53 
     54   // GeolocationDispatcherHost
     55   virtual bool OnMessageReceived(const IPC::Message& msg,
     56                                  bool* msg_was_ok) OVERRIDE;
     57 
     58  private:
     59   virtual ~GeolocationDispatcherHostImpl();
     60 
     61   void OnRequestPermission(int render_view_id,
     62                            int bridge_id,
     63                            const GURL& requesting_frame);
     64   void OnCancelPermissionRequest(int render_view_id,
     65                                  int bridge_id,
     66                                  const GURL& requesting_frame);
     67   void OnStartUpdating(int render_view_id,
     68                        const GURL& requesting_frame,
     69                        bool enable_high_accuracy);
     70   void OnStopUpdating(int render_view_id);
     71 
     72 
     73   virtual void PauseOrResume(int render_view_id, bool should_pause) OVERRIDE;
     74 
     75   // Updates the |geolocation_provider_| with the currently required update
     76   // options.
     77   void RefreshGeolocationOptions();
     78 
     79   void OnLocationUpdate(const Geoposition& position);
     80 
     81   int render_process_id_;
     82   scoped_refptr<GeolocationPermissionContext> geolocation_permission_context_;
     83 
     84   struct RendererGeolocationOptions {
     85     bool high_accuracy;
     86     bool is_paused;
     87   };
     88 
     89   // Used to keep track of the renderers in this process that are using
     90   // geolocation and the options associated with them. The map is iterated
     91   // when a location update is available and the fan out to individual bridge
     92   // IDs happens renderer side, in order to minimize context switches.
     93   // Only used on the IO thread.
     94   std::map<int, RendererGeolocationOptions> geolocation_renderers_;
     95 
     96   // Used by Android WebView to support that case that a renderer is in the
     97   // 'paused' state but not yet using geolocation. If the renderer does start
     98   // using geolocation while paused, we move from this set into
     99   // |geolocation_renderers_|. If the renderer doesn't end up wanting to use
    100   // geolocation while 'paused' then we remove from this set. A renderer id
    101   // can exist only in this set or |geolocation_renderers_|, never both.
    102   std::set<int> pending_paused_geolocation_renderers_;
    103 
    104   // Only set whilst we are registered with the geolocation provider.
    105   GeolocationProviderImpl* geolocation_provider_;
    106 
    107   GeolocationProviderImpl::LocationUpdateCallback callback_;
    108 
    109   DISALLOW_COPY_AND_ASSIGN(GeolocationDispatcherHostImpl);
    110 };
    111 
    112 GeolocationDispatcherHostImpl::GeolocationDispatcherHostImpl(
    113     int render_process_id,
    114     GeolocationPermissionContext* geolocation_permission_context)
    115     : render_process_id_(render_process_id),
    116       geolocation_permission_context_(geolocation_permission_context),
    117       geolocation_provider_(NULL) {
    118   callback_ = base::Bind(
    119       &GeolocationDispatcherHostImpl::OnLocationUpdate, base::Unretained(this));
    120   // This is initialized by ResourceMessageFilter. Do not add any non-trivial
    121   // initialization here, defer to OnRegisterBridge which is triggered whenever
    122   // a javascript geolocation object is actually initialized.
    123 }
    124 
    125 GeolocationDispatcherHostImpl::~GeolocationDispatcherHostImpl() {
    126   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    127   if (geolocation_provider_)
    128     geolocation_provider_->RemoveLocationUpdateCallback(callback_);
    129 }
    130 
    131 bool GeolocationDispatcherHostImpl::OnMessageReceived(
    132     const IPC::Message& msg, bool* msg_was_ok) {
    133   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    134   *msg_was_ok = true;
    135   bool handled = true;
    136   IPC_BEGIN_MESSAGE_MAP_EX(GeolocationDispatcherHostImpl, msg, *msg_was_ok)
    137     IPC_MESSAGE_HANDLER(GeolocationHostMsg_CancelPermissionRequest,
    138                         OnCancelPermissionRequest)
    139     IPC_MESSAGE_HANDLER(GeolocationHostMsg_RequestPermission,
    140                         OnRequestPermission)
    141     IPC_MESSAGE_HANDLER(GeolocationHostMsg_StartUpdating, OnStartUpdating)
    142     IPC_MESSAGE_HANDLER(GeolocationHostMsg_StopUpdating, OnStopUpdating)
    143     IPC_MESSAGE_UNHANDLED(handled = false)
    144   IPC_END_MESSAGE_MAP()
    145   return handled;
    146 }
    147 
    148 void GeolocationDispatcherHostImpl::OnLocationUpdate(
    149     const Geoposition& geoposition) {
    150   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    151   for (std::map<int, RendererGeolocationOptions>::iterator it =
    152        geolocation_renderers_.begin();
    153        it != geolocation_renderers_.end(); ++it) {
    154     if (!(it->second.is_paused))
    155       Send(new GeolocationMsg_PositionUpdated(it->first, geoposition));
    156   }
    157 }
    158 
    159 void GeolocationDispatcherHostImpl::OnRequestPermission(
    160     int render_view_id,
    161     int bridge_id,
    162     const GURL& requesting_frame) {
    163   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    164   DVLOG(1) << __FUNCTION__ << " " << render_process_id_ << ":"
    165            << render_view_id << ":" << bridge_id;
    166   if (geolocation_permission_context_.get()) {
    167     geolocation_permission_context_->RequestGeolocationPermission(
    168         render_process_id_,
    169         render_view_id,
    170         bridge_id,
    171         requesting_frame,
    172         base::Bind(&SendGeolocationPermissionResponse,
    173                    render_process_id_,
    174                    render_view_id,
    175                    bridge_id));
    176   } else {
    177     BrowserThread::PostTask(
    178         BrowserThread::UI, FROM_HERE,
    179         base::Bind(&SendGeolocationPermissionResponse, render_process_id_,
    180                    render_view_id, bridge_id, true));
    181   }
    182 }
    183 
    184 void GeolocationDispatcherHostImpl::OnCancelPermissionRequest(
    185     int render_view_id,
    186     int bridge_id,
    187     const GURL& requesting_frame) {
    188   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    189   DVLOG(1) << __FUNCTION__ << " " << render_process_id_ << ":"
    190            << render_view_id << ":" << bridge_id;
    191   if (geolocation_permission_context_.get()) {
    192     geolocation_permission_context_->CancelGeolocationPermissionRequest(
    193         render_process_id_, render_view_id, bridge_id, requesting_frame);
    194   }
    195 }
    196 
    197 void GeolocationDispatcherHostImpl::OnStartUpdating(
    198     int render_view_id,
    199     const GURL& requesting_frame,
    200     bool enable_high_accuracy) {
    201   // StartUpdating() can be invoked as a result of high-accuracy mode
    202   // being enabled / disabled. No need to record the dispatcher again.
    203   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    204   DVLOG(1) << __FUNCTION__ << " " << render_process_id_ << ":"
    205            << render_view_id;
    206   UMA_HISTOGRAM_BOOLEAN(
    207       "Geolocation.GeolocationDispatcherHostImpl.EnableHighAccuracy",
    208       enable_high_accuracy);
    209 
    210   std::map<int, RendererGeolocationOptions>::iterator it =
    211             geolocation_renderers_.find(render_view_id);
    212   if (it == geolocation_renderers_.end()) {
    213     bool should_start_paused = false;
    214     if (pending_paused_geolocation_renderers_.erase(render_view_id) == 1) {
    215       should_start_paused = true;
    216     }
    217     RendererGeolocationOptions opts = {
    218       enable_high_accuracy,
    219       should_start_paused
    220     };
    221     geolocation_renderers_[render_view_id] = opts;
    222   } else {
    223     it->second.high_accuracy = enable_high_accuracy;
    224   }
    225   RefreshGeolocationOptions();
    226 }
    227 
    228 void GeolocationDispatcherHostImpl::OnStopUpdating(int render_view_id) {
    229   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    230   DVLOG(1) << __FUNCTION__ << " " << render_process_id_ << ":"
    231            << render_view_id;
    232   DCHECK_EQ(1U, geolocation_renderers_.count(render_view_id));
    233   geolocation_renderers_.erase(render_view_id);
    234   RefreshGeolocationOptions();
    235 }
    236 
    237 void GeolocationDispatcherHostImpl::PauseOrResume(int render_view_id,
    238                                                   bool should_pause) {
    239   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    240   std::map<int, RendererGeolocationOptions>::iterator it =
    241       geolocation_renderers_.find(render_view_id);
    242   if (it == geolocation_renderers_.end()) {
    243     // This renderer is not using geolocation yet, but if it does before
    244     // we get a call to resume, we should start it up in the paused state.
    245     if (should_pause) {
    246       pending_paused_geolocation_renderers_.insert(render_view_id);
    247     } else {
    248       pending_paused_geolocation_renderers_.erase(render_view_id);
    249     }
    250   } else {
    251     RendererGeolocationOptions* opts = &(it->second);
    252     if (opts->is_paused != should_pause)
    253       opts->is_paused = should_pause;
    254     RefreshGeolocationOptions();
    255   }
    256 }
    257 
    258 void GeolocationDispatcherHostImpl::RefreshGeolocationOptions() {
    259   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    260 
    261   bool needs_updates = false;
    262   bool use_high_accuracy = false;
    263   std::map<int, RendererGeolocationOptions>::const_iterator i =
    264       geolocation_renderers_.begin();
    265   for (; i != geolocation_renderers_.end(); ++i) {
    266     needs_updates |= !(i->second.is_paused);
    267     use_high_accuracy |= i->second.high_accuracy;
    268     if (needs_updates && use_high_accuracy)
    269       break;
    270   }
    271   if (needs_updates) {
    272     if (!geolocation_provider_)
    273       geolocation_provider_ = GeolocationProviderImpl::GetInstance();
    274     // Re-add to re-establish our options, in case they changed.
    275     geolocation_provider_->AddLocationUpdateCallback(
    276         callback_, use_high_accuracy);
    277   } else {
    278     if (geolocation_provider_)
    279       geolocation_provider_->RemoveLocationUpdateCallback(callback_);
    280     geolocation_provider_ = NULL;
    281   }
    282 }
    283 
    284 }  // namespace
    285 
    286 
    287 // GeolocationDispatcherHost --------------------------------------------------
    288 
    289 // static
    290 GeolocationDispatcherHost* GeolocationDispatcherHost::New(
    291     int render_process_id,
    292     GeolocationPermissionContext* geolocation_permission_context) {
    293   return new GeolocationDispatcherHostImpl(
    294       render_process_id,
    295       geolocation_permission_context);
    296 }
    297 
    298 GeolocationDispatcherHost::GeolocationDispatcherHost() {
    299 }
    300 
    301 GeolocationDispatcherHost::~GeolocationDispatcherHost() {
    302 }
    303 
    304 }  // namespace content
    305