Index: chrome/browser/geolocation/core_location_data_provider_mac.mm |
diff --git a/chrome/browser/geolocation/core_location_data_provider_mac.mm b/chrome/browser/geolocation/core_location_data_provider_mac.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ae8e749480da4ad4b20bbe8d68f094b1211a3806 |
--- /dev/null |
+++ b/chrome/browser/geolocation/core_location_data_provider_mac.mm |
@@ -0,0 +1,241 @@ |
+// Copyright (c) 2010 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 "chrome/browser/geolocation/core_location_data_provider_mac.h" |
+#include "chrome/browser/geolocation/core_location_provider_mac.h" |
+#include "base/logging.h" |
+#include "base/time.h" |
+ |
+// 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 it's first |
+// successful call (to avoid |bundle_| being double initialized) |
+- (BOOL)locationDataAvailable; |
+ |
+// These should always be called from ChromeThread::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_ 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 |
+ |
+CoreLocationDataProviderMac::CoreLocationDataProviderMac() { |
+ if(!ChromeThread::GetCurrentThreadIdentifier(&origin_thread_id_)) |
+ NOTREACHED() << |
+ "CoreLocation data provider must be created in a valid ChromeThread."; |
+ 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; |
+ ChromeThread::PostTask(ChromeThread::UI, FROM_HERE, |
+ NewRunnableMethod(this, &CoreLocationDataProviderMac::StartUpdatingTask)); |
+ return true; |
+} |
+ |
+// Clears provider_ so that any leftover messages from CoreLocation get ignored |
+void CoreLocationDataProviderMac::StopUpdating() { |
+ provider_ = NULL; |
+ ChromeThread::PostTask(ChromeThread::UI, FROM_HERE, |
+ NewRunnableMethod(this, |
+ &CoreLocationDataProviderMac::StopUpdatingTask)); |
+} |
+ |
+void CoreLocationDataProviderMac::UpdatePosition(Geoposition *position) { |
+ ChromeThread::PostTask(origin_thread_id_, FROM_HERE, |
+ NewRunnableMethod(this, |
+ &CoreLocationDataProviderMac::PositionUpdated, |
+ *position)); |
+} |
+ |
+// Runs in ChromeThread::UI |
+void CoreLocationDataProviderMac::StartUpdatingTask() { |
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
+ [wrapper_ startLocation]; |
+} |
+ |
+// Runs in ChromeThread::UI |
+void CoreLocationDataProviderMac::StopUpdatingTask() { |
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
+ [wrapper_ stopLocation]; |
+} |
+ |
+void CoreLocationDataProviderMac::PositionUpdated(Geoposition position) { |
+ DCHECK(ChromeThread::CurrentlyOn(origin_thread_id_)); |
+ if(provider_) |
+ provider_->SetPosition(&position); |
+} |