1/*
2 * Copyright (C) 2009, 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 "WebGeolocationClient.h"
27
28#if ENABLE(GEOLOCATION)
29
30#import "WebDelegateImplementationCaching.h"
31#import "WebFrameInternal.h"
32#import "WebGeolocationPositionInternal.h"
33#import "WebSecurityOriginInternal.h"
34#import "WebUIDelegatePrivate.h"
35#import "WebViewInternal.h"
36#import <WebCore/BlockExceptions.h>
37#import <WebCore/Document.h>
38#import <WebCore/Frame.h>
39#import <WebCore/Geolocation.h>
40
41#if PLATFORM(IOS)
42#import <WebCore/WAKResponder.h>
43#import <WebKitLegacy/WebCoreThreadRun.h>
44#endif
45
46using namespace WebCore;
47
48#if !PLATFORM(IOS)
49@interface WebGeolocationPolicyListener : NSObject <WebAllowDenyPolicyListener>
50{
51    RefPtr<Geolocation> _geolocation;
52}
53- (id)initWithGeolocation:(Geolocation*)geolocation;
54@end
55#else
56@interface WebGeolocationPolicyListener : NSObject <WebAllowDenyPolicyListener>
57{
58    RefPtr<Geolocation> _geolocation;
59    RetainPtr<WebView *> _webView;
60}
61- (id)initWithGeolocation:(Geolocation*)geolocation forWebView:(WebView*)webView;
62@end
63#endif
64
65#if PLATFORM(IOS)
66@interface WebGeolocationProviderInitializationListener : NSObject <WebGeolocationProviderInitializationListener> {
67@private
68    RefPtr<Geolocation> m_geolocation;
69}
70- (id)initWithGeolocation:(Geolocation*)geolocation;
71@end
72#endif
73
74WebGeolocationClient::WebGeolocationClient(WebView *webView)
75    : m_webView(webView)
76{
77}
78
79void WebGeolocationClient::geolocationDestroyed()
80{
81    delete this;
82}
83
84void WebGeolocationClient::startUpdating()
85{
86    [[m_webView _geolocationProvider] registerWebView:m_webView];
87}
88
89void WebGeolocationClient::stopUpdating()
90{
91    [[m_webView _geolocationProvider] unregisterWebView:m_webView];
92}
93
94#if PLATFORM(IOS)
95void WebGeolocationClient::setEnableHighAccuracy(bool wantsHighAccuracy)
96{
97    BEGIN_BLOCK_OBJC_EXCEPTIONS;
98    [[m_webView _geolocationProvider] setEnableHighAccuracy:wantsHighAccuracy];
99    END_BLOCK_OBJC_EXCEPTIONS;
100}
101#endif
102
103void WebGeolocationClient::requestPermission(Geolocation* geolocation)
104{
105    BEGIN_BLOCK_OBJC_EXCEPTIONS;
106
107    SEL selector = @selector(webView:decidePolicyForGeolocationRequestFromOrigin:frame:listener:);
108    if (![[m_webView UIDelegate] respondsToSelector:selector]) {
109        geolocation->setIsAllowed(false);
110        return;
111    }
112
113#if !PLATFORM(IOS)
114    Frame *frame = geolocation->frame();
115
116    if (!frame) {
117        geolocation->setIsAllowed(false);
118        return;
119    }
120
121    WebSecurityOrigin *webOrigin = [[WebSecurityOrigin alloc] _initWithWebCoreSecurityOrigin:frame->document()->securityOrigin()];
122    WebGeolocationPolicyListener* listener = [[WebGeolocationPolicyListener alloc] initWithGeolocation:geolocation];
123
124    CallUIDelegate(m_webView, selector, webOrigin, kit(frame), listener);
125
126    [webOrigin release];
127    [listener release];
128#else
129    RetainPtr<WebGeolocationProviderInitializationListener> listener = adoptNS([[WebGeolocationProviderInitializationListener alloc] initWithGeolocation:geolocation]);
130    [[m_webView _geolocationProvider] initializeGeolocationForWebView:m_webView listener:listener.get()];
131#endif
132    END_BLOCK_OBJC_EXCEPTIONS;
133}
134
135GeolocationPosition* WebGeolocationClient::lastPosition()
136{
137    return core([[m_webView _geolocationProvider] lastPosition]);
138}
139
140#if !PLATFORM(IOS)
141@implementation WebGeolocationPolicyListener
142
143- (id)initWithGeolocation:(Geolocation*)geolocation
144{
145    if (!(self = [super init]))
146        return nil;
147    _geolocation = geolocation;
148    return self;
149}
150
151- (void)allow
152{
153    _geolocation->setIsAllowed(true);
154}
155
156- (void)deny
157{
158    _geolocation->setIsAllowed(false);
159}
160
161@end
162
163#else
164@implementation WebGeolocationPolicyListener
165- (id)initWithGeolocation:(Geolocation*)geolocation forWebView:(WebView*)webView
166{
167    self = [super init];
168    if (!self)
169        return nil;
170    _geolocation = geolocation;
171    _webView = webView;
172    return self;
173}
174
175- (void)allow
176{
177    WebThreadRun(^{
178        _geolocation->setIsAllowed(true);
179    });
180}
181
182- (void)deny
183{
184    WebThreadRun(^{
185        _geolocation->setIsAllowed(false);
186    });
187}
188
189- (void)denyOnlyThisRequest
190{
191    WebThreadRun(^{
192        // A soft deny does not prevent subsequent request from the Geolocation object.
193        [self deny];
194        _geolocation->resetAllGeolocationPermission();
195    });
196}
197
198- (BOOL)shouldClearCache
199{
200    // Theoretically, WebView could changes the WebPreferences after we get the pointer.
201    // We lock to be on the safe side.
202    WebThreadLock();
203
204    return [[_webView.get() preferences] _alwaysRequestGeolocationPermission];
205}
206@end
207
208@implementation WebGeolocationProviderInitializationListener
209- (id)initWithGeolocation:(Geolocation*)geolocation
210{
211    self = [super init];
212    if (self)
213        m_geolocation = geolocation;
214    return self;
215}
216
217- (void)initializationAllowedWebView:(WebView *)webView
218{
219    BEGIN_BLOCK_OBJC_EXCEPTIONS;
220
221    Frame* frame = m_geolocation->frame();
222    if (!frame)
223        return;
224    WebSecurityOrigin *webOrigin = [[WebSecurityOrigin alloc] _initWithWebCoreSecurityOrigin:frame->document()->securityOrigin()];
225    WebGeolocationPolicyListener *listener = [[WebGeolocationPolicyListener alloc] initWithGeolocation:m_geolocation.get() forWebView:webView];
226    SEL selector = @selector(webView:decidePolicyForGeolocationRequestFromOrigin:frame:listener:);
227    CallUIDelegate(webView, selector, webOrigin, kit(frame), listener);
228    [webOrigin release];
229    [listener release];
230
231    END_BLOCK_OBJC_EXCEPTIONS;
232}
233
234- (void)initializationDeniedWebView:(WebView *)webView
235{
236    m_geolocation->setIsAllowed(false);
237}
238@end
239#endif // PLATFORM(IOS)
240
241#endif // ENABLE(GEOLOCATION)
242