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 41 SOFT_LINK_FRAMEWORK(CoreLocation) 42 43 SOFT_LINK_CLASS(CoreLocation, CLLocationManager) 44 SOFT_LINK_CLASS(CoreLocation, CLLocation) 45 46 SOFT_LINK_CONSTANT(CoreLocation, kCLLocationAccuracyBest, double) 47 SOFT_LINK_CONSTANT(CoreLocation, kCLLocationAccuracyHundredMeters, double) 48 49 #define kCLLocationAccuracyBest getkCLLocationAccuracyBest() 50 #define kCLLocationAccuracyHundredMeters getkCLLocationAccuracyHundredMeters() 51 52 using 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 66 namespace WebCore { 67 68 GeolocationService* GeolocationServiceMac::create(GeolocationServiceClient* client) 69 { 70 return new GeolocationServiceMac(client); 71 } 72 73 GeolocationService::FactoryFunction* GeolocationService::s_factoryFunction = &GeolocationServiceMac::create; 74 75 GeolocationServiceMac::GeolocationServiceMac(GeolocationServiceClient* client) 76 : GeolocationService(client) 77 , m_objcObserver(AdoptNS, [[WebCoreCoreLocationObserver alloc] initWithCallback:this]) 78 { 79 } 80 81 GeolocationServiceMac::~GeolocationServiceMac() 82 { 83 [m_locationManager.get() stopUpdatingLocation]; 84 m_locationManager.get().delegate = nil; 85 } 86 87 bool 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 111 void GeolocationServiceMac::stopUpdating() 112 { 113 [m_locationManager.get() stopUpdatingLocation]; 114 } 115 116 void GeolocationServiceMac::suspend() 117 { 118 [m_locationManager.get() stopUpdatingLocation]; 119 } 120 121 void GeolocationServiceMac::resume() 122 { 123 [m_locationManager.get() startUpdatingLocation]; 124 } 125 126 void GeolocationServiceMac::positionChanged(PassRefPtr<Geoposition> position) 127 { 128 m_lastPosition = position; 129 GeolocationService::positionChanged(); 130 } 131 132 void 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