• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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