Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / extensions / api / location / location_manager.cc
blobe3015a72fe90b36b2e401ce6a1784e0703d02476
1 // Copyright (c) 2013 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 "chrome/browser/extensions/api/location/location_manager.h"
7 #include <math.h>
8 #include <vector>
10 #include "base/bind.h"
11 #include "base/lazy_instance.h"
12 #include "base/time/time.h"
13 #include "chrome/common/extensions/api/location.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "content/public/browser/geolocation_provider.h"
16 #include "content/public/common/geoposition.h"
17 #include "extensions/browser/event_router.h"
18 #include "extensions/browser/extension_registry.h"
19 #include "extensions/browser/extension_system.h"
20 #include "extensions/common/extension.h"
21 #include "extensions/common/permissions/permission_set.h"
22 #include "extensions/common/permissions/permissions_data.h"
24 using content::BrowserThread;
26 // TODO(vadimt): Add tests.
27 namespace extensions {
29 namespace location = api::location;
31 namespace updatepolicy {
33 // Base class for all update policies for sending a location.
34 class UpdatePolicy : public base::RefCounted<UpdatePolicy> {
35 public:
36 UpdatePolicy() {}
38 // True if the caller should send an update based off of this policy.
39 virtual bool ShouldSendUpdate(const content::Geoposition&) const = 0;
41 // Updates any policy state on reporting a position.
42 virtual void OnPositionReported(const content::Geoposition&) = 0;
44 protected:
45 virtual ~UpdatePolicy() {}
47 private:
48 friend class base::RefCounted<UpdatePolicy>;
49 DISALLOW_COPY_AND_ASSIGN(UpdatePolicy);
52 // A policy that controls sending an update below a distance threshold.
53 class DistanceBasedUpdatePolicy : public UpdatePolicy {
54 public:
55 explicit DistanceBasedUpdatePolicy(double distance_update_threshold_meters) :
56 distance_update_threshold_meters_(distance_update_threshold_meters)
59 // UpdatePolicy Implementation
60 bool ShouldSendUpdate(const content::Geoposition& position) const override {
61 return !last_updated_position_.Validate() ||
62 Distance(position.latitude,
63 position.longitude,
64 last_updated_position_.latitude,
65 last_updated_position_.longitude) >
66 distance_update_threshold_meters_;
69 void OnPositionReported(const content::Geoposition& position) override {
70 last_updated_position_ = position;
73 private:
74 ~DistanceBasedUpdatePolicy() override {}
76 // Calculates the distance between two latitude and longitude points.
77 static double Distance(const double latitude1,
78 const double longitude1,
79 const double latitude2,
80 const double longitude2) {
81 // The earth has a radius of about 6371 km.
82 const double kRadius = 6371000;
83 const double kPi = 3.14159265358979323846;
84 const double kDegreesToRadians = kPi / 180.0;
86 // Conversions
87 const double latitude1Rad = latitude1 * kDegreesToRadians;
88 const double latitude2Rad = latitude2 * kDegreesToRadians;
89 const double latitudeDistRad = latitude2Rad - latitude1Rad;
90 const double longitudeDistRad = (longitude2 - longitude1) *
91 kDegreesToRadians;
93 // The Haversine Formula determines the great circle distance
94 // between two points on a sphere.
95 const double chordLengthSquared = pow(sin(latitudeDistRad / 2.0), 2) +
96 (pow(sin(longitudeDistRad / 2.0), 2) *
97 cos(latitude1Rad) *
98 cos(latitude2Rad));
99 const double angularDistance = 2.0 * atan2(sqrt(chordLengthSquared),
100 sqrt(1.0 - chordLengthSquared));
101 return kRadius * angularDistance;
104 const double distance_update_threshold_meters_;
105 content::Geoposition last_updated_position_;
107 DISALLOW_COPY_AND_ASSIGN(DistanceBasedUpdatePolicy);
110 // A policy that controls sending an update above a time threshold.
111 class TimeBasedUpdatePolicy : public UpdatePolicy {
112 public:
113 explicit TimeBasedUpdatePolicy(double time_between_updates_ms) :
114 time_between_updates_ms_(time_between_updates_ms)
117 // UpdatePolicy Implementation
118 bool ShouldSendUpdate(const content::Geoposition&) const override {
119 return (base::Time::Now() - last_update_time_).InMilliseconds() >
120 time_between_updates_ms_;
123 void OnPositionReported(const content::Geoposition&) override {
124 last_update_time_ = base::Time::Now();
127 private:
128 ~TimeBasedUpdatePolicy() override {}
130 base::Time last_update_time_;
131 const double time_between_updates_ms_;
133 DISALLOW_COPY_AND_ASSIGN(TimeBasedUpdatePolicy);
136 } // namespace updatepolicy
138 // Request created by chrome.location.watchLocation() call.
139 class LocationRequest : public base::RefCounted<LocationRequest> {
140 public:
141 LocationRequest(
142 LocationManager* location_manager,
143 const std::string& extension_id,
144 const std::string& request_name,
145 const double* distance_update_threshold_meters,
146 const double* time_between_updates_ms);
148 const std::string& request_name() const { return request_name_; }
150 private:
151 friend class base::RefCounted<LocationRequest>;
152 ~LocationRequest();
154 void OnLocationUpdate(const content::Geoposition& position);
156 // Determines if all policies say to send a position update.
157 // If there are no policies, this always says yes.
158 bool ShouldSendUpdate(const content::Geoposition& position);
160 // Updates the policies on sending a position update.
161 void OnPositionReported(const content::Geoposition& position);
163 // Request name.
164 const std::string request_name_;
166 // Id of the owner extension.
167 const std::string extension_id_;
169 // Owning location manager.
170 LocationManager* location_manager_;
172 // Holds Update Policies.
173 typedef std::vector<scoped_refptr<updatepolicy::UpdatePolicy> >
174 UpdatePolicyVector;
175 UpdatePolicyVector update_policies_;
177 scoped_ptr<content::GeolocationProvider::Subscription>
178 geolocation_subscription_;
180 DISALLOW_COPY_AND_ASSIGN(LocationRequest);
183 LocationRequest::LocationRequest(
184 LocationManager* location_manager,
185 const std::string& extension_id,
186 const std::string& request_name,
187 const double* distance_update_threshold_meters,
188 const double* time_between_updates_ms)
189 : request_name_(request_name),
190 extension_id_(extension_id),
191 location_manager_(location_manager) {
192 // TODO(vadimt): use request_info.
193 if (time_between_updates_ms) {
194 update_policies_.push_back(
195 new updatepolicy::TimeBasedUpdatePolicy(
196 *time_between_updates_ms));
199 if (distance_update_threshold_meters) {
200 update_policies_.push_back(
201 new updatepolicy::DistanceBasedUpdatePolicy(
202 *distance_update_threshold_meters));
205 // TODO(vadimt): This can get a location cached by GeolocationProvider,
206 // contrary to the API definition which says that creating a location watch
207 // will get new location.
208 geolocation_subscription_ = content::GeolocationProvider::GetInstance()->
209 AddLocationUpdateCallback(
210 base::Bind(&LocationRequest::OnLocationUpdate,
211 base::Unretained(this)),
212 true);
215 LocationRequest::~LocationRequest() {
218 void LocationRequest::OnLocationUpdate(const content::Geoposition& position) {
219 if (ShouldSendUpdate(position)) {
220 OnPositionReported(position);
221 location_manager_->SendLocationUpdate(
222 extension_id_, request_name_, position);
226 bool LocationRequest::ShouldSendUpdate(const content::Geoposition& position) {
227 for (UpdatePolicyVector::iterator it = update_policies_.begin();
228 it != update_policies_.end();
229 ++it) {
230 if (!((*it)->ShouldSendUpdate(position))) {
231 return false;
234 return true;
237 void LocationRequest::OnPositionReported(const content::Geoposition& position) {
238 for (UpdatePolicyVector::iterator it = update_policies_.begin();
239 it != update_policies_.end();
240 ++it) {
241 (*it)->OnPositionReported(position);
245 LocationManager::LocationManager(content::BrowserContext* context)
246 : browser_context_(context), extension_registry_observer_(this) {
247 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
250 void LocationManager::AddLocationRequest(
251 const std::string& extension_id,
252 const std::string& request_name,
253 const double* distance_update_threshold_meters,
254 const double* time_between_updates_ms) {
255 DCHECK_CURRENTLY_ON(BrowserThread::UI);
256 // TODO(vadimt): Consider resuming requests after restarting the browser.
258 // Override any old request with the same name.
259 RemoveLocationRequest(extension_id, request_name);
261 LocationRequestPointer location_request =
262 new LocationRequest(this,
263 extension_id,
264 request_name,
265 distance_update_threshold_meters,
266 time_between_updates_ms);
267 location_requests_.insert(
268 LocationRequestMap::value_type(extension_id, location_request));
271 void LocationManager::RemoveLocationRequest(const std::string& extension_id,
272 const std::string& name) {
273 DCHECK_CURRENTLY_ON(BrowserThread::UI);
275 std::pair<LocationRequestMap::iterator, LocationRequestMap::iterator>
276 extension_range = location_requests_.equal_range(extension_id);
278 for (LocationRequestMap::iterator it = extension_range.first;
279 it != extension_range.second;
280 ++it) {
281 if (it->second->request_name() == name) {
282 location_requests_.erase(it);
283 return;
288 LocationManager::~LocationManager() {
291 void LocationManager::GeopositionToApiCoordinates(
292 const content::Geoposition& position,
293 location::Coordinates* coordinates) {
294 coordinates->latitude = position.latitude;
295 coordinates->longitude = position.longitude;
296 if (position.altitude > -10000.)
297 coordinates->altitude.reset(new double(position.altitude));
298 coordinates->accuracy = position.accuracy;
299 if (position.altitude_accuracy >= 0.) {
300 coordinates->altitude_accuracy.reset(
301 new double(position.altitude_accuracy));
303 if (position.heading >= 0. && position.heading <= 360.)
304 coordinates->heading.reset(new double(position.heading));
305 if (position.speed >= 0.)
306 coordinates->speed.reset(new double(position.speed));
309 void LocationManager::SendLocationUpdate(
310 const std::string& extension_id,
311 const std::string& request_name,
312 const content::Geoposition& position) {
313 DCHECK_CURRENTLY_ON(BrowserThread::UI);
315 scoped_ptr<base::ListValue> args(new base::ListValue());
316 events::HistogramValue histogram_value = events::UNKNOWN;
317 std::string event_name;
319 if (position.Validate() &&
320 position.error_code == content::Geoposition::ERROR_CODE_NONE) {
321 // Set data for onLocationUpdate event.
322 location::Location location;
323 location.name = request_name;
324 GeopositionToApiCoordinates(position, &location.coords);
325 location.timestamp = position.timestamp.ToJsTime();
327 args->Append(location.ToValue().release());
328 histogram_value = events::LOCATION_ON_LOCATION_UPDATE;
329 event_name = location::OnLocationUpdate::kEventName;
330 } else {
331 // Set data for onLocationError event.
332 // TODO(vadimt): Set name.
333 args->AppendString(position.error_message);
334 histogram_value = events::LOCATION_ON_LOCATION_ERROR;
335 event_name = location::OnLocationError::kEventName;
338 scoped_ptr<Event> event(new Event(histogram_value, event_name, args.Pass()));
340 EventRouter::Get(browser_context_)
341 ->DispatchEventToExtension(extension_id, event.Pass());
344 void LocationManager::OnExtensionLoaded(
345 content::BrowserContext* browser_context,
346 const Extension* extension) {
347 // Grants permission to use geolocation once an extension with "location"
348 // permission is loaded.
349 if (extension->permissions_data()->HasAPIPermission(
350 APIPermission::kLocation)) {
351 content::GeolocationProvider::GetInstance()
352 ->UserDidOptIntoLocationServices();
356 void LocationManager::OnExtensionUnloaded(
357 content::BrowserContext* browser_context,
358 const Extension* extension,
359 UnloadedExtensionInfo::Reason reason) {
360 // Delete all requests from the unloaded extension.
361 location_requests_.erase(extension->id());
364 static base::LazyInstance<BrowserContextKeyedAPIFactory<LocationManager> >
365 g_factory = LAZY_INSTANCE_INITIALIZER;
367 // static
368 BrowserContextKeyedAPIFactory<LocationManager>*
369 LocationManager::GetFactoryInstance() {
370 return g_factory.Pointer();
373 // static
374 LocationManager* LocationManager::Get(content::BrowserContext* context) {
375 return BrowserContextKeyedAPIFactory<LocationManager>::Get(context);
378 } // namespace extensions