1 | /* |
2 | * Copyright (C) 2013 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 | #include "config.h" |
27 | |
28 | #if WK_HAVE_C_SPI |
29 | |
30 | #include "PlatformUtilities.h" |
31 | #include "PlatformWebView.h" |
32 | #include "Test.h" |
33 | #include <WebKit/WKContextPrivate.h> |
34 | #include <WebKit/WKRetainPtr.h> |
35 | #include <string.h> |
36 | #include <vector> |
37 | |
38 | using namespace std; |
39 | |
40 | namespace TestWebKitAPI { |
41 | |
42 | enum class GeolocationEvent { |
43 | StartUpdating, |
44 | StopUpdating, |
45 | EnableHighAccuracy, |
46 | DisableHighAccuracy |
47 | }; |
48 | |
49 | ostream& operator<<(ostream& outputStream, const GeolocationEvent& geolocationEvent) |
50 | { |
51 | switch (geolocationEvent) { |
52 | case GeolocationEvent::StartUpdating: |
53 | outputStream << "GeolocationEvent::StartUpdating" ; |
54 | break; |
55 | case GeolocationEvent::StopUpdating: |
56 | outputStream << "GeolocationEvent::StopUpdating" ; |
57 | break; |
58 | case GeolocationEvent::EnableHighAccuracy: |
59 | outputStream << "GeolocationEvent::EnableHighAccuracy" ; |
60 | break; |
61 | case GeolocationEvent::DisableHighAccuracy: |
62 | outputStream << "GeolocationEvent::DisableHighAccuracy" ; |
63 | break; |
64 | } |
65 | return outputStream; |
66 | } |
67 | |
68 | struct GeolocationStateTracker { |
69 | vector<GeolocationEvent> events; |
70 | |
71 | virtual ~GeolocationStateTracker() { } |
72 | virtual void eventsChanged() { } |
73 | |
74 | static void startUpdatingCallback(WKGeolocationManagerRef manager, const void* clientInfo) |
75 | { |
76 | GeolocationStateTracker* stateTracker = static_cast<GeolocationStateTracker*>(const_cast<void*>(clientInfo)); |
77 | stateTracker->events.push_back(GeolocationEvent::StartUpdating); |
78 | stateTracker->eventsChanged(); |
79 | |
80 | WKRetainPtr<WKGeolocationPositionRef> position = adoptWK(WKGeolocationPositionCreate(0, 50.644358, 3.345453, 2.53)); |
81 | WKGeolocationManagerProviderDidChangePosition(manager, position.get()); |
82 | } |
83 | |
84 | static void stopUpdatingCallback(WKGeolocationManagerRef, const void* clientInfo) |
85 | { |
86 | GeolocationStateTracker* stateTracker = static_cast<GeolocationStateTracker*>(const_cast<void*>(clientInfo)); |
87 | stateTracker->events.push_back(GeolocationEvent::StopUpdating); |
88 | stateTracker->eventsChanged(); |
89 | } |
90 | |
91 | static void setEnableHighAccuracyCallback(WKGeolocationManagerRef, bool enable, const void* clientInfo) |
92 | { |
93 | GeolocationStateTracker* stateTracker = static_cast<GeolocationStateTracker*>(const_cast<void*>(clientInfo)); |
94 | if (enable) |
95 | stateTracker->events.push_back(GeolocationEvent::EnableHighAccuracy); |
96 | else |
97 | stateTracker->events.push_back(GeolocationEvent::DisableHighAccuracy); |
98 | stateTracker->eventsChanged(); |
99 | } |
100 | }; |
101 | |
102 | void decidePolicyForGeolocationPermissionRequestCallBack(WKPageRef page, WKFrameRef frame, WKSecurityOriginRef origin, WKGeolocationPermissionRequestRef permissionRequest, const void* clientInfo) |
103 | { |
104 | WKGeolocationPermissionRequestAllow(permissionRequest); |
105 | } |
106 | |
107 | void setupGeolocationProvider(WKContextRef context, void* clientInfo) |
108 | { |
109 | WKGeolocationProviderV1 providerCallback; |
110 | memset(&providerCallback, 0, sizeof(WKGeolocationProviderV1)); |
111 | |
112 | providerCallback.base.version = 1; |
113 | providerCallback.base.clientInfo = clientInfo; |
114 | providerCallback.startUpdating = GeolocationStateTracker::startUpdatingCallback; |
115 | providerCallback.stopUpdating = GeolocationStateTracker::stopUpdatingCallback; |
116 | providerCallback.setEnableHighAccuracy = GeolocationStateTracker::setEnableHighAccuracyCallback; |
117 | |
118 | WKGeolocationManagerSetProvider(WKContextGetGeolocationManager(context), &providerCallback.base); |
119 | } |
120 | |
121 | void clearGeolocationProvider(WKContextRef context) |
122 | { |
123 | WKGeolocationManagerSetProvider(WKContextGetGeolocationManager(context), nullptr); |
124 | } |
125 | |
126 | void setupView(PlatformWebView& webView) |
127 | { |
128 | WKPageUIClientV2 uiClient; |
129 | memset(&uiClient, 0, sizeof(uiClient)); |
130 | |
131 | uiClient.base.version = 2; |
132 | uiClient.decidePolicyForGeolocationPermissionRequest = decidePolicyForGeolocationPermissionRequestCallBack; |
133 | |
134 | WKPageSetPageUIClient(webView.page(), &uiClient.base); |
135 | } |
136 | |
137 | // GeolocationBasic. |
138 | struct GeolocationBasicStateTracker : GeolocationStateTracker { |
139 | bool finished; |
140 | |
141 | GeolocationBasicStateTracker() : finished(false) { } |
142 | virtual void eventsChanged() |
143 | { |
144 | switch (events.size()) { |
145 | case 1: |
146 | EXPECT_EQ(GeolocationEvent::DisableHighAccuracy, events[0]); |
147 | break; |
148 | case 2: |
149 | EXPECT_EQ(GeolocationEvent::StartUpdating, events[1]); |
150 | break; |
151 | case 3: |
152 | EXPECT_EQ(GeolocationEvent::StopUpdating, events[2]); |
153 | finished = true; |
154 | break; |
155 | default: |
156 | EXPECT_TRUE(false); |
157 | finished = true; |
158 | } |
159 | } |
160 | }; |
161 | |
162 | TEST(WebKit, GeolocationBasic) |
163 | { |
164 | WKRetainPtr<WKContextRef> context = adoptWK(WKContextCreateWithConfiguration(nullptr)); |
165 | |
166 | GeolocationBasicStateTracker stateTracker; |
167 | setupGeolocationProvider(context.get(), &stateTracker); |
168 | |
169 | PlatformWebView webView(context.get()); |
170 | setupView(webView); |
171 | |
172 | WKRetainPtr<WKURLRef> url = adoptWK(Util::createURLForResource("geolocationGetCurrentPosition" , "html" )); |
173 | WKPageLoadURL(webView.page(), url.get()); |
174 | |
175 | Util::run(&stateTracker.finished); |
176 | clearGeolocationProvider(context.get()); |
177 | } |
178 | |
179 | // Geolocation requested with High Accuracy. |
180 | struct GeolocationBasicWithHighAccuracyStateTracker : GeolocationStateTracker { |
181 | bool finished; |
182 | |
183 | GeolocationBasicWithHighAccuracyStateTracker() : finished(false) { } |
184 | virtual void eventsChanged() |
185 | { |
186 | switch (events.size()) { |
187 | case 1: |
188 | EXPECT_EQ(GeolocationEvent::EnableHighAccuracy, events[0]); |
189 | break; |
190 | case 2: |
191 | EXPECT_EQ(GeolocationEvent::StartUpdating, events[1]); |
192 | break; |
193 | case 3: |
194 | EXPECT_EQ(GeolocationEvent::StopUpdating, events[2]); |
195 | finished = true; |
196 | break; |
197 | default: |
198 | EXPECT_TRUE(false); |
199 | finished = true; |
200 | } |
201 | } |
202 | }; |
203 | |
204 | TEST(WebKit, GeolocationBasicWithHighAccuracy) |
205 | { |
206 | WKRetainPtr<WKContextRef> context = adoptWK(WKContextCreateWithConfiguration(nullptr)); |
207 | |
208 | GeolocationBasicWithHighAccuracyStateTracker stateTracker; |
209 | setupGeolocationProvider(context.get(), &stateTracker); |
210 | |
211 | PlatformWebView webView(context.get()); |
212 | setupView(webView); |
213 | |
214 | WKRetainPtr<WKURLRef> url = adoptWK(Util::createURLForResource("geolocationGetCurrentPositionWithHighAccuracy" , "html" )); |
215 | WKPageLoadURL(webView.page(), url.get()); |
216 | |
217 | Util::run(&stateTracker.finished); |
218 | clearGeolocationProvider(context.get()); |
219 | } |
220 | |
221 | // Geolocation start without High Accuracy, then requires High Accuracy. |
222 | struct GeolocationTransitionToHighAccuracyStateTracker : GeolocationStateTracker { |
223 | bool finishedFirstStep { false }; |
224 | bool enabledHighAccuracy { false }; |
225 | bool finished { false }; |
226 | |
227 | virtual void eventsChanged() |
228 | { |
229 | switch (events.size()) { |
230 | case 1: |
231 | EXPECT_EQ(GeolocationEvent::DisableHighAccuracy, events[0]); |
232 | break; |
233 | case 2: |
234 | EXPECT_EQ(GeolocationEvent::StartUpdating, events[1]); |
235 | finishedFirstStep = true; |
236 | break; |
237 | case 3: |
238 | EXPECT_EQ(GeolocationEvent::EnableHighAccuracy, events[2]); |
239 | enabledHighAccuracy = true; |
240 | break; |
241 | case 4: |
242 | EXPECT_EQ(GeolocationEvent::DisableHighAccuracy, events[3]); |
243 | break; |
244 | case 5: |
245 | EXPECT_EQ(GeolocationEvent::StopUpdating, events[4]); |
246 | finished = true; |
247 | break; |
248 | default: |
249 | EXPECT_TRUE(false); |
250 | finishedFirstStep = true; |
251 | enabledHighAccuracy = true; |
252 | finished = true; |
253 | } |
254 | } |
255 | }; |
256 | |
257 | TEST(WebKit, GeolocationTransitionToHighAccuracy) |
258 | { |
259 | WKRetainPtr<WKContextRef> context = adoptWK(WKContextCreateWithConfiguration(nullptr)); |
260 | |
261 | GeolocationTransitionToHighAccuracyStateTracker stateTracker; |
262 | setupGeolocationProvider(context.get(), &stateTracker); |
263 | |
264 | PlatformWebView lowAccuracyWebView(context.get()); |
265 | setupView(lowAccuracyWebView); |
266 | WKRetainPtr<WKURLRef> lowAccuracyURL = adoptWK(Util::createURLForResource("geolocationWatchPosition" , "html" )); |
267 | WKPageLoadURL(lowAccuracyWebView.page(), lowAccuracyURL.get()); |
268 | Util::run(&stateTracker.finishedFirstStep); |
269 | |
270 | PlatformWebView highAccuracyWebView(lowAccuracyWebView.page()); |
271 | setupView(highAccuracyWebView); |
272 | WKRetainPtr<WKURLRef> highAccuracyURL = adoptWK(Util::createURLForResource("geolocationWatchPositionWithHighAccuracy" , "html" )); |
273 | WKPageLoadURL(highAccuracyWebView.page(), highAccuracyURL.get()); |
274 | Util::run(&stateTracker.enabledHighAccuracy); |
275 | |
276 | WKRetainPtr<WKURLRef> resetUrl = adoptWK(WKURLCreateWithUTF8CString("about:blank" )); |
277 | WKPageLoadURL(highAccuracyWebView.page(), resetUrl.get()); |
278 | Util::run(&stateTracker.enabledHighAccuracy); |
279 | WKPageLoadURL(lowAccuracyWebView.page(), resetUrl.get()); |
280 | Util::run(&stateTracker.finished); |
281 | |
282 | clearGeolocationProvider(context.get()); |
283 | } |
284 | |
285 | // Geolocation start with High Accuracy, then should fall back to low accuracy. |
286 | struct GeolocationTransitionToLowAccuracyStateTracker : GeolocationStateTracker { |
287 | bool finishedFirstStep { false }; |
288 | bool disabledHighAccuracy { false }; |
289 | bool finished { false }; |
290 | |
291 | virtual void eventsChanged() |
292 | { |
293 | switch (events.size()) { |
294 | case 1: |
295 | EXPECT_EQ(GeolocationEvent::EnableHighAccuracy, events[0]); |
296 | break; |
297 | case 2: |
298 | EXPECT_EQ(GeolocationEvent::StartUpdating, events[1]); |
299 | finishedFirstStep = true; |
300 | break; |
301 | case 3: |
302 | EXPECT_EQ(GeolocationEvent::DisableHighAccuracy, events[2]); |
303 | disabledHighAccuracy = true; |
304 | break; |
305 | case 4: |
306 | EXPECT_EQ(GeolocationEvent::StopUpdating, events[3]); |
307 | finished = true; |
308 | break; |
309 | default: |
310 | EXPECT_TRUE(false); |
311 | finishedFirstStep = true; |
312 | disabledHighAccuracy = true; |
313 | finished = true; |
314 | } |
315 | } |
316 | }; |
317 | |
318 | struct JavaScriptAlertContext { |
319 | bool didRun { false }; |
320 | std::string alertText; |
321 | }; |
322 | |
323 | static void runJavaScriptAlert(WKPageRef page, WKStringRef alertText, WKFrameRef frame, const void* clientInfo) |
324 | { |
325 | auto* context = static_cast<JavaScriptAlertContext*>(const_cast<void*>(clientInfo)); |
326 | context->didRun = true; |
327 | context->alertText = Util::toSTD(alertText); |
328 | } |
329 | |
330 | TEST(WebKit, GeolocationTransitionToLowAccuracy) |
331 | { |
332 | WKRetainPtr<WKContextRef> context = adoptWK(WKContextCreateWithConfiguration(nullptr)); |
333 | |
334 | GeolocationTransitionToLowAccuracyStateTracker stateTracker; |
335 | setupGeolocationProvider(context.get(), &stateTracker); |
336 | |
337 | PlatformWebView highAccuracyWebView(context.get()); |
338 | setupView(highAccuracyWebView); |
339 | WKRetainPtr<WKURLRef> highAccuracyURL = adoptWK(Util::createURLForResource("geolocationWatchPositionWithHighAccuracy" , "html" )); |
340 | WKPageLoadURL(highAccuracyWebView.page(), highAccuracyURL.get()); |
341 | Util::run(&stateTracker.finishedFirstStep); |
342 | |
343 | PlatformWebView lowAccuracyWebView(context.get()); |
344 | setupView(lowAccuracyWebView); |
345 | |
346 | JavaScriptAlertContext secondStepContext; |
347 | |
348 | WKPageUIClientV2 uiClient; |
349 | memset(&uiClient, 0, sizeof(uiClient)); |
350 | uiClient.base.version = 2; |
351 | uiClient.base.clientInfo = &secondStepContext; |
352 | uiClient.decidePolicyForGeolocationPermissionRequest = decidePolicyForGeolocationPermissionRequestCallBack; |
353 | uiClient.runJavaScriptAlert = runJavaScriptAlert; |
354 | WKPageSetPageUIClient(lowAccuracyWebView.page(), &uiClient.base); |
355 | |
356 | WKRetainPtr<WKURLRef> lowAccuracyURL = adoptWK(Util::createURLForResource("geolocationWatchPosition" , "html" )); |
357 | WKPageLoadURL(lowAccuracyWebView.page(), lowAccuracyURL.get()); |
358 | Util::run(&secondStepContext.didRun); |
359 | EXPECT_EQ(secondStepContext.alertText, "SUCCESS" ); |
360 | |
361 | WKRetainPtr<WKURLRef> resetUrl = adoptWK(WKURLCreateWithUTF8CString("about:blank" )); |
362 | WKPageLoadURL(highAccuracyWebView.page(), resetUrl.get()); |
363 | |
364 | Util::run(&stateTracker.disabledHighAccuracy); |
365 | |
366 | WKPageLoadURL(lowAccuracyWebView.page(), resetUrl.get()); |
367 | Util::run(&stateTracker.finished); |
368 | |
369 | clearGeolocationProvider(context.get()); |
370 | } |
371 | |
372 | TEST(WebKit, GeolocationWatchMultiprocess) |
373 | { |
374 | WKRetainPtr<WKContextRef> context = adoptWK(WKContextCreateWithConfiguration(nullptr)); |
375 | |
376 | GeolocationStateTracker stateTracker; |
377 | setupGeolocationProvider(context.get(), &stateTracker); |
378 | |
379 | JavaScriptAlertContext testContext; |
380 | |
381 | WKPageUIClientV2 uiClient; |
382 | memset(&uiClient, 0, sizeof(uiClient)); |
383 | uiClient.base.version = 2; |
384 | uiClient.base.clientInfo = &testContext; |
385 | uiClient.decidePolicyForGeolocationPermissionRequest = decidePolicyForGeolocationPermissionRequestCallBack; |
386 | uiClient.runJavaScriptAlert = runJavaScriptAlert; |
387 | |
388 | PlatformWebView view1(context.get()); |
389 | WKPageSetPageUIClient(view1.page(), &uiClient.base); |
390 | WKRetainPtr<WKURLRef> url = adoptWK(Util::createURLForResource("geolocationWatchPosition" , "html" )); |
391 | WKPageLoadURL(view1.page(), url.get()); |
392 | Util::run(&testContext.didRun); |
393 | EXPECT_EQ(testContext.alertText, "SUCCESS" ); |
394 | WKPageSetPageUIClient(view1.page(), nullptr); |
395 | |
396 | testContext.didRun = false; |
397 | testContext.alertText = { }; |
398 | |
399 | PlatformWebView view2(context.get()); |
400 | WKPageSetPageUIClient(view2.page(), &uiClient.base); |
401 | WKPageLoadURL(view2.page(), url.get()); |
402 | Util::run(&testContext.didRun); |
403 | EXPECT_EQ(testContext.alertText, "SUCCESS" ); |
404 | |
405 | clearGeolocationProvider(context.get()); |
406 | } |
407 | |
408 | } // namespace TestWebKitAPI |
409 | |
410 | #endif |
411 | |