geolocation_dispatcher_host.cc revision b2df76ea8fec9e32f6f3718986dba0d95315b29c
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
20namespace content {
21namespace {
22
23void NotifyArbitratorPermissionGranted() {
24  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
25  GeolocationProviderImpl::GetInstance()->UserDidOptIntoLocationServices();
26}
27
28void 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
46class 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
95GeolocationDispatcherHostImpl::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
108GeolocationDispatcherHostImpl::~GeolocationDispatcherHostImpl() {
109  if (location_provider_)
110    location_provider_->RemoveLocationUpdateCallback(callback_);
111}
112
113bool 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
130void 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
139void 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_) {
147    geolocation_permission_context_->RequestGeolocationPermission(
148        render_process_id_, render_view_id, bridge_id, requesting_frame,
149        base::Bind(&SendGeolocationPermissionResponse, render_process_id_,
150                   render_view_id, bridge_id));
151  } else {
152    BrowserThread::PostTask(
153        BrowserThread::UI, FROM_HERE,
154        base::Bind(&SendGeolocationPermissionResponse, render_process_id_,
155                   render_view_id, bridge_id, true));
156  }
157}
158
159void GeolocationDispatcherHostImpl::OnCancelPermissionRequest(
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_) {
167  geolocation_permission_context_->CancelGeolocationPermissionRequest(
168        render_process_id_, render_view_id, bridge_id, requesting_frame);
169  }
170}
171
172void GeolocationDispatcherHostImpl::OnStartUpdating(
173    int render_view_id,
174    const GURL& requesting_frame,
175    bool enable_high_accuracy) {
176  // StartUpdating() can be invoked as a result of high-accuracy mode
177  // being enabled / disabled. No need to record the dispatcher again.
178  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
179  DVLOG(1) << __FUNCTION__ << " " << render_process_id_ << ":"
180           << render_view_id;
181  if (!geolocation_renderer_ids_.count(render_view_id))
182    geolocation_renderer_ids_.insert(render_view_id);
183
184  renderer_high_accuracy_[render_view_id] = enable_high_accuracy;
185  RefreshHighAccuracy();
186}
187
188void GeolocationDispatcherHostImpl::OnStopUpdating(int render_view_id) {
189  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
190  DVLOG(1) << __FUNCTION__ << " " << render_process_id_ << ":"
191           << render_view_id;
192  if (renderer_high_accuracy_.erase(render_view_id))
193    RefreshHighAccuracy();
194
195  DCHECK_EQ(1U, geolocation_renderer_ids_.count(render_view_id));
196  geolocation_renderer_ids_.erase(render_view_id);
197}
198
199void GeolocationDispatcherHostImpl::RefreshHighAccuracy() {
200  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
201  if (renderer_high_accuracy_.empty()) {
202    if (location_provider_) {
203      location_provider_->RemoveLocationUpdateCallback(callback_);
204      location_provider_ = NULL;
205    }
206  } else {
207    if (!location_provider_)
208      location_provider_ = GeolocationProviderImpl::GetInstance();
209    // Re-add to re-establish our options, in case they changed.
210    bool use_high_accuracy = false;
211    std::map<int, bool>::iterator i = renderer_high_accuracy_.begin();
212    for (; i != renderer_high_accuracy_.end(); ++i) {
213      if (i->second) {
214        use_high_accuracy = true;
215        break;
216      }
217    }
218    location_provider_->AddLocationUpdateCallback(callback_, use_high_accuracy);
219  }
220}
221}  // namespace
222
223
224// GeolocationDispatcherHost --------------------------------------------------
225
226// static
227GeolocationDispatcherHost* GeolocationDispatcherHost::New(
228    int render_process_id,
229    GeolocationPermissionContext* geolocation_permission_context) {
230  return new GeolocationDispatcherHostImpl(
231      render_process_id,
232      geolocation_permission_context);
233}
234
235GeolocationDispatcherHost::GeolocationDispatcherHost() {
236}
237
238GeolocationDispatcherHost::~GeolocationDispatcherHost() {
239}
240
241}  // namespace content
242