1/* 2 * Copyright (C) 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. 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#if PLATFORM(IOS) 30 31#import "GeolocationPermissionRequestProxy.h" 32#import "WebContext.h" 33#import "WebGeolocationManagerProxy.h" 34#import "WebSecurityOrigin.h" 35#import <WebGeolocationPosition.h> 36#import <WebCore/GeolocationPosition.h> 37#import <WebKit/WKGeolocationPermissionRequest.h> 38#import <wtf/Assertions.h> 39#import <wtf/PassRefPtr.h> 40#import <wtf/RefPtr.h> 41#import <wtf/RetainPtr.h> 42#import <wtf/HashSet.h> 43 44// FIXME: Remove use of WebKit1 from WebKit2 45#import <WebKit/WebGeolocationCoreLocationProvider.h> 46#import <WebKit/WebAllowDenyPolicyListener.h> 47 48using namespace WebKit; 49 50#pragma clang diagnostic push 51#pragma clang diagnostic ignored "-Wdeprecated-declarations" 52 53@interface WKGeolocationProviderIOS (WebGeolocationCoreLocationUpdateListener) <WebGeolocationCoreLocationUpdateListener> 54@end 55 56@interface WKWebAllowDenyPolicyListener : NSObject<WebAllowDenyPolicyListener> 57- (id)initWithPermissionRequestProxy:(PassRefPtr<GeolocationPermissionRequestProxy>)permissionRequestProxy; 58- (void)denyOnlyThisRequest NO_RETURN_DUE_TO_ASSERT; 59@end 60 61namespace WebKit { 62void decidePolicyForGeolocationRequestFromOrigin(WebCore::SecurityOrigin*, const String& urlString, id<WebAllowDenyPolicyListener>, UIWindow*); 63}; 64 65struct GeolocationRequestData { 66 RefPtr<WebCore::SecurityOrigin> origin; 67 RefPtr<WebFrameProxy> frame; 68 RefPtr<GeolocationPermissionRequestProxy> permissionRequest; 69 RetainPtr<UIWindow> window; 70}; 71 72@implementation WKGeolocationProviderIOS { 73 RefPtr<WebGeolocationManagerProxy> _geolocationManager; 74 RetainPtr<WebGeolocationCoreLocationProvider> _coreLocationProvider; 75 BOOL _isWebCoreGeolocationActive; 76 RefPtr<WebGeolocationPosition> _lastActivePosition; 77 Vector<GeolocationRequestData> _requestsWaitingForCoreLocationAuthorization; 78} 79 80#pragma mark - WKGeolocationProvider callbacks implementation. 81 82static void startUpdatingCallback(WKGeolocationManagerRef geolocationManager, const void* clientInfo) 83{ 84 WKGeolocationProviderIOS *geolocationProvider = reinterpret_cast<WKGeolocationProviderIOS*>(const_cast<void*>(clientInfo)); 85 ASSERT([geolocationProvider isKindOfClass:[WKGeolocationProviderIOS class]]); 86 [geolocationProvider _startUpdating]; 87} 88 89static void stopUpdatingCallback(WKGeolocationManagerRef geolocationManager, const void* clientInfo) 90{ 91 WKGeolocationProviderIOS *geolocationProvider = reinterpret_cast<WKGeolocationProviderIOS*>(const_cast<void*>(clientInfo)); 92 ASSERT([geolocationProvider isKindOfClass:[WKGeolocationProviderIOS class]]); 93 [geolocationProvider _stopUpdating]; 94} 95 96static void setEnableHighAccuracy(WKGeolocationManagerRef geolocationManager, bool enable, const void* clientInfo) 97{ 98 WKGeolocationProviderIOS *geolocationProvider = reinterpret_cast<WKGeolocationProviderIOS*>(const_cast<void*>(clientInfo)); 99 ASSERT([geolocationProvider isKindOfClass:[WKGeolocationProviderIOS class]]); 100 [geolocationProvider _setEnableHighAccuracy:enable]; 101} 102 103-(void)_startUpdating 104{ 105 _isWebCoreGeolocationActive = YES; 106 [_coreLocationProvider start]; 107 108 // If we have the last position, it is from the initialization or warm up. It is the last known 109 // good position so we can return it directly. 110 if (_lastActivePosition) 111 _geolocationManager->providerDidChangePosition(_lastActivePosition.get()); 112} 113 114-(void)_stopUpdating 115{ 116 _isWebCoreGeolocationActive = NO; 117 [_coreLocationProvider stop]; 118 _lastActivePosition.clear(); 119} 120 121-(void)_setEnableHighAccuracy:(BOOL)enableHighAccuracy 122{ 123 [_coreLocationProvider setEnableHighAccuracy:enableHighAccuracy]; 124} 125 126#pragma mark - Public API implementation. 127 128-(id)init 129{ 130 ASSERT_NOT_REACHED(); 131 [self release]; 132 return nil; 133} 134 135-(id)initWithContext:(WebContext*)context 136{ 137 self = [super init]; 138 if (!self) 139 return nil; 140 _geolocationManager = context->supplement<WebGeolocationManagerProxy>(); 141 WKGeolocationProvider providerCallback = { 142 kWKGeolocationProviderCurrentVersion, 143 self, 144 startUpdatingCallback, 145 stopUpdatingCallback, 146 setEnableHighAccuracy 147 }; 148 _geolocationManager->initializeProvider(reinterpret_cast<WKGeolocationProviderBase*>(&providerCallback)); 149 _coreLocationProvider = adoptNS([[WebGeolocationCoreLocationProvider alloc] initWithListener:self]); 150 return self; 151} 152 153-(void)decidePolicyForGeolocationRequestFromOrigin:(WKSecurityOriginRef)origin frame:(WKFrameRef)frame request:(WKGeolocationPermissionRequestRef)permissionRequest window:(UIWindow*)window 154{ 155 // Step 1: ask the user if the app can use Geolocation. 156 GeolocationRequestData geolocationRequestData; 157 geolocationRequestData.origin = const_cast<WebCore::SecurityOrigin*>(&toImpl(origin)->securityOrigin()); 158 geolocationRequestData.frame = toImpl(frame); 159 geolocationRequestData.permissionRequest = toImpl(permissionRequest); 160 geolocationRequestData.window = window; 161 _requestsWaitingForCoreLocationAuthorization.append(geolocationRequestData); 162 [_coreLocationProvider requestGeolocationAuthorization]; 163} 164@end 165 166#pragma mark - WebGeolocationCoreLocationUpdateListener implementation. 167 168@implementation WKGeolocationProviderIOS (WebGeolocationCoreLocationUpdateListener) 169 170- (void)geolocationAuthorizationGranted 171{ 172 // Step 2: ask the user if the this particular page can use gelocation. 173 Vector<GeolocationRequestData> requests = WTF::move(_requestsWaitingForCoreLocationAuthorization); 174 for (const auto& request : requests) { 175 RetainPtr<WKWebAllowDenyPolicyListener> policyListener = adoptNS([[WKWebAllowDenyPolicyListener alloc] initWithPermissionRequestProxy:request.permissionRequest.get()]); 176 decidePolicyForGeolocationRequestFromOrigin(request.origin.get(), request.frame->url(), policyListener.get(), request.window.get()); 177 } 178} 179 180- (void)geolocationAuthorizationDenied 181{ 182 Vector<GeolocationRequestData> requests = WTF::move(_requestsWaitingForCoreLocationAuthorization); 183 for (const auto& requestData : requests) 184 requestData.permissionRequest->deny(); 185} 186 187- (void)positionChanged:(WebCore::GeolocationPosition*)position 188{ 189 _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()); 190 _geolocationManager->providerDidChangePosition(_lastActivePosition.get()); 191} 192 193- (void)errorOccurred:(NSString *)errorMessage 194{ 195 _geolocationManager->providerDidFailToDeterminePosition(errorMessage); 196} 197 198- (void)resetGeolocation 199{ 200 _geolocationManager->resetPermissions(); 201} 202 203@end 204 205# pragma mark - Implementation of WKWebAllowDenyPolicyListener 206@implementation WKWebAllowDenyPolicyListener { 207 RefPtr<GeolocationPermissionRequestProxy> _permissionRequestProxy; 208} 209 210- (id)initWithPermissionRequestProxy:(PassRefPtr<GeolocationPermissionRequestProxy>)permissionRequestProxy 211{ 212 self = [super init]; 213 if (!self) 214 return nil; 215 216 _permissionRequestProxy = permissionRequestProxy; 217 return self; 218} 219 220- (void)allow 221{ 222 _permissionRequestProxy->allow(); 223} 224 225- (void)deny 226{ 227 _permissionRequestProxy->deny(); 228} 229 230- (void)denyOnlyThisRequest 231{ 232 // The method denyOnlyThisRequest is iAd specific for WebKit1. 233 ASSERT_NOT_REACHED(); 234} 235 236- (BOOL)shouldClearCache 237{ 238 return NO; 239} 240@end 241 242#pragma clang diagnostic pop 243 244#endif // PLATFORM(IOS) 245