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 "content/browser/geolocation/geolocation_provider_impl.h" 13 #include "content/browser/renderer_host/render_message_filter.h" 14 #include "content/browser/renderer_host/render_process_host_impl.h" 15 #include "content/browser/renderer_host/render_view_host_impl.h" 16 #include "content/public/browser/geolocation_permission_context.h" 17 #include "content/public/common/geoposition.h" 18 #include "content/common/geolocation_messages.h" 19 20 namespace content { 21 namespace { 22 23 void NotifyArbitratorPermissionGranted() { 24 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 25 GeolocationProviderImpl::GetInstance()->UserDidOptIntoLocationServices(); 26 } 27 28 void SendGeolocationPermissionResponse(int render_process_id, 29 int render_view_id, 30 int bridge_id, 31 bool allowed) { 32 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 33 RenderViewHostImpl* r = 34 RenderViewHostImpl::FromID(render_process_id, render_view_id); 35 if (!r) 36 return; 37 r->Send(new GeolocationMsg_PermissionSet(render_view_id, bridge_id, allowed)); 38 39 if (allowed) { 40 BrowserThread::PostTask( 41 BrowserThread::IO, FROM_HERE, 42 base::Bind(&NotifyArbitratorPermissionGranted)); 43 } 44 } 45 46 class GeolocationDispatcherHostImpl : public GeolocationDispatcherHost { 47 public: 48 GeolocationDispatcherHostImpl( 49 int render_process_id, 50 GeolocationPermissionContext* geolocation_permission_context); 51 52 // GeolocationDispatcherHost 53 virtual bool OnMessageReceived(const IPC::Message& msg, 54 bool* msg_was_ok) OVERRIDE; 55 56 private: 57 virtual ~GeolocationDispatcherHostImpl(); 58 59 void OnRequestPermission(int render_view_id, 60 int bridge_id, 61 const GURL& requesting_frame); 62 void OnCancelPermissionRequest(int render_view_id, 63 int bridge_id, 64 const GURL& requesting_frame); 65 void OnStartUpdating(int render_view_id, 66 const GURL& requesting_frame, 67 bool enable_high_accuracy); 68 void OnStopUpdating(int render_view_id); 69 70 // Updates the |location_arbitrator_| with the currently required update 71 // options, based on |renderer_high_accuracy_|. 72 void RefreshHighAccuracy(); 73 74 void OnLocationUpdate(const Geoposition& position); 75 76 int render_process_id_; 77 scoped_refptr<GeolocationPermissionContext> geolocation_permission_context_; 78 79 // Iterated when sending location updates to renderer processes. The fan out 80 // to individual bridge IDs happens renderer side, in order to minimize 81 // context switches. 82 // Only used on the IO thread. 83 std::set<int> geolocation_renderer_ids_; 84 // Maps renderer_id to whether high accuracy is requestd for this particular 85 // bridge. 86 std::map<int, bool> renderer_high_accuracy_; 87 // Only set whilst we are registered with the arbitrator. 88 GeolocationProviderImpl* location_provider_; 89 90 GeolocationProviderImpl::LocationUpdateCallback callback_; 91 92 DISALLOW_COPY_AND_ASSIGN(GeolocationDispatcherHostImpl); 93 }; 94 95 GeolocationDispatcherHostImpl::GeolocationDispatcherHostImpl( 96 int render_process_id, 97 GeolocationPermissionContext* geolocation_permission_context) 98 : render_process_id_(render_process_id), 99 geolocation_permission_context_(geolocation_permission_context), 100 location_provider_(NULL) { 101 callback_ = base::Bind( 102 &GeolocationDispatcherHostImpl::OnLocationUpdate, base::Unretained(this)); 103 // This is initialized by ResourceMessageFilter. Do not add any non-trivial 104 // initialization here, defer to OnRegisterBridge which is triggered whenever 105 // a javascript geolocation object is actually initialized. 106 } 107 108 GeolocationDispatcherHostImpl::~GeolocationDispatcherHostImpl() { 109 if (location_provider_) 110 location_provider_->RemoveLocationUpdateCallback(callback_); 111 } 112 113 bool GeolocationDispatcherHostImpl::OnMessageReceived( 114 const IPC::Message& msg, bool* msg_was_ok) { 115 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 116 *msg_was_ok = true; 117 bool handled = true; 118 IPC_BEGIN_MESSAGE_MAP_EX(GeolocationDispatcherHostImpl, msg, *msg_was_ok) 119 IPC_MESSAGE_HANDLER(GeolocationHostMsg_CancelPermissionRequest, 120 OnCancelPermissionRequest) 121 IPC_MESSAGE_HANDLER(GeolocationHostMsg_RequestPermission, 122 OnRequestPermission) 123 IPC_MESSAGE_HANDLER(GeolocationHostMsg_StartUpdating, OnStartUpdating) 124 IPC_MESSAGE_HANDLER(GeolocationHostMsg_StopUpdating, OnStopUpdating) 125 IPC_MESSAGE_UNHANDLED(handled = false) 126 IPC_END_MESSAGE_MAP() 127 return handled; 128 } 129 130 void GeolocationDispatcherHostImpl::OnLocationUpdate( 131 const Geoposition& geoposition) { 132 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 133 for (std::set<int>::iterator it = geolocation_renderer_ids_.begin(); 134 it != geolocation_renderer_ids_.end(); ++it) { 135 Send(new GeolocationMsg_PositionUpdated(*it, geoposition)); 136 } 137 } 138 139 void GeolocationDispatcherHostImpl::OnRequestPermission( 140 int render_view_id, 141 int bridge_id, 142 const GURL& requesting_frame) { 143 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 144 DVLOG(1) << __FUNCTION__ << " " << render_process_id_ << ":" 145 << render_view_id << ":" << bridge_id; 146 if (geolocation_permission_context_.get()) { 147 geolocation_permission_context_->RequestGeolocationPermission( 148 render_process_id_, 149 render_view_id, 150 bridge_id, 151 requesting_frame, 152 base::Bind(&SendGeolocationPermissionResponse, 153 render_process_id_, 154 render_view_id, 155 bridge_id)); 156 } else { 157 BrowserThread::PostTask( 158 BrowserThread::UI, FROM_HERE, 159 base::Bind(&SendGeolocationPermissionResponse, render_process_id_, 160 render_view_id, bridge_id, true)); 161 } 162 } 163 164 void GeolocationDispatcherHostImpl::OnCancelPermissionRequest( 165 int render_view_id, 166 int bridge_id, 167 const GURL& requesting_frame) { 168 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 169 DVLOG(1) << __FUNCTION__ << " " << render_process_id_ << ":" 170 << render_view_id << ":" << bridge_id; 171 if (geolocation_permission_context_.get()) { 172 geolocation_permission_context_->CancelGeolocationPermissionRequest( 173 render_process_id_, render_view_id, bridge_id, requesting_frame); 174 } 175 } 176 177 void GeolocationDispatcherHostImpl::OnStartUpdating( 178 int render_view_id, 179 const GURL& requesting_frame, 180 bool enable_high_accuracy) { 181 // StartUpdating() can be invoked as a result of high-accuracy mode 182 // being enabled / disabled. No need to record the dispatcher again. 183 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 184 DVLOG(1) << __FUNCTION__ << " " << render_process_id_ << ":" 185 << render_view_id; 186 if (!geolocation_renderer_ids_.count(render_view_id)) 187 geolocation_renderer_ids_.insert(render_view_id); 188 189 renderer_high_accuracy_[render_view_id] = enable_high_accuracy; 190 RefreshHighAccuracy(); 191 } 192 193 void GeolocationDispatcherHostImpl::OnStopUpdating(int render_view_id) { 194 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 195 DVLOG(1) << __FUNCTION__ << " " << render_process_id_ << ":" 196 << render_view_id; 197 if (renderer_high_accuracy_.erase(render_view_id)) 198 RefreshHighAccuracy(); 199 200 DCHECK_EQ(1U, geolocation_renderer_ids_.count(render_view_id)); 201 geolocation_renderer_ids_.erase(render_view_id); 202 } 203 204 void GeolocationDispatcherHostImpl::RefreshHighAccuracy() { 205 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 206 if (renderer_high_accuracy_.empty()) { 207 if (location_provider_) { 208 location_provider_->RemoveLocationUpdateCallback(callback_); 209 location_provider_ = NULL; 210 } 211 } else { 212 if (!location_provider_) 213 location_provider_ = GeolocationProviderImpl::GetInstance(); 214 // Re-add to re-establish our options, in case they changed. 215 bool use_high_accuracy = false; 216 std::map<int, bool>::iterator i = renderer_high_accuracy_.begin(); 217 for (; i != renderer_high_accuracy_.end(); ++i) { 218 if (i->second) { 219 use_high_accuracy = true; 220 break; 221 } 222 } 223 location_provider_->AddLocationUpdateCallback(callback_, use_high_accuracy); 224 } 225 } 226 } // namespace 227 228 229 // GeolocationDispatcherHost -------------------------------------------------- 230 231 // static 232 GeolocationDispatcherHost* GeolocationDispatcherHost::New( 233 int render_process_id, 234 GeolocationPermissionContext* geolocation_permission_context) { 235 return new GeolocationDispatcherHostImpl( 236 render_process_id, 237 geolocation_permission_context); 238 } 239 240 GeolocationDispatcherHost::GeolocationDispatcherHost() { 241 } 242 243 GeolocationDispatcherHost::~GeolocationDispatcherHost() { 244 } 245 246 } // namespace content 247