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.
5 #include "content/browser/geolocation/geolocation_dispatcher_host.h"
10 #include "base/metrics/histogram.h"
11 #include "content/browser/renderer_host/render_message_filter.h"
12 #include "content/browser/renderer_host/render_process_host_impl.h"
13 #include "content/browser/renderer_host/render_view_host_impl.h"
14 #include "content/public/browser/geolocation_permission_context.h"
15 #include "content/public/common/geoposition.h"
16 #include "content/common/geolocation_messages.h"
21 // Geoposition error codes for reporting in UMA.
22 enum GeopositionErrorCode
{
23 // NOTE: Do not renumber these as that would confuse interpretation of
24 // previously logged data. When making changes, also update the enum list
25 // in tools/metrics/histograms/histograms.xml to keep it in sync.
27 // There was no error.
28 GEOPOSITION_ERROR_CODE_NONE
= 0,
30 // User denied use of geolocation.
31 GEOPOSITION_ERROR_CODE_PERMISSION_DENIED
= 1,
33 // Geoposition could not be determined.
34 GEOPOSITION_ERROR_CODE_POSITION_UNAVAILABLE
= 2,
37 GEOPOSITION_ERROR_CODE_TIMEOUT
= 3,
39 // NOTE: Add entries only immediately above this line.
40 GEOPOSITION_ERROR_CODE_COUNT
= 4
43 void RecordGeopositionErrorCode(Geoposition::ErrorCode error_code
) {
44 GeopositionErrorCode code
= GEOPOSITION_ERROR_CODE_NONE
;
46 case Geoposition::ERROR_CODE_NONE
:
47 code
= GEOPOSITION_ERROR_CODE_NONE
;
49 case Geoposition::ERROR_CODE_PERMISSION_DENIED
:
50 code
= GEOPOSITION_ERROR_CODE_PERMISSION_DENIED
;
52 case Geoposition::ERROR_CODE_POSITION_UNAVAILABLE
:
53 code
= GEOPOSITION_ERROR_CODE_POSITION_UNAVAILABLE
;
55 case Geoposition::ERROR_CODE_TIMEOUT
:
56 code
= GEOPOSITION_ERROR_CODE_TIMEOUT
;
59 UMA_HISTOGRAM_ENUMERATION("Geolocation.LocationUpdate.ErrorCode",
61 GEOPOSITION_ERROR_CODE_COUNT
);
64 void NotifyGeolocationProviderPermissionGranted() {
65 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
66 GeolocationProviderImpl::GetInstance()->UserDidOptIntoLocationServices();
69 void SendGeolocationPermissionResponse(int render_process_id
,
73 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
74 RenderViewHostImpl
* render_view_host
=
75 RenderViewHostImpl::FromID(render_process_id
, render_view_id
);
76 if (!render_view_host
)
78 render_view_host
->Send(
79 new GeolocationMsg_PermissionSet(render_view_id
, bridge_id
, allowed
));
82 BrowserThread::PostTask(
83 BrowserThread::IO
, FROM_HERE
,
84 base::Bind(&NotifyGeolocationProviderPermissionGranted
));
90 GeolocationDispatcherHost::GeolocationDispatcherHost(
91 int render_process_id
,
92 GeolocationPermissionContext
* geolocation_permission_context
)
93 : BrowserMessageFilter(GeolocationMsgStart
),
94 render_process_id_(render_process_id
),
95 geolocation_permission_context_(geolocation_permission_context
),
96 geolocation_provider_(NULL
) {
97 callback_
= base::Bind(
98 &GeolocationDispatcherHost::OnLocationUpdate
, base::Unretained(this));
99 // This is initialized by ResourceMessageFilter. Do not add any non-trivial
100 // initialization here, defer to OnRegisterBridge which is triggered whenever
101 // a javascript geolocation object is actually initialized.
104 GeolocationDispatcherHost::~GeolocationDispatcherHost() {
105 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
106 if (geolocation_provider_
)
107 geolocation_provider_
->RemoveLocationUpdateCallback(callback_
);
110 bool GeolocationDispatcherHost::OnMessageReceived(
111 const IPC::Message
& msg
, bool* msg_was_ok
) {
112 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
115 IPC_BEGIN_MESSAGE_MAP_EX(GeolocationDispatcherHost
, msg
, *msg_was_ok
)
116 IPC_MESSAGE_HANDLER(GeolocationHostMsg_CancelPermissionRequest
,
117 OnCancelPermissionRequest
)
118 IPC_MESSAGE_HANDLER(GeolocationHostMsg_RequestPermission
,
120 IPC_MESSAGE_HANDLER(GeolocationHostMsg_StartUpdating
, OnStartUpdating
)
121 IPC_MESSAGE_HANDLER(GeolocationHostMsg_StopUpdating
, OnStopUpdating
)
122 IPC_MESSAGE_UNHANDLED(handled
= false)
123 IPC_END_MESSAGE_MAP()
127 void GeolocationDispatcherHost::OnLocationUpdate(
128 const Geoposition
& geoposition
) {
129 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
130 RecordGeopositionErrorCode(geoposition
.error_code
);
131 for (std::map
<int, RendererGeolocationOptions
>::iterator it
=
132 geolocation_renderers_
.begin();
133 it
!= geolocation_renderers_
.end(); ++it
) {
134 if (!(it
->second
.is_paused
))
135 Send(new GeolocationMsg_PositionUpdated(it
->first
, geoposition
));
139 void GeolocationDispatcherHost::OnRequestPermission(
142 const GURL
& requesting_frame
,
144 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
145 DVLOG(1) << __FUNCTION__
<< " " << render_process_id_
<< ":"
146 << render_view_id
<< ":" << bridge_id
;
147 if (geolocation_permission_context_
.get()) {
148 geolocation_permission_context_
->RequestGeolocationPermission(
154 base::Bind(&SendGeolocationPermissionResponse
,
159 BrowserThread::PostTask(
160 BrowserThread::UI
, FROM_HERE
,
161 base::Bind(&SendGeolocationPermissionResponse
, render_process_id_
,
162 render_view_id
, bridge_id
, true));
166 void GeolocationDispatcherHost::OnCancelPermissionRequest(
169 const GURL
& requesting_frame
) {
170 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
171 DVLOG(1) << __FUNCTION__
<< " " << render_process_id_
<< ":"
172 << render_view_id
<< ":" << bridge_id
;
173 if (geolocation_permission_context_
.get()) {
174 geolocation_permission_context_
->CancelGeolocationPermissionRequest(
175 render_process_id_
, render_view_id
, bridge_id
, requesting_frame
);
179 void GeolocationDispatcherHost::OnStartUpdating(
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 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
186 DVLOG(1) << __FUNCTION__
<< " " << render_process_id_
<< ":"
188 UMA_HISTOGRAM_BOOLEAN(
189 "Geolocation.GeolocationDispatcherHostImpl.EnableHighAccuracy",
190 enable_high_accuracy
);
192 std::map
<int, RendererGeolocationOptions
>::iterator it
=
193 geolocation_renderers_
.find(render_view_id
);
194 if (it
== geolocation_renderers_
.end()) {
195 bool should_start_paused
= false;
196 if (pending_paused_geolocation_renderers_
.erase(render_view_id
) == 1) {
197 should_start_paused
= true;
199 RendererGeolocationOptions opts
= {
200 enable_high_accuracy
,
203 geolocation_renderers_
[render_view_id
] = opts
;
205 it
->second
.high_accuracy
= enable_high_accuracy
;
207 RefreshGeolocationOptions();
210 void GeolocationDispatcherHost::OnStopUpdating(int render_view_id
) {
211 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
212 DVLOG(1) << __FUNCTION__
<< " " << render_process_id_
<< ":"
214 DCHECK_EQ(1U, geolocation_renderers_
.count(render_view_id
));
215 geolocation_renderers_
.erase(render_view_id
);
216 RefreshGeolocationOptions();
219 void GeolocationDispatcherHost::PauseOrResume(int render_view_id
,
221 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
222 std::map
<int, RendererGeolocationOptions
>::iterator it
=
223 geolocation_renderers_
.find(render_view_id
);
224 if (it
== geolocation_renderers_
.end()) {
225 // This renderer is not using geolocation yet, but if it does before
226 // we get a call to resume, we should start it up in the paused state.
228 pending_paused_geolocation_renderers_
.insert(render_view_id
);
230 pending_paused_geolocation_renderers_
.erase(render_view_id
);
233 RendererGeolocationOptions
* opts
= &(it
->second
);
234 if (opts
->is_paused
!= should_pause
)
235 opts
->is_paused
= should_pause
;
236 RefreshGeolocationOptions();
240 void GeolocationDispatcherHost::RefreshGeolocationOptions() {
241 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
243 bool needs_updates
= false;
244 bool use_high_accuracy
= false;
245 std::map
<int, RendererGeolocationOptions
>::const_iterator i
=
246 geolocation_renderers_
.begin();
247 for (; i
!= geolocation_renderers_
.end(); ++i
) {
248 needs_updates
|= !(i
->second
.is_paused
);
249 use_high_accuracy
|= i
->second
.high_accuracy
;
250 if (needs_updates
&& use_high_accuracy
)
254 if (!geolocation_provider_
)
255 geolocation_provider_
= GeolocationProviderImpl::GetInstance();
256 // Re-add to re-establish our options, in case they changed.
257 geolocation_provider_
->AddLocationUpdateCallback(
258 callback_
, use_high_accuracy
);
260 if (geolocation_provider_
)
261 geolocation_provider_
->RemoveLocationUpdateCallback(callback_
);
262 geolocation_provider_
= NULL
;
266 } // namespace content