[iOS] Fix the iOS build; add case statements for kCLAuthorizationStatusAuthorized
[WebKit-https.git] / Source / WebKit / ios / Misc / WebGeolocationCoreLocationProvider.mm
1 /*
2  * Copyright (C) 2008, 2009, 2010, 2012, 2014 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 #if PLATFORM(IOS)
27
28 #import "WebGeolocationCoreLocationProvider.h"
29
30 #import <CoreLocation/CLLocation.h>
31 #import <CoreLocation/CLLocationManagerDelegate.h>
32 #import <CoreLocation/CoreLocation.h>
33 #import <CoreLocation/CoreLocationPriv.h>
34 #import <WebCore/GeolocationPosition.h>
35 #import <WebCore/SoftLinking.h>
36 #import <WebKitLogging.h>
37 #import <objc/objc-runtime.h>
38 #import <wtf/RefPtr.h>
39 #import <wtf/RetainPtr.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 WebGeolocationCoreLocationProvider () <CLLocationManagerDelegate>
55 @end
56
57 @implementation WebGeolocationCoreLocationProvider
58 {
59     id<WebGeolocationCoreLocationUpdateListener> _positionListener;
60     RetainPtr<CLLocationManager> _locationManager;
61     BOOL _isWaitingForAuthorization;
62     CLAuthorizationStatus _lastAuthorizationStatus;
63 }
64
65 - (void)createLocationManager
66 {
67     ASSERT(!_locationManager);
68
69 #define CLLocationManager getCLLocationManagerClass()
70     _locationManager = adoptNS([[CLLocationManager alloc] init]);
71     _lastAuthorizationStatus = [CLLocationManager authorizationStatus];
72 #undef CLLocationManager
73
74     [ _locationManager setDelegate:self];
75 }
76
77 - (id)initWithListener:(id<WebGeolocationCoreLocationUpdateListener>)listener
78 {
79     ASSERT_MAIN_THREAD();
80     self = [super init];
81     if (self) {
82         _positionListener = listener;
83         [self createLocationManager];
84     }
85     return self;
86 }
87
88 - (void)dealloc
89 {
90     ASSERT_MAIN_THREAD();
91     [_locationManager setDelegate:nil];
92     [super dealloc];
93 }
94
95 - (void)requestGeolocationAuthorization
96 {
97     ASSERT_MAIN_THREAD();
98
99     if (![getCLLocationManagerClass() locationServicesEnabled]) {
100         [_positionListener geolocationAuthorizationDenied];
101         return;
102     }
103
104     switch ([getCLLocationManagerClass() authorizationStatus]) {
105     case kCLAuthorizationStatusNotDetermined: {
106         if (!_isWaitingForAuthorization) {
107             _isWaitingForAuthorization = YES;
108             [_locationManager requestWhenInUseAuthorization];
109         }
110         break;
111     }
112 #pragma clang diagnostic push
113 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
114     case kCLAuthorizationStatusAuthorized:
115 #pragma clang diagnostic pop
116     case kCLAuthorizationStatusAuthorizedAlways:
117     case kCLAuthorizationStatusAuthorizedWhenInUse: {
118         [_positionListener geolocationAuthorizationGranted];
119         break;
120     }
121     case kCLAuthorizationStatusRestricted:
122     case kCLAuthorizationStatusDenied:
123         [_positionListener geolocationAuthorizationDenied];
124         break;
125     }
126 }
127
128 static bool isAuthorizationGranted(CLAuthorizationStatus authorizationStatus)
129 {
130     return authorizationStatus == kCLAuthorizationStatusAuthorizedAlways || authorizationStatus == kCLAuthorizationStatusAuthorizedWhenInUse;
131 }
132
133 - (void)start
134 {
135     ASSERT_MAIN_THREAD();
136
137     if (![getCLLocationManagerClass() locationServicesEnabled]
138         || !isAuthorizationGranted([getCLLocationManagerClass() authorizationStatus])) {
139         [_locationManager stopUpdatingLocation];
140         [_positionListener resetGeolocation];
141         return;
142     }
143
144     [_locationManager startUpdatingLocation];
145 }
146
147 - (void)stop
148 {
149     ASSERT_MAIN_THREAD();
150     [_locationManager stopUpdatingLocation];
151 }
152
153 - (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
154 {
155     if (_isWaitingForAuthorization) {
156         switch (status) {
157         case kCLAuthorizationStatusNotDetermined:
158             // This can happen after resume if the user has still not answered the dialog. We just have to wait for the permission.
159             break;
160         case kCLAuthorizationStatusDenied:
161         case kCLAuthorizationStatusRestricted:
162             _isWaitingForAuthorization = NO;
163             [_positionListener geolocationAuthorizationDenied];
164             break;
165 #pragma clang diagnostic push
166 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
167         case kCLAuthorizationStatusAuthorized:
168 #pragma clang diagnostic pop
169         case kCLAuthorizationStatusAuthorizedAlways:
170         case kCLAuthorizationStatusAuthorizedWhenInUse:
171             _isWaitingForAuthorization = NO;
172             [_positionListener geolocationAuthorizationGranted];
173             break;
174         }
175     } else {
176         if (!(isAuthorizationGranted(_lastAuthorizationStatus) && isAuthorizationGranted(status))) {
177             [_locationManager stopUpdatingLocation];
178             [_positionListener resetGeolocation];
179         }
180     }
181     _lastAuthorizationStatus = status;
182 }
183
184 - (void)sendLocation:(CLLocation *)newLocation
185 {
186     // Normalize.
187     bool canProvideAltitude = true;
188     bool canProvideAltitudeAccuracy = true;
189     double altitude = newLocation.altitude;
190     double altitudeAccuracy = newLocation.verticalAccuracy;
191     if (altitudeAccuracy < 0.0) {
192         canProvideAltitude = false;
193         canProvideAltitudeAccuracy = false;
194     }
195
196     bool canProvideSpeed = true;
197     double speed = newLocation.speed;
198     if (speed < 0.0)
199         canProvideSpeed = false;
200
201     bool canProvideHeading = true;
202     double heading = newLocation.course;
203     if (heading < 0.0)
204         canProvideHeading = false;
205
206     double timestamp = [newLocation.timestamp timeIntervalSince1970];
207     RefPtr<GeolocationPosition> geolocationPosition = GeolocationPosition::create(timestamp, newLocation.coordinate.latitude, newLocation.coordinate.longitude, newLocation.horizontalAccuracy, canProvideAltitude, altitude, canProvideAltitudeAccuracy, altitudeAccuracy, canProvideHeading, heading, canProvideSpeed, speed);
208     [_positionListener positionChanged:geolocationPosition.get()];
209 }
210
211 - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
212 {
213     UNUSED_PARAM(manager);
214     for (CLLocation *location in locations)
215         [self sendLocation:location];
216 }
217
218 - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
219 {
220     ASSERT(_positionListener);
221     ASSERT(error);
222     UNUSED_PARAM(manager);
223
224     if ([error code] == kCLErrorDenied) {
225         // Ignore the error here and let locationManager:didChangeAuthorizationStatus: handle the permission.
226         return;
227     }
228
229     NSString *errorMessage = [error localizedDescription];
230     [_positionListener errorOccurred:errorMessage];
231 }
232
233 - (void)setEnableHighAccuracy:(BOOL)flag
234 {
235     ASSERT_MAIN_THREAD();
236     [_locationManager setDesiredAccuracy:flag ? kCLLocationAccuracyBest : kCLLocationAccuracyHundredMeters];
237 }
238
239 @end
240
241 #endif // PLATFORM(IOS)