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