/* * Copyright (C) 2012-2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #import "config.h" #import "WKGeolocationProviderIOS.h" #if PLATFORM(IOS) #import "APIFrameInfo.h" #import "APISecurityOrigin.h" #import "CompletionHandlerCallChecker.h" #import "WKFrameInfoInternal.h" #import "WKGeolocationManager.h" #import "WKProcessPoolInternal.h" #import "WKUIDelegatePrivate.h" #import "WKWebView.h" #import "WebGeolocationManagerProxy.h" #import "WebProcessPool.h" #import "_WKGeolocationCoreLocationProvider.h" #import "_WKGeolocationPositionInternal.h" #import #import #import #import #import #import #import #import // FIXME: Remove use of WebKit1 from WebKit2 #import #import using namespace WebCore; using namespace WebKit; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" @interface WKGeolocationProviderIOS (_WKGeolocationCoreLocationListener) <_WKGeolocationCoreLocationListener> @end @interface WKLegacyCoreLocationProvider : NSObject<_WKGeolocationCoreLocationProvider, WebGeolocationCoreLocationUpdateListener> @end @interface WKWebAllowDenyPolicyListener : NSObject - (id)initWithCompletionHandler:(Function&&)completionHandler; - (void)denyOnlyThisRequest NO_RETURN_DUE_TO_ASSERT; @end namespace WebKit { void decidePolicyForGeolocationRequestFromOrigin(SecurityOrigin*, const String& urlString, id, UIWindow*); }; struct GeolocationRequestData { RefPtr origin; RefPtr frame; Function completionHandler; RetainPtr view; }; @implementation WKGeolocationProviderIOS { RefPtr _geolocationManager; RetainPtr> _coreLocationProvider; BOOL _isWebCoreGeolocationActive; RefPtr _lastActivePosition; Vector _requestsWaitingForCoreLocationAuthorization; } #pragma mark - WKGeolocationProvider callbacks implementation. static void startUpdatingCallback(WKGeolocationManagerRef geolocationManager, const void* clientInfo) { WKGeolocationProviderIOS *geolocationProvider = reinterpret_cast(const_cast(clientInfo)); ASSERT([geolocationProvider isKindOfClass:[WKGeolocationProviderIOS class]]); [geolocationProvider _startUpdating]; } static void stopUpdatingCallback(WKGeolocationManagerRef geolocationManager, const void* clientInfo) { WKGeolocationProviderIOS *geolocationProvider = reinterpret_cast(const_cast(clientInfo)); ASSERT([geolocationProvider isKindOfClass:[WKGeolocationProviderIOS class]]); [geolocationProvider _stopUpdating]; } static void setEnableHighAccuracy(WKGeolocationManagerRef geolocationManager, bool enable, const void* clientInfo) { WKGeolocationProviderIOS *geolocationProvider = reinterpret_cast(const_cast(clientInfo)); ASSERT([geolocationProvider isKindOfClass:[WKGeolocationProviderIOS class]]); [geolocationProvider _setEnableHighAccuracy:enable]; } -(void)_startUpdating { _isWebCoreGeolocationActive = YES; [_coreLocationProvider start]; // If we have the last position, it is from the initialization or warm up. It is the last known // good position so we can return it directly. if (_lastActivePosition) _geolocationManager->providerDidChangePosition(_lastActivePosition.get()); } -(void)_stopUpdating { _isWebCoreGeolocationActive = NO; [_coreLocationProvider stop]; _lastActivePosition = nullptr; } -(void)_setEnableHighAccuracy:(BOOL)enableHighAccuracy { [_coreLocationProvider setEnableHighAccuracy:enableHighAccuracy]; } #pragma mark - Public API implementation. -(id)init { ASSERT_NOT_REACHED(); [self release]; return nil; } -(id)initWithProcessPool:(WebProcessPool&)processPool { self = [super init]; if (!self) return nil; _geolocationManager = processPool.supplement(); WKGeolocationProviderV1 providerCallback = { { 1, self }, startUpdatingCallback, stopUpdatingCallback, setEnableHighAccuracy }; WKGeolocationManagerSetProvider(toAPI(_geolocationManager.get()), &providerCallback.base); _coreLocationProvider = wrapper(processPool)._coreLocationProvider ?: adoptNS(static_cast>([[WKLegacyCoreLocationProvider alloc] init])); [_coreLocationProvider setListener:self]; return self; } -(void)decidePolicyForGeolocationRequestFromOrigin:(SecurityOrigin&)origin frame:(WebFrameProxy&)frame completionHandler:(Function&&)completionHandler view:(WKWebView*)contentView { // Step 1: ask the user if the app can use Geolocation. GeolocationRequestData geolocationRequestData; geolocationRequestData.origin = &origin; geolocationRequestData.frame = &frame; geolocationRequestData.completionHandler = WTFMove(completionHandler); geolocationRequestData.view = contentView; _requestsWaitingForCoreLocationAuthorization.append(WTFMove(geolocationRequestData)); [_coreLocationProvider requestGeolocationAuthorization]; } @end #pragma mark - WebGeolocationCoreLocationUpdateListener implementation. @implementation WKGeolocationProviderIOS (WebGeolocationCoreLocationUpdateListener) - (void)geolocationAuthorizationGranted { // Step 2: ask the user if the this particular page can use gelocation. Vector requests = WTFMove(_requestsWaitingForCoreLocationAuthorization); for (auto& request : requests) { bool requiresUserAuthorization = true; id uiDelegate = static_cast>([request.view UIDelegate]); if ([uiDelegate respondsToSelector:@selector(_webView:requestGeolocationAuthorizationForURL:frame:decisionHandler:)]) { URL requestFrameURL(URL(), request.frame->url()); RetainPtr frameInfo = wrapper(API::FrameInfo::create(*request.frame.get(), *request.origin.get())); RefPtr checker = CompletionHandlerCallChecker::create(uiDelegate, @selector(_webView:requestGeolocationAuthorizationForURL:frame:decisionHandler:)); WKWebView *viewFromRequest = request.view.get(); [uiDelegate _webView:viewFromRequest requestGeolocationAuthorizationForURL:requestFrameURL frame:frameInfo.get() decisionHandler:BlockPtr::fromCallable([request = WTFMove(request), checker = WTFMove(checker)](BOOL authorized) { if (checker->completionHandlerHasBeenCalled()) return; checker->didCallCompletionHandler(); request.completionHandler(authorized); }).get()]; return; } if ([uiDelegate respondsToSelector:@selector(_webView:shouldRequestGeolocationAuthorizationForURL:isMainFrame:mainFrameURL:)]) { const WebFrameProxy* mainFrame = request.frame->page()->mainFrame(); bool isMainFrame = request.frame == mainFrame; URL requestFrameURL(URL(), request.frame->url()); URL mainFrameURL(URL(), mainFrame->url()); requiresUserAuthorization = [uiDelegate _webView:request.view.get() shouldRequestGeolocationAuthorizationForURL:requestFrameURL isMainFrame:isMainFrame mainFrameURL:mainFrameURL]; } if (requiresUserAuthorization) { RetainPtr policyListener = adoptNS([[WKWebAllowDenyPolicyListener alloc] initWithCompletionHandler:WTFMove(request.completionHandler)]); decidePolicyForGeolocationRequestFromOrigin(request.origin.get(), request.frame->url(), policyListener.get(), [request.view window]); } else request.completionHandler(true); } } - (void)geolocationAuthorizationDenied { Vector requests = WTFMove(_requestsWaitingForCoreLocationAuthorization); for (const auto& requestData : requests) requestData.completionHandler(false); } - (void)positionChanged:(_WKGeolocationPosition *)position { _lastActivePosition = position->_geolocationPosition.get(); _geolocationManager->providerDidChangePosition(_lastActivePosition.get()); } - (void)errorOccurred:(NSString *)errorMessage { _geolocationManager->providerDidFailToDeterminePosition(errorMessage); } - (void)resetGeolocation { _geolocationManager->resetPermissions(); } @end # pragma mark - Implementation of WKLegacyCoreLocationProvider @implementation WKLegacyCoreLocationProvider { id <_WKGeolocationCoreLocationListener> _listener; RetainPtr _provider; } // <_WKGeolocationCoreLocationProvider> Methods - (void)setListener:(id<_WKGeolocationCoreLocationListener>)listener { ASSERT(listener && !_listener && !_provider); _listener = listener; _provider = adoptNS([[WebGeolocationCoreLocationProvider alloc] initWithListener:self]); } - (void)requestGeolocationAuthorization { ASSERT(_provider); [_provider requestGeolocationAuthorization]; } - (void)start { ASSERT(_provider); [_provider start]; } - (void)stop { ASSERT(_provider); [_provider stop]; } - (void)setEnableHighAccuracy:(BOOL)flag { ASSERT(_provider); [_provider setEnableHighAccuracy:flag]; } // Methods - (void)geolocationAuthorizationGranted { ASSERT(_listener); [_listener geolocationAuthorizationGranted]; } - (void)geolocationAuthorizationDenied { ASSERT(_listener); [_listener geolocationAuthorizationDenied]; } - (void)positionChanged:(WebCore::GeolocationPosition&&)corePosition { ASSERT(_listener); auto position = WebGeolocationPosition::create(WTFMove(corePosition)); [_listener positionChanged:wrapper(position.get())]; } - (void)errorOccurred:(NSString *)errorMessage { ASSERT(_listener); [_listener errorOccurred:errorMessage]; } - (void)resetGeolocation { ASSERT(_listener); [_listener resetGeolocation]; } @end # pragma mark - Implementation of WKWebAllowDenyPolicyListener @implementation WKWebAllowDenyPolicyListener { Function _completionHandler; } - (id)initWithCompletionHandler:(Function&&)completionHandler { self = [super init]; if (!self) return nil; _completionHandler = WTFMove(completionHandler); return self; } - (void)allow { _completionHandler(true); } - (void)deny { _completionHandler(false); } - (void)denyOnlyThisRequest { // The method denyOnlyThisRequest is iAd specific for WebKit1. ASSERT_NOT_REACHED(); } - (BOOL)shouldClearCache { return NO; } @end #pragma clang diagnostic pop #endif // PLATFORM(IOS)