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"
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"
24 void NotifyGeolocationProviderPermissionGranted() {
25 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
26 GeolocationProviderImpl::GetInstance()->UserDidOptIntoLocationServices();
29 void SendGeolocationPermissionResponse(int render_process_id
,
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
)
38 render_view_host
->Send(
39 new GeolocationMsg_PermissionSet(render_view_id
, bridge_id
, allowed
));
42 BrowserThread::PostTask(
43 BrowserThread::IO
, FROM_HERE
,
44 base::Bind(&NotifyGeolocationProviderPermissionGranted
));
48 class GeolocationDispatcherHostImpl
: public GeolocationDispatcherHost
{
50 GeolocationDispatcherHostImpl(
51 int render_process_id
,
52 GeolocationPermissionContext
* geolocation_permission_context
);
54 // GeolocationDispatcherHost
55 virtual bool OnMessageReceived(const IPC::Message
& msg
,
56 bool* msg_was_ok
) OVERRIDE
;
59 virtual ~GeolocationDispatcherHostImpl();
61 void OnRequestPermission(int render_view_id
,
63 const GURL
& requesting_frame
);
64 void OnCancelPermissionRequest(int render_view_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
);
73 virtual void PauseOrResume(int render_view_id
, bool should_pause
) OVERRIDE
;
75 // Updates the |geolocation_provider_| with the currently required update
77 void RefreshGeolocationOptions();
79 void OnLocationUpdate(const Geoposition
& position
);
81 int render_process_id_
;
82 scoped_refptr
<GeolocationPermissionContext
> geolocation_permission_context_
;
84 struct RendererGeolocationOptions
{
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_
;
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_
;
104 // Only set whilst we are registered with the geolocation provider.
105 GeolocationProviderImpl
* geolocation_provider_
;
107 GeolocationProviderImpl::LocationUpdateCallback callback_
;
109 DISALLOW_COPY_AND_ASSIGN(GeolocationDispatcherHostImpl
);
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.
125 GeolocationDispatcherHostImpl::~GeolocationDispatcherHostImpl() {
126 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
127 if (geolocation_provider_
)
128 geolocation_provider_
->RemoveLocationUpdateCallback(callback_
);
131 bool GeolocationDispatcherHostImpl::OnMessageReceived(
132 const IPC::Message
& msg
, bool* msg_was_ok
) {
133 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
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
,
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()
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
));
159 void GeolocationDispatcherHostImpl::OnRequestPermission(
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(
172 base::Bind(&SendGeolocationPermissionResponse
,
177 BrowserThread::PostTask(
178 BrowserThread::UI
, FROM_HERE
,
179 base::Bind(&SendGeolocationPermissionResponse
, render_process_id_
,
180 render_view_id
, bridge_id
, true));
184 void GeolocationDispatcherHostImpl::OnCancelPermissionRequest(
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
);
197 void GeolocationDispatcherHostImpl::OnStartUpdating(
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_
<< ":"
206 UMA_HISTOGRAM_BOOLEAN(
207 "Geolocation.GeolocationDispatcherHostImpl.EnableHighAccuracy",
208 enable_high_accuracy
);
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;
217 RendererGeolocationOptions opts
= {
218 enable_high_accuracy
,
221 geolocation_renderers_
[render_view_id
] = opts
;
223 it
->second
.high_accuracy
= enable_high_accuracy
;
225 RefreshGeolocationOptions();
228 void GeolocationDispatcherHostImpl::OnStopUpdating(int render_view_id
) {
229 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
230 DVLOG(1) << __FUNCTION__
<< " " << render_process_id_
<< ":"
232 DCHECK_EQ(1U, geolocation_renderers_
.count(render_view_id
));
233 geolocation_renderers_
.erase(render_view_id
);
234 RefreshGeolocationOptions();
237 void GeolocationDispatcherHostImpl::PauseOrResume(int render_view_id
,
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.
246 pending_paused_geolocation_renderers_
.insert(render_view_id
);
248 pending_paused_geolocation_renderers_
.erase(render_view_id
);
251 RendererGeolocationOptions
* opts
= &(it
->second
);
252 if (opts
->is_paused
!= should_pause
)
253 opts
->is_paused
= should_pause
;
254 RefreshGeolocationOptions();
258 void GeolocationDispatcherHostImpl::RefreshGeolocationOptions() {
259 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
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
)
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
);
278 if (geolocation_provider_
)
279 geolocation_provider_
->RemoveLocationUpdateCallback(callback_
);
280 geolocation_provider_
= NULL
;
287 // GeolocationDispatcherHost --------------------------------------------------
290 GeolocationDispatcherHost
* GeolocationDispatcherHost::New(
291 int render_process_id
,
292 GeolocationPermissionContext
* geolocation_permission_context
) {
293 return new GeolocationDispatcherHostImpl(
295 geolocation_permission_context
);
298 GeolocationDispatcherHost::GeolocationDispatcherHost()
299 : BrowserMessageFilter(GeolocationMsgStart
) {
302 GeolocationDispatcherHost::~GeolocationDispatcherHost() {
305 } // namespace content