summaryrefslogtreecommitdiff
path: root/chromium/content/browser/geolocation/core_location_data_provider_mac.mm
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/content/browser/geolocation/core_location_data_provider_mac.mm')
-rw-r--r--chromium/content/browser/geolocation/core_location_data_provider_mac.mm256
1 files changed, 256 insertions, 0 deletions
diff --git a/chromium/content/browser/geolocation/core_location_data_provider_mac.mm b/chromium/content/browser/geolocation/core_location_data_provider_mac.mm
new file mode 100644
index 00000000000..8b1cf82c28b
--- /dev/null
+++ b/chromium/content/browser/geolocation/core_location_data_provider_mac.mm
@@ -0,0 +1,256 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains the class definitions for the CoreLocation data provider
+// class and the accompanying Objective C wrapper class. This data provider
+// is used to allow the CoreLocation wrapper to run on the UI thread, since
+// CLLocationManager's start and stop updating methods must be called from a
+// thread with an active NSRunLoop. Currently only the UI thread appears to
+// fill that requirement.
+
+#include "content/browser/geolocation/core_location_data_provider_mac.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/time/time.h"
+#include "content/browser/geolocation/core_location_provider_mac.h"
+#include "content/browser/geolocation/geolocation_provider_impl.h"
+
+using content::CoreLocationDataProviderMac;
+using content::Geoposition;
+
+// A few required declarations since the CoreLocation headers are not available
+// with the Mac OS X 10.5 SDK.
+// TODO(jorgevillatoro): Remove these declarations when we build against 10.6
+
+// This idea was borrowed from wifi_data_provider_corewlan_mac.mm
+typedef double CLLocationDegrees;
+typedef double CLLocationAccuracy;
+typedef double CLLocationSpeed;
+typedef double CLLocationDirection;
+typedef double CLLocationDistance;
+typedef struct {
+ CLLocationDegrees latitude;
+ CLLocationDegrees longitude;
+} CLLocationCoordinate2D;
+
+enum {
+ kCLErrorLocationUnknown = 0,
+ kCLErrorDenied
+};
+
+@interface CLLocationManager : NSObject
++ (BOOL)locationServicesEnabled;
+@property(assign) id delegate;
+- (void)startUpdatingLocation;
+- (void)stopUpdatingLocation;
+@end
+
+@interface CLLocation : NSObject<NSCopying, NSCoding>
+@property(readonly) CLLocationCoordinate2D coordinate;
+@property(readonly) CLLocationDistance altitude;
+@property(readonly) CLLocationAccuracy horizontalAccuracy;
+@property(readonly) CLLocationAccuracy verticalAccuracy;
+@property(readonly) CLLocationDirection course;
+@property(readonly) CLLocationSpeed speed;
+@end
+
+@protocol CLLocationManagerDelegate
+- (void)locationManager:(CLLocationManager*)manager
+ didUpdateToLocation:(CLLocation*)newLocation
+ fromLocation:(CLLocation*)oldLocation;
+- (void)locationManager:(CLLocationManager*)manager
+ didFailWithError:(NSError*)error;
+@end
+
+// This wrapper class receives CLLocation objects from CoreLocation, converts
+// them to Geoposition objects, and passes them on to the data provider class
+// Note: This class has some specific threading requirements, inherited from
+// CLLocationManager. The location manaager's start and stop updating
+// methods must be called from a thread that has an active run loop (which
+// seems to only be the UI thread)
+@interface CoreLocationWrapperMac : NSObject<CLLocationManagerDelegate>
+{
+ @private
+ NSBundle* bundle_;
+ Class locationManagerClass_;
+ id locationManager_;
+ CoreLocationDataProviderMac* dataProvider_;
+}
+
+- (id)initWithDataProvider:(CoreLocationDataProviderMac*)dataProvider;
+- (void)dealloc;
+
+// Can be called from any thread since it does not require an NSRunLoop. However
+// it is not threadsafe to receive concurrent calls until after a first
+// successful call (to avoid |bundle_| being double initialized)
+- (BOOL)locationDataAvailable;
+
+// These should always be called from BrowserThread::UI
+- (void)startLocation;
+- (void)stopLocation;
+
+// These should only be called by CLLocationManager
+- (void)locationManager:(CLLocationManager*)manager
+ didUpdateToLocation:(CLLocation*)newLocation
+ fromLocation:(CLLocation*)oldLocation;
+- (void)locationManager:(CLLocationManager*)manager
+ didFailWithError:(NSError*)error;
+- (BOOL)loadCoreLocationBundle;
+
+@end
+
+@implementation CoreLocationWrapperMac
+
+- (id)initWithDataProvider:(CoreLocationDataProviderMac*)dataProvider {
+ DCHECK(dataProvider);
+ dataProvider_ = dataProvider;
+ self = [super init];
+ return self;
+}
+
+- (void)dealloc {
+ [locationManager_ setDelegate:nil];
+ [locationManager_ release];
+ [locationManagerClass_ release];
+ [bundle_ release];
+ [super dealloc];
+}
+
+// Load the bundle and check to see if location services are enabled
+// but don't do anything else
+- (BOOL)locationDataAvailable {
+ return ([self loadCoreLocationBundle] &&
+ [locationManagerClass_ locationServicesEnabled]);
+}
+
+- (void)startLocation {
+ if ([self locationDataAvailable]) {
+ if (!locationManager_) {
+ locationManager_ = [[locationManagerClass_ alloc] init];
+ [locationManager_ setDelegate:self];
+ }
+ [locationManager_ startUpdatingLocation];
+ }
+}
+
+- (void)stopLocation {
+ [locationManager_ stopUpdatingLocation];
+}
+
+- (void)locationManager:(CLLocationManager*)manager
+ didUpdateToLocation:(CLLocation*)newLocation
+ fromLocation:(CLLocation*)oldLocation {
+ Geoposition position;
+ position.latitude = [newLocation coordinate].latitude;
+ position.longitude = [newLocation coordinate].longitude;
+ position.altitude = [newLocation altitude];
+ position.accuracy = [newLocation horizontalAccuracy];
+ position.altitude_accuracy = [newLocation verticalAccuracy];
+ position.speed = [newLocation speed];
+ position.heading = [newLocation course];
+ position.timestamp = base::Time::Now();
+ position.error_code = Geoposition::ERROR_CODE_NONE;
+ dataProvider_->UpdatePosition(&position);
+}
+
+- (void)locationManager:(CLLocationManager*)manager
+ didFailWithError:(NSError*)error {
+ Geoposition position;
+ switch ([error code]) {
+ case kCLErrorLocationUnknown:
+ position.error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE;
+ break;
+ case kCLErrorDenied:
+ position.error_code = Geoposition::ERROR_CODE_PERMISSION_DENIED;
+ break;
+ default:
+ NOTREACHED() << "Unknown CoreLocation error: " << [error code];
+ return;
+ }
+ dataProvider_->UpdatePosition(&position);
+}
+
+- (BOOL)loadCoreLocationBundle {
+ if (!bundle_) {
+ bundle_ = [[NSBundle alloc]
+ initWithPath:@"/System/Library/Frameworks/CoreLocation.framework"];
+ if (!bundle_) {
+ DLOG(WARNING) << "Couldn't load CoreLocation Framework";
+ return NO;
+ }
+
+ locationManagerClass_ = [bundle_ classNamed:@"CLLocationManager"];
+ }
+
+ return YES;
+}
+
+@end
+
+namespace content {
+
+CoreLocationDataProviderMac::CoreLocationDataProviderMac() {
+ if (base::MessageLoop::current() !=
+ GeolocationProviderImpl::GetInstance()->message_loop()) {
+ NOTREACHED() << "CoreLocation data provider must be created on "
+ "the Geolocation thread.";
+ }
+ provider_ = NULL;
+ wrapper_.reset([[CoreLocationWrapperMac alloc] initWithDataProvider:this]);
+}
+
+CoreLocationDataProviderMac::~CoreLocationDataProviderMac() {
+}
+
+// Returns true if the CoreLocation wrapper can load the framework and
+// location services are enabled. The pointer argument will only be accessed
+// in the origin thread.
+bool CoreLocationDataProviderMac::
+ StartUpdating(CoreLocationProviderMac* provider) {
+ DCHECK(provider);
+ DCHECK(!provider_) << "StartUpdating called twice";
+ if (![wrapper_ locationDataAvailable]) return false;
+ provider_ = provider;
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&CoreLocationDataProviderMac::StartUpdatingTask, this));
+ return true;
+}
+
+// Clears provider_ so that any leftover messages from CoreLocation get ignored
+void CoreLocationDataProviderMac::StopUpdating() {
+ provider_ = NULL;
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&CoreLocationDataProviderMac::StopUpdatingTask, this));
+}
+
+void CoreLocationDataProviderMac::UpdatePosition(Geoposition *position) {
+ GeolocationProviderImpl::GetInstance()->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&CoreLocationDataProviderMac::PositionUpdated, this,
+ *position));
+}
+
+// Runs in BrowserThread::UI
+void CoreLocationDataProviderMac::StartUpdatingTask() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ [wrapper_ startLocation];
+}
+
+// Runs in BrowserThread::UI
+void CoreLocationDataProviderMac::StopUpdatingTask() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ [wrapper_ stopLocation];
+}
+
+void CoreLocationDataProviderMac::PositionUpdated(Geoposition position) {
+ DCHECK(base::MessageLoop::current() ==
+ GeolocationProviderImpl::GetInstance()->message_loop());
+ if (provider_)
+ provider_->SetPosition(&position);
+}
+
+} // namespace content