1/* 2 * Copyright (C) 2009 Apple Inc. All Rights Reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#import "config.h" 27 28#if ENABLE(GEOLOCATION) && !ENABLE(CLIENT_BASED_GEOLOCATION) 29 30#import "GeolocationServiceMac.h" 31 32#import "Geoposition.h" 33#import "PositionError.h" 34#import "PositionOptions.h" 35#import "SoftLinking.h" 36#import <CoreLocation/CoreLocation.h> 37#import <objc/objc-runtime.h> 38#import <wtf/RefPtr.h> 39#import <wtf/UnusedParam.h> 40 41SOFT_LINK_FRAMEWORK(CoreLocation) 42 43SOFT_LINK_CLASS(CoreLocation, CLLocationManager) 44SOFT_LINK_CLASS(CoreLocation, CLLocation) 45 46SOFT_LINK_CONSTANT(CoreLocation, kCLLocationAccuracyBest, double) 47SOFT_LINK_CONSTANT(CoreLocation, kCLLocationAccuracyHundredMeters, double) 48 49#define kCLLocationAccuracyBest getkCLLocationAccuracyBest() 50#define kCLLocationAccuracyHundredMeters getkCLLocationAccuracyHundredMeters() 51 52using namespace WebCore; 53 54@interface WebCoreCoreLocationObserver : NSObject<CLLocationManagerDelegate> 55{ 56 GeolocationServiceMac* m_callback; 57} 58 59- (id)initWithCallback:(GeolocationServiceMac*)callback; 60 61- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation; 62- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error; 63 64@end 65 66namespace WebCore { 67 68GeolocationService* GeolocationServiceMac::create(GeolocationServiceClient* client) 69{ 70 return new GeolocationServiceMac(client); 71} 72 73GeolocationService::FactoryFunction* GeolocationService::s_factoryFunction = &GeolocationServiceMac::create; 74 75GeolocationServiceMac::GeolocationServiceMac(GeolocationServiceClient* client) 76 : GeolocationService(client) 77 , m_objcObserver(AdoptNS, [[WebCoreCoreLocationObserver alloc] initWithCallback:this]) 78{ 79} 80 81GeolocationServiceMac::~GeolocationServiceMac() 82{ 83 [m_locationManager.get() stopUpdatingLocation]; 84 m_locationManager.get().delegate = nil; 85} 86 87bool GeolocationServiceMac::startUpdating(PositionOptions* options) 88{ 89 #define CLLocationManager getCLLocationManagerClass() 90 if (!m_locationManager.get()) { 91 m_locationManager.adoptNS([[CLLocationManager alloc] init]); 92 m_locationManager.get().delegate = m_objcObserver.get(); 93 } 94 95 if (!m_locationManager.get().locationServicesEnabled) 96 return false; 97 98 if (options) { 99 // CLLocationAccuracy values suggested by Ron Huang. 100 CLLocationAccuracy accuracy = options->enableHighAccuracy() ? kCLLocationAccuracyBest : kCLLocationAccuracyHundredMeters; 101 m_locationManager.get().desiredAccuracy = accuracy; 102 } 103 104 // This can safely be called multiple times. 105 [m_locationManager.get() startUpdatingLocation]; 106 107 return true; 108 #undef CLLocationManager 109} 110 111void GeolocationServiceMac::stopUpdating() 112{ 113 [m_locationManager.get() stopUpdatingLocation]; 114} 115 116void GeolocationServiceMac::suspend() 117{ 118 [m_locationManager.get() stopUpdatingLocation]; 119} 120 121void GeolocationServiceMac::resume() 122{ 123 [m_locationManager.get() startUpdatingLocation]; 124} 125 126void GeolocationServiceMac::positionChanged(PassRefPtr<Geoposition> position) 127{ 128 m_lastPosition = position; 129 GeolocationService::positionChanged(); 130} 131 132void GeolocationServiceMac::errorOccurred(PassRefPtr<PositionError> error) 133{ 134 m_lastError = error; 135 GeolocationService::errorOccurred(); 136} 137 138} // namespace WebCore 139 140@implementation WebCoreCoreLocationObserver 141 142- (id)initWithCallback:(GeolocationServiceMac *)callback 143{ 144 self = [super init]; 145 if (self) 146 m_callback = callback; 147 return self; 148} 149 150- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation 151{ 152 ASSERT(m_callback); 153 ASSERT(newLocation); 154 UNUSED_PARAM(manager); 155 UNUSED_PARAM(oldLocation); 156 157 // Normalize 158 bool canProvideAltitude = true; 159 bool canProvideAltitudeAccuracy = true; 160 double altitude = newLocation.altitude; 161 double altitudeAccuracy = newLocation.verticalAccuracy; 162 if (altitudeAccuracy < 0.0) { 163 canProvideAltitude = false; 164 canProvideAltitudeAccuracy = false; 165 } 166 167 bool canProvideSpeed = true; 168 double speed = newLocation.speed; 169 if (speed < 0.0) 170 canProvideSpeed = false; 171 172 bool canProvideHeading = true; 173 double heading = newLocation.course; 174 if (heading < 0.0) 175 canProvideHeading = false; 176 177 WTF::RefPtr<WebCore::Coordinates> newCoordinates = WebCore::Coordinates::create( 178 newLocation.coordinate.latitude, 179 newLocation.coordinate.longitude, 180 canProvideAltitude, 181 altitude, 182 newLocation.horizontalAccuracy, 183 canProvideAltitudeAccuracy, 184 altitudeAccuracy, 185 canProvideHeading, 186 heading, 187 canProvideSpeed, 188 speed); 189 WTF::RefPtr<WebCore::Geoposition> newPosition = WebCore::Geoposition::create( 190 newCoordinates.release(), 191 [newLocation.timestamp timeIntervalSince1970] * 1000.0); // seconds -> milliseconds 192 193 m_callback->positionChanged(newPosition.release()); 194} 195 196- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error 197{ 198 ASSERT(m_callback); 199 ASSERT(error); 200 201 UNUSED_PARAM(manager); 202 203 PositionError::ErrorCode code; 204 switch ([error code]) { 205 case kCLErrorDenied: 206 code = PositionError::PERMISSION_DENIED; 207 break; 208 case kCLErrorLocationUnknown: 209 code = PositionError::POSITION_UNAVAILABLE; 210 break; 211 default: 212 code = PositionError::POSITION_UNAVAILABLE; 213 break; 214 } 215 216 m_callback->errorOccurred(PositionError::create(code, [error localizedDescription])); 217} 218 219@end 220 221#endif // ENABLE(GEOLOCATION) 222