Remove WebContext::sharedProcessContext()
[WebKit-https.git] / Source / WebKit2 / UIProcess / API / ios / WKGeolocationProviderIOS.mm
1 /*
2  * Copyright (C) 2012 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. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "WKGeolocationProviderIOS.h"
28
29 #import "GeolocationPermissionRequestProxy.h"
30 #import "WebContext.h"
31 #import "WebGeolocationManagerProxy.h"
32 #import "WebSecurityOrigin.h"
33 #import <WebGeolocationPosition.h>
34 #import <WebCore/GeolocationPosition.h>
35 #import <WebKit2/WKGeolocationPermissionRequest.h>
36 #import <wtf/Assertions.h>
37 #import <wtf/PassRefPtr.h>
38 #import <wtf/RefPtr.h>
39 #import <wtf/RetainPtr.h>
40 #import <wtf/HashSet.h>
41
42 // FIXME: Remove use of WebKit1 from WebKit2
43 #import <WebKit/WebGeolocationCoreLocationProvider.h>
44 #import <WebKit/WebAllowDenyPolicyListener.h>
45
46 using namespace WebKit;
47
48 @interface WKGeolocationProviderIOS ()
49 -(void)_startUpdating;
50 -(void)_stopUpdating;
51 -(void)_setEnableHighAccuracy:(BOOL)enable;
52 @end
53
54 @interface WKGeolocationProviderIOS (WebGeolocationCoreLocationUpdateListener) <WebGeolocationCoreLocationUpdateListener>
55 @end
56
57 @interface WKWebAllowDenyPolicyListener : NSObject<WebAllowDenyPolicyListener>
58 - (id)initWithProvider:(WKGeolocationProviderIOS*)provider permissionRequestProxy:(PassRefPtr<GeolocationPermissionRequestProxy>)permissionRequestProxy;
59 - (void)denyOnlyThisRequest NO_RETURN_DUE_TO_ASSERT;
60 @end
61
62 namespace WebKit {
63 void decidePolicyForGeolocationRequestFromOrigin(WebCore::SecurityOrigin*, const String& urlString, id<WebAllowDenyPolicyListener>, UIWindow*);
64 };
65
66 static void startUpdatingCallback(WKGeolocationManagerRef geolocationManager, const void* clientInfo)
67 {
68     WKGeolocationProviderIOS *geolocationProvider = reinterpret_cast<WKGeolocationProviderIOS*>(const_cast<void*>(clientInfo));
69     ASSERT([geolocationProvider isKindOfClass:[WKGeolocationProviderIOS class]]);
70     [geolocationProvider _startUpdating];
71 }
72
73 static void stopUpdatingCallback(WKGeolocationManagerRef geolocationManager, const void* clientInfo)
74 {
75     WKGeolocationProviderIOS *geolocationProvider = reinterpret_cast<WKGeolocationProviderIOS*>(const_cast<void*>(clientInfo));
76     ASSERT([geolocationProvider isKindOfClass:[WKGeolocationProviderIOS class]]);
77     [geolocationProvider _stopUpdating];
78 }
79
80 static void setEnableHighAccuracy(WKGeolocationManagerRef geolocationManager, bool enable, const void* clientInfo)
81 {
82     WKGeolocationProviderIOS *geolocationProvider = reinterpret_cast<WKGeolocationProviderIOS*>(const_cast<void*>(clientInfo));
83     ASSERT([geolocationProvider isKindOfClass:[WKGeolocationProviderIOS class]]);
84     [geolocationProvider _setEnableHighAccuracy:enable];
85 }
86
87 struct GeolocationRequestData {
88     RefPtr<WebCore::SecurityOrigin> origin;
89     RefPtr<WebFrameProxy> frame;
90     RefPtr<GeolocationPermissionRequestProxy> permissionRequest;
91     RetainPtr<UIWindow> window;
92 };
93
94 @implementation WKGeolocationProviderIOS {
95     RefPtr<WebGeolocationManagerProxy> _geolocationManager;
96     RetainPtr<WebGeolocationCoreLocationProvider> _coreLocationProvider;
97     BOOL _isWebCoreGeolocationActive;
98     RefPtr<WebGeolocationPosition> _lastActivePosition;
99     Vector<GeolocationRequestData> _requestsWaitingForCoreLocationStart;
100     HashSet<void*> _requestsInWarmUp;
101 }
102
103 -(void)_stopUpdatingIfPossible
104 {
105     if (_isWebCoreGeolocationActive)
106         return;
107
108     if (_requestsWaitingForCoreLocationStart.isEmpty() && _requestsInWarmUp.isEmpty()) {
109         [_coreLocationProvider.get() stop];
110         _lastActivePosition.clear();
111     }
112 }
113
114 #pragma mark - WKGeolocationProvider callbacks implementation.
115 -(void)_startUpdating
116 {
117     _isWebCoreGeolocationActive = YES;
118     [_coreLocationProvider.get() start];
119
120     // If we have the last position, it is from the initialization or warm up. It is the last known
121     // good position so we can return it directly.
122     if (_lastActivePosition)
123         _geolocationManager->providerDidChangePosition(_lastActivePosition.get());
124 }
125
126 -(void)_stopUpdating
127 {
128     _isWebCoreGeolocationActive = NO;
129     [self _stopUpdatingIfPossible];
130 }
131
132 -(void)_setEnableHighAccuracy:(BOOL)enableHighAccuracy
133 {
134     [_coreLocationProvider.get() setEnableHighAccuracy:enableHighAccuracy];
135 }
136
137 #pragma mark - Public API implementation.
138
139 -(id)init
140 {
141     ASSERT_NOT_REACHED();
142     [self release];
143     return nil;
144 }
145
146 -(id)initWithContext:(WebContext*)context
147 {
148     self = [super init];
149     if (!self)
150         return nil;
151     _geolocationManager = context->supplement<WebGeolocationManagerProxy>();
152     WKGeolocationProvider providerCallback = {
153         kWKGeolocationProviderCurrentVersion,
154         self,
155         startUpdatingCallback,
156         stopUpdatingCallback,
157         setEnableHighAccuracy
158     };
159     _geolocationManager->initializeProvider(reinterpret_cast<WKGeolocationProviderBase*>(&providerCallback));
160     _coreLocationProvider = adoptNS([[WebGeolocationCoreLocationProvider alloc] initWithListener:self]);
161     return self;
162 }
163
164 -(void)decidePolicyForGeolocationRequestFromOrigin:(WKSecurityOriginRef)origin frame:(WKFrameRef)frame request:(WKGeolocationPermissionRequestRef)permissionRequest window:(UIWindow*)window
165 {
166     GeolocationRequestData geolocationRequestData;
167     geolocationRequestData.origin = toImpl(origin)->securityOrigin();
168     geolocationRequestData.frame = toImpl(frame);
169     geolocationRequestData.permissionRequest = toImpl(permissionRequest);
170     geolocationRequestData.window = window;
171     _requestsWaitingForCoreLocationStart.append(geolocationRequestData);
172     [_coreLocationProvider.get() start];
173 }
174 @end
175
176 #pragma mark - WebGeolocationCoreLocationUpdateListener implementation.
177
178 @implementation WKGeolocationProviderIOS (WebGeolocationCoreLocationUpdateListener)
179 - (void)geolocationDelegateStarted
180 {
181     Vector<GeolocationRequestData> requests;
182     requests.swap(_requestsWaitingForCoreLocationStart);
183     HashSet<void*> latestRequestsForWarmup;
184     for (size_t i = 0; i < requests.size(); ++i) {
185         GeolocationPermissionRequestProxy* permissionRequest = requests[i].permissionRequest.get();
186         latestRequestsForWarmup.add(permissionRequest);
187         _requestsInWarmUp.add(permissionRequest);
188     }
189
190     // Start the warmup period in which we keep CoreLocation running while waiting for the user response.
191     const int64_t warmUpTimeoutInterval = 20 * NSEC_PER_SEC;
192     dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, warmUpTimeoutInterval);
193     dispatch_after(when, dispatch_get_main_queue(), ^{
194         HashSet<void*>::const_iterator end = latestRequestsForWarmup.end();
195         for (HashSet<void*>::const_iterator it = latestRequestsForWarmup.begin(); it != end; ++it)
196             _requestsInWarmUp.remove(*it);
197         [self _stopUpdatingIfPossible];
198     });
199
200     for (size_t i = 0; i < requests.size(); ++i) {
201         RetainPtr<WKWebAllowDenyPolicyListener> policyListener = adoptNS([[WKWebAllowDenyPolicyListener alloc] initWithProvider:self permissionRequestProxy:requests[i].permissionRequest.get()]);
202         decidePolicyForGeolocationRequestFromOrigin(requests[i].origin.get(), requests[i].frame->url(), policyListener.get(), requests[i].window.get());
203     }
204 }
205
206 - (void)geolocationDelegateUnableToStart
207 {
208     for (size_t i = 0; i < _requestsWaitingForCoreLocationStart.size(); ++i)
209         _requestsWaitingForCoreLocationStart[i].permissionRequest->deny();
210     _requestsWaitingForCoreLocationStart.clear();
211     [self _stopUpdatingIfPossible];
212 }
213
214 - (void)positionChanged:(WebCore::GeolocationPosition*)position
215 {
216     _lastActivePosition = WebGeolocationPosition::create(position->timestamp(), position->latitude(), position->longitude(), position->accuracy(), position->canProvideAltitude(), position->altitude(), position->canProvideAltitudeAccuracy(), position->altitudeAccuracy(), position->canProvideHeading(), position->heading(), position->canProvideSpeed(), position->speed());
217     _geolocationManager->providerDidChangePosition(_lastActivePosition.get());
218 }
219
220 - (void)errorOccurred:(NSString *)errorMessage
221 {
222     _geolocationManager->providerDidFailToDeterminePosition(errorMessage);
223 }
224
225 - (void)resetGeolocation
226 {
227     _geolocationManager->resetPermissions();
228 }
229
230 #pragma mark - Methods for udating the state WKWebAllowDenyPolicyListener
231 - (void)permissionDenied:(GeolocationPermissionRequestProxy*)permissionRequestProxy
232 {
233     _requestsInWarmUp.remove(permissionRequestProxy);
234     [self _stopUpdatingIfPossible];
235 }
236
237 @end
238
239 # pragma mark - Implementation of WKWebAllowDenyPolicyListener
240 @implementation WKWebAllowDenyPolicyListener {
241     RetainPtr<WKGeolocationProviderIOS> _provider;
242     RefPtr<GeolocationPermissionRequestProxy> _permissionRequestProxy;
243 }
244
245 - (id)initWithProvider:(WKGeolocationProviderIOS*)provider permissionRequestProxy:(PassRefPtr<GeolocationPermissionRequestProxy>)permissionRequestProxy
246 {
247     self = [super init];
248     if (!self)
249         return nil;
250
251     _provider = provider;
252     _permissionRequestProxy = permissionRequestProxy;
253     return self;
254 }
255
256 - (void)allow
257 {
258     _permissionRequestProxy->allow();
259 }
260
261 - (void)deny
262 {
263     [_provider.get() permissionDenied:_permissionRequestProxy.get()];
264     _permissionRequestProxy->deny();
265 }
266
267 - (void)denyOnlyThisRequest
268 {
269     // The method denyOnlyThisRequest is iAd specific for WebKit1.
270     ASSERT_NOT_REACHED();
271 }
272
273 - (BOOL)shouldClearCache
274 {
275     return NO;
276 }
277 @end