Home | History | Annotate | Download | only in mac
      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