[content shell] implement testRunner.overridePreference
[chromium-blink-merge.git] / content / browser / geolocation / core_location_data_provider_mac.mm
blob51d09add5aa1613619a60611bbd3d83e6d73e980
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 // This file contains the class definitions for the CoreLocation data provider
6 // class and the accompanying Objective C wrapper class. This data provider
7 // is used to allow the CoreLocation wrapper to run on the UI thread, since
8 // CLLocationManager's start and stop updating methods must be called from a
9 // thread with an active NSRunLoop.  Currently only the UI thread appears to
10 // fill that requirement.
12 #include "content/browser/geolocation/core_location_data_provider_mac.h"
14 #include "base/bind.h"
15 #include "base/logging.h"
16 #include "base/time.h"
17 #include "content/browser/geolocation/core_location_provider_mac.h"
18 #include "content/browser/geolocation/geolocation_provider.h"
20 using content::CoreLocationDataProviderMac;
21 using content::Geoposition;
23 // A few required declarations since the CoreLocation headers are not available
24 // with the Mac OS X 10.5 SDK.
25 // TODO(jorgevillatoro): Remove these declarations when we build against 10.6
27 // This idea was borrowed from wifi_data_provider_corewlan_mac.mm
28 typedef double CLLocationDegrees;
29 typedef double CLLocationAccuracy;
30 typedef double CLLocationSpeed;
31 typedef double CLLocationDirection;
32 typedef double CLLocationDistance;
33 typedef struct {
34   CLLocationDegrees latitude;
35   CLLocationDegrees longitude;
36 } CLLocationCoordinate2D;
38 enum {
39   kCLErrorLocationUnknown  = 0,
40   kCLErrorDenied
43 @interface CLLocationManager : NSObject
44 + (BOOL)locationServicesEnabled;
45 @property(assign) id delegate;
46 - (void)startUpdatingLocation;
47 - (void)stopUpdatingLocation;
48 @end
50 @interface CLLocation : NSObject<NSCopying, NSCoding>
51 @property(readonly) CLLocationCoordinate2D coordinate;
52 @property(readonly) CLLocationDistance altitude;
53 @property(readonly) CLLocationAccuracy horizontalAccuracy;
54 @property(readonly) CLLocationAccuracy verticalAccuracy;
55 @property(readonly) CLLocationDirection course;
56 @property(readonly) CLLocationSpeed speed;
57 @end
59 @protocol CLLocationManagerDelegate
60 - (void)locationManager:(CLLocationManager*)manager
61     didUpdateToLocation:(CLLocation*)newLocation
62            fromLocation:(CLLocation*)oldLocation;
63 - (void)locationManager:(CLLocationManager*)manager
64        didFailWithError:(NSError*)error;
65 @end
67 // This wrapper class receives CLLocation objects from CoreLocation, converts
68 // them to Geoposition objects, and passes them on to the data provider class
69 // Note: This class has some specific threading requirements, inherited from
70 //       CLLocationManager.  The location manaager's start and stop updating
71 //       methods must be called from a thread that has an active run loop (which
72 //       seems to only be the UI thread)
73 @interface CoreLocationWrapperMac : NSObject<CLLocationManagerDelegate>
75  @private
76   NSBundle* bundle_;
77   Class locationManagerClass_;
78   id locationManager_;
79   CoreLocationDataProviderMac* dataProvider_;
82 - (id)initWithDataProvider:(CoreLocationDataProviderMac*)dataProvider;
83 - (void)dealloc;
85 // Can be called from any thread since it does not require an NSRunLoop. However
86 // it is not threadsafe to receive concurrent calls until after a first
87 // successful call (to avoid |bundle_| being double initialized)
88 - (BOOL)locationDataAvailable;
90 // These should always be called from BrowserThread::UI
91 - (void)startLocation;
92 - (void)stopLocation;
94 // These should only be called by CLLocationManager
95 - (void)locationManager:(CLLocationManager*)manager
96     didUpdateToLocation:(CLLocation*)newLocation
97            fromLocation:(CLLocation*)oldLocation;
98 - (void)locationManager:(CLLocationManager*)manager
99        didFailWithError:(NSError*)error;
100 - (BOOL)loadCoreLocationBundle;
102 @end
104 @implementation CoreLocationWrapperMac
106 - (id)initWithDataProvider:(CoreLocationDataProviderMac*)dataProvider {
107   DCHECK(dataProvider);
108   dataProvider_ = dataProvider;
109   self = [super init];
110   return self;
113 - (void)dealloc {
114   [locationManager_ release];
115   [locationManagerClass_ release];
116   [bundle_ release];
117   [super dealloc];
120 // Load the bundle and check to see if location services are enabled
121 // but don't do anything else
122 - (BOOL)locationDataAvailable {
123   return ([self loadCoreLocationBundle] &&
124           [locationManagerClass_ locationServicesEnabled]);
127 - (void)startLocation {
128   if ([self locationDataAvailable]) {
129     if (!locationManager_) {
130       locationManager_ = [[locationManagerClass_ alloc] init];
131       [locationManager_ setDelegate:self];
132     }
133     [locationManager_ startUpdatingLocation];
134   }
137 - (void)stopLocation {
138   [locationManager_ stopUpdatingLocation];
141 - (void)locationManager:(CLLocationManager*)manager
142     didUpdateToLocation:(CLLocation*)newLocation
143            fromLocation:(CLLocation*)oldLocation {
144   Geoposition position;
145   position.latitude  = [newLocation coordinate].latitude;
146   position.longitude = [newLocation coordinate].longitude;
147   position.altitude  = [newLocation altitude];
148   position.accuracy  = [newLocation horizontalAccuracy];
149   position.altitude_accuracy = [newLocation verticalAccuracy];
150   position.speed = [newLocation speed];
151   position.heading = [newLocation course];
152   position.timestamp = base::Time::Now();
153   position.error_code = Geoposition::ERROR_CODE_NONE;
154   dataProvider_->UpdatePosition(&position);
157 - (void)locationManager:(CLLocationManager*)manager
158        didFailWithError:(NSError*)error {
159   Geoposition position;
160   switch ([error code]) {
161     case kCLErrorLocationUnknown:
162       position.error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE;
163       break;
164     case kCLErrorDenied:
165       position.error_code = Geoposition::ERROR_CODE_PERMISSION_DENIED;
166       break;
167     default:
168       NOTREACHED() << "Unknown CoreLocation error: " << [error code];
169       return;
170   }
171   dataProvider_->UpdatePosition(&position);
174 - (BOOL)loadCoreLocationBundle {
175   if (!bundle_) {
176     bundle_ = [[NSBundle alloc]
177         initWithPath:@"/System/Library/Frameworks/CoreLocation.framework"];
178     if (!bundle_) {
179       DLOG(WARNING) << "Couldn't load CoreLocation Framework";
180       return NO;
181     }
183     locationManagerClass_ = [bundle_ classNamed:@"CLLocationManager"];
184   }
186   return YES;
189 @end
191 namespace content {
193 CoreLocationDataProviderMac::CoreLocationDataProviderMac() {
194   if (MessageLoop::current() !=
195       GeolocationProvider::GetInstance()->message_loop()) {
196     NOTREACHED() << "CoreLocation data provider must be created on "
197         "the Geolocation thread.";
198   }
199   provider_ = NULL;
200   wrapper_.reset([[CoreLocationWrapperMac alloc] initWithDataProvider:this]);
203 CoreLocationDataProviderMac::~CoreLocationDataProviderMac() {
206 // Returns true if the CoreLocation wrapper can load the framework and
207 // location services are enabled.  The pointer argument will only be accessed
208 // in the origin thread.
209 bool CoreLocationDataProviderMac::
210     StartUpdating(CoreLocationProviderMac* provider) {
211   DCHECK(provider);
212   DCHECK(!provider_) << "StartUpdating called twice";
213   if (![wrapper_ locationDataAvailable]) return false;
214   provider_ = provider;
215   BrowserThread::PostTask(
216       BrowserThread::UI, FROM_HERE,
217       base::Bind(&CoreLocationDataProviderMac::StartUpdatingTask, this));
218   return true;
221 // Clears provider_ so that any leftover messages from CoreLocation get ignored
222 void CoreLocationDataProviderMac::StopUpdating() {
223   provider_ = NULL;
224   BrowserThread::PostTask(
225       BrowserThread::UI, FROM_HERE,
226       base::Bind(&CoreLocationDataProviderMac::StopUpdatingTask, this));
229 void CoreLocationDataProviderMac::UpdatePosition(Geoposition *position) {
230   GeolocationProvider::GetInstance()->message_loop()->PostTask(
231       FROM_HERE,
232       base::Bind(&CoreLocationDataProviderMac::PositionUpdated, this,
233                  *position));
236 // Runs in BrowserThread::UI
237 void CoreLocationDataProviderMac::StartUpdatingTask() {
238   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
239   [wrapper_ startLocation];
242 // Runs in BrowserThread::UI
243 void CoreLocationDataProviderMac::StopUpdatingTask() {
244   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
245   [wrapper_ stopLocation];
248 void CoreLocationDataProviderMac::PositionUpdated(Geoposition position) {
249   DCHECK(MessageLoop::current() ==
250          GeolocationProvider::GetInstance()->message_loop());
251   if (provider_)
252     provider_->SetPosition(&position);
255 }  // namespace content