1 | /* |
2 | * Copyright (C) 2010-2019 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 | #include "TestController.h" |
28 | |
29 | #include "EventSenderProxy.h" |
30 | #include "Options.h" |
31 | #include "PlatformWebView.h" |
32 | #include "StringFunctions.h" |
33 | #include "TestInvocation.h" |
34 | #include "WebCoreTestSupport.h" |
35 | #include <JavaScriptCore/InitializeThreading.h> |
36 | #include <WebKit/WKArray.h> |
37 | #include <WebKit/WKAuthenticationChallenge.h> |
38 | #include <WebKit/WKAuthenticationDecisionListener.h> |
39 | #include <WebKit/WKContextConfigurationRef.h> |
40 | #include <WebKit/WKContextPrivate.h> |
41 | #include <WebKit/WKCookieManager.h> |
42 | #include <WebKit/WKCredential.h> |
43 | #include <WebKit/WKFrameHandleRef.h> |
44 | #include <WebKit/WKFrameInfoRef.h> |
45 | #include <WebKit/WKIconDatabase.h> |
46 | #include <WebKit/WKMockDisplay.h> |
47 | #include <WebKit/WKMockMediaDevice.h> |
48 | #include <WebKit/WKNavigationActionRef.h> |
49 | #include <WebKit/WKNavigationResponseRef.h> |
50 | #include <WebKit/WKNotification.h> |
51 | #include <WebKit/WKNotificationManager.h> |
52 | #include <WebKit/WKNotificationPermissionRequest.h> |
53 | #include <WebKit/WKNumber.h> |
54 | #include <WebKit/WKOpenPanelResultListener.h> |
55 | #include <WebKit/WKPageGroup.h> |
56 | #include <WebKit/WKPageInjectedBundleClient.h> |
57 | #include <WebKit/WKPagePrivate.h> |
58 | #include <WebKit/WKPluginInformation.h> |
59 | #include <WebKit/WKPreferencesRefPrivate.h> |
60 | #include <WebKit/WKProtectionSpace.h> |
61 | #include <WebKit/WKRetainPtr.h> |
62 | #include <WebKit/WKSecurityOriginRef.h> |
63 | #include <WebKit/WKTextChecker.h> |
64 | #include <WebKit/WKURL.h> |
65 | #include <WebKit/WKUserContentControllerRef.h> |
66 | #include <WebKit/WKUserContentExtensionStoreRef.h> |
67 | #include <WebKit/WKUserMediaPermissionCheck.h> |
68 | #include <WebKit/WKWebsiteDataStoreRef.h> |
69 | #include <algorithm> |
70 | #include <cstdio> |
71 | #include <ctype.h> |
72 | #include <fstream> |
73 | #include <stdlib.h> |
74 | #include <string> |
75 | #include <wtf/AutodrainedPool.h> |
76 | #include <wtf/CryptographicallyRandomNumber.h> |
77 | #include <wtf/MainThread.h> |
78 | #include <wtf/ProcessPrivilege.h> |
79 | #include <wtf/RefCounted.h> |
80 | #include <wtf/RunLoop.h> |
81 | #include <wtf/SetForScope.h> |
82 | #include <wtf/UUID.h> |
83 | #include <wtf/text/CString.h> |
84 | #include <wtf/text/StringConcatenateNumbers.h> |
85 | |
86 | #if PLATFORM(COCOA) |
87 | #include <WebKit/WKContextPrivateMac.h> |
88 | #include <WebKit/WKPagePrivateMac.h> |
89 | #endif |
90 | |
91 | #if PLATFORM(WIN) |
92 | #include <direct.h> |
93 | #define getcwd _getcwd |
94 | #define PATH_MAX _MAX_PATH |
95 | #else |
96 | #include <unistd.h> |
97 | #endif |
98 | |
99 | namespace WTR { |
100 | |
101 | #if OS(WINDOWS) |
102 | static constexpr auto pathSeparator = '\\'; |
103 | #else |
104 | static constexpr auto pathSeparator = '/'; |
105 | #endif |
106 | |
107 | const unsigned TestController::viewWidth = 800; |
108 | const unsigned TestController::viewHeight = 600; |
109 | |
110 | const unsigned TestController::w3cSVGViewWidth = 480; |
111 | const unsigned TestController::w3cSVGViewHeight = 360; |
112 | |
113 | const WTF::Seconds TestController::defaultShortTimeout = 5_s; |
114 | const WTF::Seconds TestController::noTimeout = -1_s; |
115 | |
116 | static WKURLRef blankURL() |
117 | { |
118 | static WKURLRef staticBlankURL = WKURLCreateWithUTF8CString("about:blank" ); |
119 | return staticBlankURL; |
120 | } |
121 | |
122 | static WKDataRef copyWebCryptoMasterKey(WKPageRef, const void*) |
123 | { |
124 | // Any 128 bit key would do, all we need for testing is to implement the callback. |
125 | return WKDataCreate((const uint8_t*)"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" , 16); |
126 | } |
127 | |
128 | static WKStringRef copySignedPublicKeyAndChallengeString(WKPageRef, const void*) |
129 | { |
130 | // Any fake response would do, all we need for testing is to implement the callback. |
131 | return WKStringCreateWithUTF8CString("MIHFMHEwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAnX0TILJrOMUue%2BPtwBRE6XfV%0AWtKQbsshxk5ZhcUwcwyvcnIq9b82QhJdoACdD34rqfCAIND46fXKQUnb0mvKzQID%0AAQABFhFNb3ppbGxhSXNNeUZyaWVuZDANBgkqhkiG9w0BAQQFAANBAAKv2Eex2n%2FS%0Ar%2F7iJNroWlSzSMtTiQTEB%2BADWHGj9u1xrUrOilq%2Fo2cuQxIfZcNZkYAkWP4DubqW%0Ai0%2F%2FrgBvmco%3D" ); |
132 | } |
133 | |
134 | AsyncTask* AsyncTask::m_currentTask; |
135 | |
136 | bool AsyncTask::run() |
137 | { |
138 | m_currentTask = this; |
139 | m_task(); |
140 | TestController::singleton().runUntil(m_taskDone, m_timeout); |
141 | m_currentTask = nullptr; |
142 | return m_taskDone; |
143 | } |
144 | |
145 | AsyncTask* AsyncTask::currentTask() |
146 | { |
147 | return m_currentTask; |
148 | } |
149 | |
150 | static TestController* controller; |
151 | |
152 | TestController& TestController::singleton() |
153 | { |
154 | ASSERT(controller); |
155 | return *controller; |
156 | } |
157 | |
158 | TestController::TestController(int argc, const char* argv[]) |
159 | { |
160 | initialize(argc, argv); |
161 | controller = this; |
162 | run(); |
163 | controller = nullptr; |
164 | } |
165 | |
166 | TestController::~TestController() |
167 | { |
168 | // The context will be null if WebKitTestRunner was in server mode, but ran no tests. |
169 | if (m_context) |
170 | WKIconDatabaseClose(WKContextGetIconDatabase(m_context.get())); |
171 | |
172 | platformDestroy(); |
173 | } |
174 | |
175 | static WKRect getWindowFrame(WKPageRef page, const void* clientInfo) |
176 | { |
177 | PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo)); |
178 | return view->windowFrame(); |
179 | } |
180 | |
181 | static void setWindowFrame(WKPageRef page, WKRect frame, const void* clientInfo) |
182 | { |
183 | PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo)); |
184 | view->setWindowFrame(frame); |
185 | } |
186 | |
187 | static bool runBeforeUnloadConfirmPanel(WKPageRef page, WKStringRef message, WKFrameRef frame, const void*) |
188 | { |
189 | printf("CONFIRM NAVIGATION: %s\n" , toSTD(message).c_str()); |
190 | return TestController::singleton().beforeUnloadReturnValue(); |
191 | } |
192 | |
193 | static void runOpenPanel(WKPageRef page, WKFrameRef frame, WKOpenPanelParametersRef parameters, WKOpenPanelResultListenerRef resultListenerRef, const void*) |
194 | { |
195 | printf("OPEN FILE PANEL\n" ); |
196 | if (WKOpenPanelParametersGetAllowsDirectories(parameters)) |
197 | printf("-> DIRECTORIES ARE ALLOWED\n" ); |
198 | WKArrayRef fileURLs = TestController::singleton().openPanelFileURLs(); |
199 | if (!fileURLs || !WKArrayGetSize(fileURLs)) { |
200 | WKOpenPanelResultListenerCancel(resultListenerRef); |
201 | return; |
202 | } |
203 | |
204 | WKTypeRef firstItem = WKArrayGetItemAtIndex(fileURLs, 0); |
205 | |
206 | #if PLATFORM(IOS_FAMILY) |
207 | WKStringRef displayString = WKURLCopyLastPathComponent(static_cast<WKURLRef>(firstItem)); |
208 | WKDataRef mediaIcon = TestController::singleton().openPanelFileURLsMediaIcon(); |
209 | |
210 | if (mediaIcon) { |
211 | if (WKOpenPanelParametersGetAllowsMultipleFiles(parameters)) { |
212 | WKOpenPanelResultListenerChooseMediaFiles(resultListenerRef, fileURLs, displayString, mediaIcon); |
213 | return; |
214 | } |
215 | |
216 | WKOpenPanelResultListenerChooseMediaFiles(resultListenerRef, adoptWK(WKArrayCreate(&firstItem, 1)).get(), displayString, mediaIcon); |
217 | return; |
218 | } |
219 | #endif |
220 | |
221 | if (WKOpenPanelParametersGetAllowsMultipleFiles(parameters)) { |
222 | WKOpenPanelResultListenerChooseFiles(resultListenerRef, fileURLs); |
223 | return; |
224 | } |
225 | |
226 | WKOpenPanelResultListenerChooseFiles(resultListenerRef, adoptWK(WKArrayCreate(&firstItem, 1)).get()); |
227 | } |
228 | |
229 | void TestController::runModal(WKPageRef page, const void* clientInfo) |
230 | { |
231 | PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo)); |
232 | TestController::singleton().mainWebView()->setWindowIsKey(false); |
233 | runModal(view); |
234 | TestController::singleton().mainWebView()->setWindowIsKey(true); |
235 | } |
236 | |
237 | static void closeOtherPage(WKPageRef page, const void* clientInfo) |
238 | { |
239 | WKPageClose(page); |
240 | PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo)); |
241 | delete view; |
242 | } |
243 | |
244 | static void focus(WKPageRef page, const void* clientInfo) |
245 | { |
246 | PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo)); |
247 | view->focus(); |
248 | view->setWindowIsKey(true); |
249 | } |
250 | |
251 | static void unfocus(WKPageRef page, const void* clientInfo) |
252 | { |
253 | PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo)); |
254 | view->setWindowIsKey(false); |
255 | } |
256 | |
257 | static void decidePolicyForGeolocationPermissionRequest(WKPageRef, WKFrameRef, WKSecurityOriginRef, WKGeolocationPermissionRequestRef permissionRequest, const void* clientInfo) |
258 | { |
259 | TestController::singleton().handleGeolocationPermissionRequest(permissionRequest); |
260 | } |
261 | |
262 | static void decidePolicyForUserMediaPermissionRequest(WKPageRef, WKFrameRef frame, WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin, WKUserMediaPermissionRequestRef permissionRequest, const void* clientInfo) |
263 | { |
264 | TestController::singleton().handleUserMediaPermissionRequest(frame, userMediaDocumentOrigin, topLevelDocumentOrigin, permissionRequest); |
265 | } |
266 | |
267 | static void (WKPageRef page, WKStringRef alertText, WKFrameRef frame, WKSecurityOriginRef securityOrigin, WKPageRunJavaScriptAlertResultListenerRef listener, const void *clientInfo) |
268 | { |
269 | TestController::singleton().handleJavaScriptAlert(listener); |
270 | } |
271 | |
272 | static void checkUserMediaPermissionForOrigin(WKPageRef, WKFrameRef frame, WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin, WKUserMediaPermissionCheckRef checkRequest, const void*) |
273 | { |
274 | TestController::singleton().handleCheckOfUserMediaPermissionForOrigin(frame, userMediaDocumentOrigin, topLevelDocumentOrigin, checkRequest); |
275 | } |
276 | |
277 | static void requestPointerLock(WKPageRef page, const void*) |
278 | { |
279 | WKPageDidAllowPointerLock(page); |
280 | } |
281 | |
282 | static void printFrame(WKPageRef page, WKFrameRef frame, const void*) |
283 | { |
284 | WKPageBeginPrinting(page, frame, WKPrintInfo { 1, 21, 29.7f }); |
285 | WKPageEndPrinting(page); |
286 | } |
287 | |
288 | static bool shouldAllowDeviceOrientationAndMotionAccess(WKPageRef, WKSecurityOriginRef origin, const void*) |
289 | { |
290 | return TestController::singleton().handleDeviceOrientationAndMotionAccessRequest(origin); |
291 | } |
292 | |
293 | WKPageRef TestController::createOtherPage(WKPageRef, WKPageConfigurationRef configuration, WKNavigationActionRef navigationAction, WKWindowFeaturesRef windowFeatures, const void *clientInfo) |
294 | { |
295 | PlatformWebView* parentView = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo)); |
296 | return TestController::singleton().createOtherPage(parentView, configuration, navigationAction, windowFeatures); |
297 | } |
298 | |
299 | WKPageRef TestController::createOtherPage(PlatformWebView* parentView, WKPageConfigurationRef configuration, WKNavigationActionRef navigationAction, WKWindowFeaturesRef windowFeatures) |
300 | { |
301 | // The test needs to call testRunner.setCanOpenWindows() to open new windows. |
302 | if (!m_currentInvocation->canOpenWindows()) |
303 | return nullptr; |
304 | |
305 | PlatformWebView* view = platformCreateOtherPage(parentView, configuration, parentView->options()); |
306 | WKPageRef newPage = view->page(); |
307 | |
308 | view->resizeTo(800, 600); |
309 | |
310 | WKPageUIClientV8 otherPageUIClient = { |
311 | { 8, view }, |
312 | 0, // createNewPage_deprecatedForUseWithV0 |
313 | 0, // showPage |
314 | closeOtherPage, |
315 | 0, // takeFocus |
316 | focus, |
317 | unfocus, |
318 | 0, // runJavaScriptAlert_deprecatedForUseWithV0 |
319 | 0, // runJavaScriptAlert_deprecatedForUseWithV0 |
320 | 0, // runJavaScriptAlert_deprecatedForUseWithV0 |
321 | 0, // setStatusText |
322 | 0, // mouseDidMoveOverElement_deprecatedForUseWithV0 |
323 | 0, // missingPluginButtonClicked |
324 | 0, // didNotHandleKeyEvent |
325 | 0, // didNotHandleWheelEvent |
326 | 0, // toolbarsAreVisible |
327 | 0, // setToolbarsAreVisible |
328 | 0, // menuBarIsVisible |
329 | 0, // setMenuBarIsVisible |
330 | 0, // statusBarIsVisible |
331 | 0, // setStatusBarIsVisible |
332 | 0, // isResizable |
333 | 0, // setIsResizable |
334 | getWindowFrame, |
335 | setWindowFrame, |
336 | runBeforeUnloadConfirmPanel, |
337 | 0, // didDraw |
338 | 0, // pageDidScroll |
339 | 0, // exceededDatabaseQuota |
340 | runOpenPanel, |
341 | decidePolicyForGeolocationPermissionRequest, |
342 | 0, // headerHeight |
343 | 0, // footerHeight |
344 | 0, // drawHeader |
345 | 0, // drawFooter |
346 | printFrame, |
347 | runModal, |
348 | 0, // didCompleteRubberBandForMainFrame |
349 | 0, // saveDataToFileInDownloadsFolder |
350 | 0, // shouldInterruptJavaScript |
351 | 0, // createNewPage_deprecatedForUseWithV1 |
352 | 0, // mouseDidMoveOverElement |
353 | 0, // decidePolicyForNotificationPermissionRequest |
354 | 0, // unavailablePluginButtonClicked_deprecatedForUseWithV1 |
355 | 0, // showColorPicker |
356 | 0, // hideColorPicker |
357 | 0, // unavailablePluginButtonClicked |
358 | 0, // pinnedStateDidChange |
359 | 0, // didBeginTrackingPotentialLongMousePress |
360 | 0, // didRecognizeLongMousePress |
361 | 0, // didCancelTrackingPotentialLongMousePress |
362 | 0, // isPlayingAudioDidChange |
363 | decidePolicyForUserMediaPermissionRequest, |
364 | 0, // didClickAutofillButton |
365 | 0, // runJavaScriptAlert |
366 | 0, // runJavaScriptConfirm |
367 | 0, // runJavaScriptPrompt |
368 | 0, // mediaSessionMetadataDidChange |
369 | createOtherPage, |
370 | runJavaScriptAlert, |
371 | 0, // runJavaScriptConfirm |
372 | 0, // runJavaScriptPrompt |
373 | checkUserMediaPermissionForOrigin, |
374 | 0, // runBeforeUnloadConfirmPanel |
375 | 0, // fullscreenMayReturnToInline |
376 | requestPointerLock, |
377 | 0, |
378 | }; |
379 | WKPageSetPageUIClient(newPage, &otherPageUIClient.base); |
380 | |
381 | WKPageNavigationClientV3 pageNavigationClient = { |
382 | { 3, &TestController::singleton() }, |
383 | decidePolicyForNavigationAction, |
384 | decidePolicyForNavigationResponse, |
385 | decidePolicyForPluginLoad, |
386 | 0, // didStartProvisionalNavigation |
387 | didReceiveServerRedirectForProvisionalNavigation, |
388 | 0, // didFailProvisionalNavigation |
389 | 0, // didCommitNavigation |
390 | 0, // didFinishNavigation |
391 | 0, // didFailNavigation |
392 | 0, // didFailProvisionalLoadInSubframe |
393 | 0, // didFinishDocumentLoad |
394 | 0, // didSameDocumentNavigation |
395 | 0, // renderingProgressDidChange |
396 | canAuthenticateAgainstProtectionSpace, |
397 | didReceiveAuthenticationChallenge, |
398 | processDidCrash, |
399 | copyWebCryptoMasterKey, |
400 | didBeginNavigationGesture, |
401 | willEndNavigationGesture, |
402 | didEndNavigationGesture, |
403 | didRemoveNavigationGestureSnapshot, |
404 | 0, // webProcessDidTerminate |
405 | 0, // contentRuleListNotification |
406 | copySignedPublicKeyAndChallengeString |
407 | }; |
408 | WKPageSetPageNavigationClient(newPage, &pageNavigationClient.base); |
409 | |
410 | view->didInitializeClients(); |
411 | |
412 | TestController::singleton().updateWindowScaleForTest(view, *TestController::singleton().m_currentInvocation); |
413 | |
414 | WKRetain(newPage); |
415 | return newPage; |
416 | } |
417 | |
418 | const char* TestController::libraryPathForTesting() |
419 | { |
420 | // FIXME: This may not be sufficient to prevent interactions/crashes |
421 | // when running more than one copy of DumpRenderTree. |
422 | // See https://bugs.webkit.org/show_bug.cgi?id=10906 |
423 | char* dumpRenderTreeTemp = getenv("DUMPRENDERTREE_TEMP" ); |
424 | if (dumpRenderTreeTemp) |
425 | return dumpRenderTreeTemp; |
426 | return platformLibraryPathForTesting(); |
427 | } |
428 | |
429 | void TestController::initialize(int argc, const char* argv[]) |
430 | { |
431 | JSC::initializeThreading(); |
432 | RunLoop::initializeMainRunLoop(); |
433 | WTF::setProcessPrivileges(allPrivileges()); |
434 | |
435 | platformInitialize(); |
436 | |
437 | Options options; |
438 | OptionsHandler optionsHandler(options); |
439 | |
440 | if (argc < 2) { |
441 | optionsHandler.printHelp(); |
442 | exit(1); |
443 | } |
444 | if (!optionsHandler.parse(argc, argv)) |
445 | exit(1); |
446 | |
447 | m_useWaitToDumpWatchdogTimer = options.useWaitToDumpWatchdogTimer; |
448 | m_forceNoTimeout = options.forceNoTimeout; |
449 | m_verbose = options.verbose; |
450 | m_gcBetweenTests = options.gcBetweenTests; |
451 | m_shouldDumpPixelsForAllTests = options.shouldDumpPixelsForAllTests; |
452 | m_forceComplexText = options.forceComplexText; |
453 | m_shouldUseAcceleratedDrawing = options.shouldUseAcceleratedDrawing; |
454 | m_shouldUseRemoteLayerTree = options.shouldUseRemoteLayerTree; |
455 | m_paths = options.paths; |
456 | m_allowedHosts = options.allowedHosts; |
457 | m_shouldShowWebView = options.shouldShowWebView; |
458 | m_shouldShowTouches = options.shouldShowTouches; |
459 | m_checkForWorldLeaks = options.checkForWorldLeaks; |
460 | m_allowAnyHTTPSCertificateForAllowedHosts = options.allowAnyHTTPSCertificateForAllowedHosts; |
461 | |
462 | if (options.printSupportedFeatures) { |
463 | // FIXME: On Windows, DumpRenderTree uses this to expose whether it supports 3d |
464 | // transforms and accelerated compositing. When we support those features, we |
465 | // should match DRT's behavior. |
466 | exit(0); |
467 | } |
468 | |
469 | m_usingServerMode = (m_paths.size() == 1 && m_paths[0] == "-" ); |
470 | if (m_usingServerMode) |
471 | m_printSeparators = true; |
472 | else |
473 | m_printSeparators = m_paths.size() > 1; |
474 | |
475 | initializeInjectedBundlePath(); |
476 | initializeTestPluginDirectory(); |
477 | |
478 | #if PLATFORM(MAC) |
479 | WebCoreTestSupport::installMockGamepadProvider(); |
480 | #endif |
481 | |
482 | WKRetainPtr<WKStringRef> pageGroupIdentifier = adoptWK(WKStringCreateWithUTF8CString("WebKitTestRunnerPageGroup" )); |
483 | m_pageGroup.adopt(WKPageGroupCreateWithIdentifier(pageGroupIdentifier.get())); |
484 | |
485 | m_eventSenderProxy = std::make_unique<EventSenderProxy>(this); |
486 | } |
487 | |
488 | WKRetainPtr<WKContextConfigurationRef> TestController::generateContextConfiguration(const TestOptions::ContextOptions& options) const |
489 | { |
490 | auto configuration = adoptWK(WKContextConfigurationCreate()); |
491 | WKContextConfigurationSetInjectedBundlePath(configuration.get(), injectedBundlePath()); |
492 | WKContextConfigurationSetFullySynchronousModeIsAllowedForTesting(configuration.get(), true); |
493 | WKContextConfigurationSetIgnoreSynchronousMessagingTimeoutsForTesting(configuration.get(), options.ignoreSynchronousMessagingTimeouts); |
494 | |
495 | WKRetainPtr<WKMutableArrayRef> overrideLanguages = adoptWK(WKMutableArrayCreate()); |
496 | for (auto& language : options.overrideLanguages) |
497 | WKArrayAppendItem(overrideLanguages.get(), adoptWK(WKStringCreateWithUTF8CString(language.utf8().data())).get()); |
498 | WKContextConfigurationSetOverrideLanguages(configuration.get(), overrideLanguages.get()); |
499 | |
500 | if (options.shouldEnableProcessSwapOnNavigation()) { |
501 | WKContextConfigurationSetProcessSwapsOnNavigation(configuration.get(), true); |
502 | if (options.enableProcessSwapOnWindowOpen) |
503 | WKContextConfigurationSetProcessSwapsOnWindowOpenWithOpener(configuration.get(), true); |
504 | } |
505 | |
506 | if (const char* dumpRenderTreeTemp = libraryPathForTesting()) { |
507 | String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp); |
508 | |
509 | WKContextConfigurationSetApplicationCacheDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "ApplicationCache" ).get()); |
510 | WKContextConfigurationSetDiskCacheDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "Cache" ).get()); |
511 | WKContextConfigurationSetIndexedDBDatabaseDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "Databases" + pathSeparator + "IndexedDB" ).get()); |
512 | WKContextConfigurationSetLocalStorageDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "LocalStorage" ).get()); |
513 | WKContextConfigurationSetWebSQLDatabaseDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "Databases" + pathSeparator + "WebSQL" ).get()); |
514 | WKContextConfigurationSetMediaKeysStorageDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "MediaKeys" ).get()); |
515 | WKContextConfigurationSetResourceLoadStatisticsDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "ResourceLoadStatistics" ).get()); |
516 | } |
517 | return configuration; |
518 | } |
519 | |
520 | WKRetainPtr<WKPageConfigurationRef> TestController::generatePageConfiguration(const TestOptions& options) |
521 | { |
522 | if (!m_context || !m_contextOptions->hasSameInitializationOptions(options.contextOptions)) { |
523 | auto contextConfiguration = generateContextConfiguration(options.contextOptions); |
524 | m_context = platformAdjustContext(adoptWK(WKContextCreateWithConfiguration(contextConfiguration.get())).get(), contextConfiguration.get()); |
525 | m_contextOptions = options.contextOptions; |
526 | |
527 | m_geolocationProvider = std::make_unique<GeolocationProviderMock>(m_context.get()); |
528 | |
529 | if (const char* dumpRenderTreeTemp = libraryPathForTesting()) { |
530 | String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp); |
531 | |
532 | // FIXME: This should be migrated to WKContextConfigurationRef. |
533 | // Disable icon database to avoid fetching <http://127.0.0.1:8000/favicon.ico> and making tests flaky. |
534 | // Invividual tests can enable it using testRunner.setIconDatabaseEnabled, although it's not currently supported in WebKitTestRunner. |
535 | WKContextSetIconDatabasePath(m_context.get(), toWK(emptyString()).get()); |
536 | } |
537 | |
538 | WKContextSetDiskCacheSpeculativeValidationEnabled(m_context.get(), true); |
539 | WKContextUseTestingNetworkSession(m_context.get()); |
540 | WKContextSetCacheModel(m_context.get(), kWKCacheModelDocumentBrowser); |
541 | |
542 | auto* websiteDataStore = WKContextGetWebsiteDataStore(m_context.get()); |
543 | WKWebsiteDataStoreSetPerOriginStorageQuota(websiteDataStore, 400 * 1024); |
544 | |
545 | platformInitializeContext(); |
546 | } |
547 | |
548 | WKContextInjectedBundleClientV1 injectedBundleClient = { |
549 | { 1, this }, |
550 | didReceiveMessageFromInjectedBundle, |
551 | didReceiveSynchronousMessageFromInjectedBundle, |
552 | getInjectedBundleInitializationUserData, |
553 | }; |
554 | WKContextSetInjectedBundleClient(m_context.get(), &injectedBundleClient.base); |
555 | |
556 | WKContextClientV2 contextClient = { |
557 | { 2, this }, |
558 | 0, // plugInAutoStartOriginHashesChanged |
559 | networkProcessDidCrash, |
560 | 0, // plugInInformationBecameAvailable |
561 | 0, // copyWebCryptoMasterKey |
562 | }; |
563 | WKContextSetClient(m_context.get(), &contextClient.base); |
564 | |
565 | WKContextHistoryClientV0 historyClient = { |
566 | { 0, this }, |
567 | didNavigateWithNavigationData, |
568 | didPerformClientRedirect, |
569 | didPerformServerRedirect, |
570 | didUpdateHistoryTitle, |
571 | 0, // populateVisitedLinks |
572 | }; |
573 | WKContextSetHistoryClient(m_context.get(), &historyClient.base); |
574 | |
575 | WKNotificationManagerRef notificationManager = WKContextGetNotificationManager(m_context.get()); |
576 | WKNotificationProviderV0 notificationKit = m_webNotificationProvider.provider(); |
577 | WKNotificationManagerSetProvider(notificationManager, ¬ificationKit.base); |
578 | |
579 | if (testPluginDirectory()) |
580 | WKContextSetAdditionalPluginsDirectory(m_context.get(), testPluginDirectory()); |
581 | |
582 | if (m_forceComplexText) |
583 | WKContextSetAlwaysUsesComplexTextCodePath(m_context.get(), true); |
584 | |
585 | auto pageConfiguration = adoptWK(WKPageConfigurationCreate()); |
586 | WKPageConfigurationSetContext(pageConfiguration.get(), m_context.get()); |
587 | WKPageConfigurationSetPageGroup(pageConfiguration.get(), m_pageGroup.get()); |
588 | |
589 | m_userContentController = adoptWK(WKUserContentControllerCreate()); |
590 | WKPageConfigurationSetUserContentController(pageConfiguration.get(), userContentController()); |
591 | return pageConfiguration; |
592 | } |
593 | |
594 | void TestController::createWebViewWithOptions(const TestOptions& options) |
595 | { |
596 | auto configuration = generatePageConfiguration(options); |
597 | |
598 | // Some preferences (notably mock scroll bars setting) currently cannot be re-applied to an existing view, so we need to set them now. |
599 | // FIXME: Migrate these preferences to WKContextConfigurationRef. |
600 | resetPreferencesToConsistentValues(options); |
601 | |
602 | platformCreateWebView(configuration.get(), options); |
603 | WKPageUIClientV13 pageUIClient = { |
604 | { 13, m_mainWebView.get() }, |
605 | 0, // createNewPage_deprecatedForUseWithV0 |
606 | 0, // showPage |
607 | 0, // close |
608 | 0, // takeFocus |
609 | focus, |
610 | unfocus, |
611 | 0, // runJavaScriptAlert_deprecatedForUseWithV0 |
612 | 0, // runJavaScriptAlert_deprecatedForUseWithV0 |
613 | 0, // runJavaScriptAlert_deprecatedForUseWithV0 |
614 | 0, // setStatusText |
615 | 0, // mouseDidMoveOverElement_deprecatedForUseWithV0 |
616 | 0, // missingPluginButtonClicked |
617 | 0, // didNotHandleKeyEvent |
618 | 0, // didNotHandleWheelEvent |
619 | 0, // toolbarsAreVisible |
620 | 0, // setToolbarsAreVisible |
621 | 0, // menuBarIsVisible |
622 | 0, // setMenuBarIsVisible |
623 | 0, // statusBarIsVisible |
624 | 0, // setStatusBarIsVisible |
625 | 0, // isResizable |
626 | 0, // setIsResizable |
627 | getWindowFrame, |
628 | setWindowFrame, |
629 | runBeforeUnloadConfirmPanel, |
630 | 0, // didDraw |
631 | 0, // pageDidScroll |
632 | 0, // exceededDatabaseQuota, |
633 | options.shouldHandleRunOpenPanel ? runOpenPanel : 0, |
634 | decidePolicyForGeolocationPermissionRequest, |
635 | 0, // headerHeight |
636 | 0, // footerHeight |
637 | 0, // drawHeader |
638 | 0, // drawFooter |
639 | printFrame, |
640 | runModal, |
641 | 0, // didCompleteRubberBandForMainFrame |
642 | 0, // saveDataToFileInDownloadsFolder |
643 | 0, // shouldInterruptJavaScript |
644 | 0, // createNewPage_deprecatedForUseWithV1 |
645 | 0, // mouseDidMoveOverElement |
646 | decidePolicyForNotificationPermissionRequest, // decidePolicyForNotificationPermissionRequest |
647 | 0, // unavailablePluginButtonClicked_deprecatedForUseWithV1 |
648 | 0, // showColorPicker |
649 | 0, // hideColorPicker |
650 | unavailablePluginButtonClicked, |
651 | 0, // pinnedStateDidChange |
652 | 0, // didBeginTrackingPotentialLongMousePress |
653 | 0, // didRecognizeLongMousePress |
654 | 0, // didCancelTrackingPotentialLongMousePress |
655 | 0, // isPlayingAudioDidChange |
656 | decidePolicyForUserMediaPermissionRequest, |
657 | 0, // didClickAutofillButton |
658 | 0, // runJavaScriptAlert |
659 | 0, // runJavaScriptConfirm |
660 | 0, // runJavaScriptPrompt |
661 | 0, // mediaSessionMetadataDidChange |
662 | createOtherPage, |
663 | runJavaScriptAlert, |
664 | 0, // runJavaScriptConfirm |
665 | 0, // runJavaScriptPrompt |
666 | checkUserMediaPermissionForOrigin, |
667 | 0, // runBeforeUnloadConfirmPanel |
668 | 0, // fullscreenMayReturnToInline |
669 | requestPointerLock, |
670 | 0, // didLosePointerLock |
671 | 0, // handleAutoplayEvent |
672 | 0, // hasVideoInPictureInPictureDidChange |
673 | 0, // didExceedBackgroundResourceLimitWhileInForeground |
674 | 0, // didResignInputElementStrongPasswordAppearance |
675 | 0, // requestStorageAccessConfirm |
676 | shouldAllowDeviceOrientationAndMotionAccess |
677 | }; |
678 | WKPageSetPageUIClient(m_mainWebView->page(), &pageUIClient.base); |
679 | |
680 | WKPageNavigationClientV3 pageNavigationClient = { |
681 | { 3, this }, |
682 | decidePolicyForNavigationAction, |
683 | decidePolicyForNavigationResponse, |
684 | decidePolicyForPluginLoad, |
685 | 0, // didStartProvisionalNavigation |
686 | didReceiveServerRedirectForProvisionalNavigation, |
687 | 0, // didFailProvisionalNavigation |
688 | didCommitNavigation, |
689 | didFinishNavigation, |
690 | 0, // didFailNavigation |
691 | 0, // didFailProvisionalLoadInSubframe |
692 | 0, // didFinishDocumentLoad |
693 | 0, // didSameDocumentNavigation |
694 | 0, // renderingProgressDidChange |
695 | canAuthenticateAgainstProtectionSpace, |
696 | didReceiveAuthenticationChallenge, |
697 | processDidCrash, |
698 | copyWebCryptoMasterKey, |
699 | didBeginNavigationGesture, |
700 | willEndNavigationGesture, |
701 | didEndNavigationGesture, |
702 | didRemoveNavigationGestureSnapshot, |
703 | 0, // webProcessDidTerminate |
704 | 0, // contentRuleListNotification |
705 | copySignedPublicKeyAndChallengeString |
706 | }; |
707 | WKPageSetPageNavigationClient(m_mainWebView->page(), &pageNavigationClient.base); |
708 | |
709 | WKContextDownloadClientV1 downloadClient = { |
710 | { 1, this }, |
711 | downloadDidStart, |
712 | 0, // didReceiveAuthenticationChallenge |
713 | 0, // didReceiveResponse |
714 | 0, // didReceiveData |
715 | 0, // shouldDecodeSourceDataOfMIMEType |
716 | decideDestinationWithSuggestedFilename, |
717 | 0, // didCreateDestination |
718 | downloadDidFinish, |
719 | downloadDidFail, |
720 | downloadDidCancel, |
721 | 0, // processDidCrash; |
722 | downloadDidReceiveServerRedirectToURL |
723 | }; |
724 | WKContextSetDownloadClient(context(), &downloadClient.base); |
725 | |
726 | // this should just be done on the page? |
727 | WKPageInjectedBundleClientV0 injectedBundleClient = { |
728 | { 0, this }, |
729 | didReceivePageMessageFromInjectedBundle, |
730 | didReceiveSynchronousPageMessageFromInjectedBundle |
731 | }; |
732 | WKPageSetPageInjectedBundleClient(m_mainWebView->page(), &injectedBundleClient.base); |
733 | |
734 | m_mainWebView->didInitializeClients(); |
735 | |
736 | // Generally, the tests should default to running at 1x. updateWindowScaleForTest() will adjust the scale to |
737 | // something else for specific tests that need to run at a different window scale. |
738 | m_mainWebView->changeWindowScaleIfNeeded(1); |
739 | } |
740 | |
741 | void TestController::ensureViewSupportsOptionsForTest(const TestInvocation& test) |
742 | { |
743 | auto options = test.options(); |
744 | |
745 | if (m_mainWebView) { |
746 | if (m_mainWebView->viewSupportsOptions(options)) |
747 | return; |
748 | |
749 | willDestroyWebView(); |
750 | |
751 | WKPageSetPageUIClient(m_mainWebView->page(), nullptr); |
752 | WKPageSetPageNavigationClient(m_mainWebView->page(), nullptr); |
753 | WKPageClose(m_mainWebView->page()); |
754 | |
755 | m_mainWebView = nullptr; |
756 | } |
757 | |
758 | createWebViewWithOptions(options); |
759 | |
760 | if (!resetStateToConsistentValues(options, ResetStage::BeforeTest)) |
761 | TestInvocation::dumpWebProcessUnresponsiveness("<unknown> - TestController::run - Failed to reset state to consistent values\n" ); |
762 | } |
763 | |
764 | void TestController::resetPreferencesToConsistentValues(const TestOptions& options) |
765 | { |
766 | // Reset preferences |
767 | WKPreferencesRef preferences = platformPreferences(); |
768 | WKPreferencesResetTestRunnerOverrides(preferences); |
769 | |
770 | WKPreferencesEnableAllExperimentalFeatures(preferences); |
771 | for (const auto& experimentalFeature : options.experimentalFeatures) |
772 | WKPreferencesSetExperimentalFeatureForKey(preferences, experimentalFeature.value, toWK(experimentalFeature.key).get()); |
773 | |
774 | WKPreferencesResetAllInternalDebugFeatures(preferences); |
775 | |
776 | // Set internal features that have different default values for testing. |
777 | static WKStringRef asyncOverflowScrollingFeature = WKStringCreateWithUTF8CString("AsyncOverflowScrollingEnabled" ); |
778 | WKPreferencesSetInternalDebugFeatureForKey(preferences, false, asyncOverflowScrollingFeature); |
779 | |
780 | static WKStringRef asyncFrameScrollingFeature = WKStringCreateWithUTF8CString("AsyncFrameScrollingEnabled" ); |
781 | WKPreferencesSetInternalDebugFeatureForKey(preferences, false, asyncFrameScrollingFeature); |
782 | |
783 | for (const auto& internalDebugFeature : options.internalDebugFeatures) |
784 | WKPreferencesSetInternalDebugFeatureForKey(preferences, internalDebugFeature.value, toWK(internalDebugFeature.key).get()); |
785 | |
786 | WKPreferencesSetProcessSwapOnNavigationEnabled(preferences, options.contextOptions.shouldEnableProcessSwapOnNavigation()); |
787 | WKPreferencesSetPageVisibilityBasedProcessSuppressionEnabled(preferences, options.enableAppNap); |
788 | WKPreferencesSetOfflineWebApplicationCacheEnabled(preferences, true); |
789 | WKPreferencesSetSubpixelAntialiasedLayerTextEnabled(preferences, false); |
790 | WKPreferencesSetXSSAuditorEnabled(preferences, false); |
791 | WKPreferencesSetWebAudioEnabled(preferences, true); |
792 | WKPreferencesSetMediaDevicesEnabled(preferences, true); |
793 | WKPreferencesSetWebRTCMDNSICECandidatesEnabled(preferences, false); |
794 | WKPreferencesSetDeveloperExtrasEnabled(preferences, true); |
795 | WKPreferencesSetJavaScriptRuntimeFlags(preferences, kWKJavaScriptRuntimeFlagsAllEnabled); |
796 | WKPreferencesSetJavaScriptCanOpenWindowsAutomatically(preferences, true); |
797 | WKPreferencesSetJavaScriptCanAccessClipboard(preferences, true); |
798 | WKPreferencesSetDOMPasteAllowed(preferences, options.domPasteAllowed); |
799 | WKPreferencesSetUniversalAccessFromFileURLsAllowed(preferences, true); |
800 | WKPreferencesSetFileAccessFromFileURLsAllowed(preferences, true); |
801 | #if ENABLE(FULLSCREEN_API) |
802 | WKPreferencesSetFullScreenEnabled(preferences, true); |
803 | #endif |
804 | WKPreferencesSetPageCacheEnabled(preferences, false); |
805 | WKPreferencesSetAsynchronousPluginInitializationEnabled(preferences, false); |
806 | WKPreferencesSetAsynchronousPluginInitializationEnabledForAllPlugins(preferences, false); |
807 | WKPreferencesSetArtificialPluginInitializationDelayEnabled(preferences, false); |
808 | WKPreferencesSetTabToLinksEnabled(preferences, false); |
809 | WKPreferencesSetInteractiveFormValidationEnabled(preferences, true); |
810 | WKPreferencesSetDataTransferItemsEnabled(preferences, true); |
811 | WKPreferencesSetCustomPasteboardDataEnabled(preferences, true); |
812 | |
813 | WKPreferencesSetMockScrollbarsEnabled(preferences, options.useMockScrollbars); |
814 | WKPreferencesSetNeedsSiteSpecificQuirks(preferences, options.needsSiteSpecificQuirks); |
815 | WKPreferencesSetAttachmentElementEnabled(preferences, options.enableAttachmentElement); |
816 | WKPreferencesSetMenuItemElementEnabled(preferences, options.enableMenuItemElement); |
817 | WKPreferencesSetModernMediaControlsEnabled(preferences, options.enableModernMediaControls); |
818 | WKPreferencesSetWebAuthenticationEnabled(preferences, options.enableWebAuthentication); |
819 | WKPreferencesSetWebAuthenticationLocalAuthenticatorEnabled(preferences, options.enableWebAuthenticationLocalAuthenticator); |
820 | WKPreferencesSetIsSecureContextAttributeEnabled(preferences, options.enableIsSecureContextAttribute); |
821 | WKPreferencesSetAllowCrossOriginSubresourcesToAskForCredentials(preferences, options.allowCrossOriginSubresourcesToAskForCredentials); |
822 | WKPreferencesSetColorFilterEnabled(preferences, options.enableColorFilter); |
823 | WKPreferencesSetPunchOutWhiteBackgroundsInDarkMode(preferences, options.punchOutWhiteBackgroundsInDarkMode); |
824 | |
825 | static WKStringRef defaultTextEncoding = WKStringCreateWithUTF8CString("ISO-8859-1" ); |
826 | WKPreferencesSetDefaultTextEncodingName(preferences, defaultTextEncoding); |
827 | |
828 | static WKStringRef standardFontFamily = WKStringCreateWithUTF8CString("Times" ); |
829 | static WKStringRef cursiveFontFamily = WKStringCreateWithUTF8CString("Apple Chancery" ); |
830 | static WKStringRef fantasyFontFamily = WKStringCreateWithUTF8CString("Papyrus" ); |
831 | static WKStringRef fixedFontFamily = WKStringCreateWithUTF8CString("Courier" ); |
832 | static WKStringRef pictographFontFamily = WKStringCreateWithUTF8CString("Apple Color Emoji" ); |
833 | static WKStringRef sansSerifFontFamily = WKStringCreateWithUTF8CString("Helvetica" ); |
834 | static WKStringRef serifFontFamily = WKStringCreateWithUTF8CString("Times" ); |
835 | |
836 | WKPreferencesSetMinimumFontSize(preferences, 0); |
837 | WKPreferencesSetStandardFontFamily(preferences, standardFontFamily); |
838 | WKPreferencesSetCursiveFontFamily(preferences, cursiveFontFamily); |
839 | WKPreferencesSetFantasyFontFamily(preferences, fantasyFontFamily); |
840 | WKPreferencesSetFixedFontFamily(preferences, fixedFontFamily); |
841 | WKPreferencesSetPictographFontFamily(preferences, pictographFontFamily); |
842 | WKPreferencesSetSansSerifFontFamily(preferences, sansSerifFontFamily); |
843 | WKPreferencesSetSerifFontFamily(preferences, serifFontFamily); |
844 | WKPreferencesSetAsynchronousSpellCheckingEnabled(preferences, false); |
845 | #if ENABLE(MEDIA_SOURCE) |
846 | WKPreferencesSetMediaSourceEnabled(preferences, true); |
847 | WKPreferencesSetSourceBufferChangeTypeEnabled(preferences, true); |
848 | #endif |
849 | |
850 | WKPreferencesSetHiddenPageDOMTimerThrottlingEnabled(preferences, false); |
851 | WKPreferencesSetHiddenPageCSSAnimationSuspensionEnabled(preferences, false); |
852 | |
853 | WKPreferencesSetAcceleratedDrawingEnabled(preferences, m_shouldUseAcceleratedDrawing || options.useAcceleratedDrawing); |
854 | // FIXME: We should be testing the default. |
855 | WKPreferencesSetStorageBlockingPolicy(preferences, kWKAllowAllStorage); |
856 | |
857 | WKPreferencesSetFetchAPIKeepAliveEnabled(preferences, true); |
858 | WKPreferencesSetResourceTimingEnabled(preferences, true); |
859 | WKPreferencesSetUserTimingEnabled(preferences, true); |
860 | WKPreferencesSetMediaPreloadingEnabled(preferences, true); |
861 | WKPreferencesSetMediaPlaybackAllowsInline(preferences, true); |
862 | WKPreferencesSetInlineMediaPlaybackRequiresPlaysInlineAttribute(preferences, false); |
863 | WKPreferencesSetBeaconAPIEnabled(preferences, true); |
864 | WKPreferencesSetDirectoryUploadEnabled(preferences, true); |
865 | |
866 | WKCookieManagerDeleteAllCookies(WKContextGetCookieManager(m_context.get())); |
867 | |
868 | WKPreferencesSetMockCaptureDevicesEnabled(preferences, true); |
869 | |
870 | WKPreferencesSetLargeImageAsyncDecodingEnabled(preferences, false); |
871 | |
872 | WKPreferencesSetInspectorAdditionsEnabled(preferences, options.enableInspectorAdditions); |
873 | |
874 | WKPreferencesSetStorageAccessAPIEnabled(preferences, true); |
875 | |
876 | WKPreferencesSetAccessibilityObjectModelEnabled(preferences, true); |
877 | WKPreferencesSetCSSOMViewScrollingAPIEnabled(preferences, true); |
878 | WKPreferencesSetMediaCapabilitiesEnabled(preferences, true); |
879 | |
880 | WKPreferencesSetRestrictedHTTPResponseAccess(preferences, true); |
881 | |
882 | WKPreferencesSetServerTimingEnabled(preferences, true); |
883 | |
884 | WKPreferencesSetWebSQLDisabled(preferences, false); |
885 | |
886 | WKPreferencesSetMediaPlaybackRequiresUserGesture(preferences, false); |
887 | WKPreferencesSetVideoPlaybackRequiresUserGesture(preferences, false); |
888 | WKPreferencesSetAudioPlaybackRequiresUserGesture(preferences, false); |
889 | |
890 | platformResetPreferencesToConsistentValues(); |
891 | } |
892 | |
893 | bool TestController::resetStateToConsistentValues(const TestOptions& options, ResetStage resetStage) |
894 | { |
895 | SetForScope<State> changeState(m_state, Resetting); |
896 | m_beforeUnloadReturnValue = true; |
897 | |
898 | // This setting differs between the antique and modern Mac WebKit2 API. |
899 | // For now, maintain the antique behavior, because some tests depend on it! |
900 | // FIXME: We should be testing the default. |
901 | WKPageSetBackgroundExtendsBeyondPage(m_mainWebView->page(), false); |
902 | |
903 | WKPageSetCustomUserAgent(m_mainWebView->page(), nullptr); |
904 | |
905 | WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("Reset" )); |
906 | WKRetainPtr<WKMutableDictionaryRef> resetMessageBody = adoptWK(WKMutableDictionaryCreate()); |
907 | |
908 | WKRetainPtr<WKStringRef> shouldGCKey = adoptWK(WKStringCreateWithUTF8CString("ShouldGC" )); |
909 | WKRetainPtr<WKBooleanRef> shouldGCValue = adoptWK(WKBooleanCreate(m_gcBetweenTests)); |
910 | WKDictionarySetItem(resetMessageBody.get(), shouldGCKey.get(), shouldGCValue.get()); |
911 | |
912 | WKRetainPtr<WKStringRef> allowedHostsKey = adoptWK(WKStringCreateWithUTF8CString("AllowedHosts" )); |
913 | WKRetainPtr<WKMutableArrayRef> allowedHostsValue = adoptWK(WKMutableArrayCreate()); |
914 | for (auto& host : m_allowedHosts) { |
915 | WKRetainPtr<WKStringRef> wkHost = adoptWK(WKStringCreateWithUTF8CString(host.c_str())); |
916 | WKArrayAppendItem(allowedHostsValue.get(), wkHost.get()); |
917 | } |
918 | WKDictionarySetItem(resetMessageBody.get(), allowedHostsKey.get(), allowedHostsValue.get()); |
919 | |
920 | if (options.jscOptions.length()) { |
921 | WKRetainPtr<WKStringRef> jscOptionsKey = adoptWK(WKStringCreateWithUTF8CString("JSCOptions" )); |
922 | WKRetainPtr<WKStringRef> jscOptionsValue = adoptWK(WKStringCreateWithUTF8CString(options.jscOptions.c_str())); |
923 | WKDictionarySetItem(resetMessageBody.get(), jscOptionsKey.get(), jscOptionsValue.get()); |
924 | } |
925 | |
926 | #if PLATFORM(COCOA) |
927 | WebCoreTestSupport::setAdditionalSupportedImageTypesForTesting(options.additionalSupportedImageTypes.c_str()); |
928 | #endif |
929 | |
930 | WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), resetMessageBody.get()); |
931 | |
932 | WKContextSetShouldUseFontSmoothing(TestController::singleton().context(), false); |
933 | |
934 | WKContextSetCacheModel(TestController::singleton().context(), kWKCacheModelDocumentBrowser); |
935 | |
936 | WKContextClearCachedCredentials(TestController::singleton().context()); |
937 | |
938 | auto websiteDataStore = WKContextGetWebsiteDataStore(TestController::singleton().context()); |
939 | WKWebsiteDataStoreClearAllDeviceOrientationPermissions(websiteDataStore); |
940 | |
941 | ClearIndexedDatabases(); |
942 | setIDBPerOriginQuota(50 * MB); |
943 | |
944 | clearServiceWorkerRegistrations(); |
945 | clearDOMCaches(); |
946 | |
947 | WKContextSetAllowsAnySSLCertificateForServiceWorkerTesting(platformContext(), true); |
948 | |
949 | WKContextClearCurrentModifierStateForTesting(TestController::singleton().context()); |
950 | |
951 | // FIXME: This function should also ensure that there is only one page open. |
952 | |
953 | // Reset the EventSender for each test. |
954 | m_eventSenderProxy = std::make_unique<EventSenderProxy>(this); |
955 | |
956 | // FIXME: Is this needed? Nothing in TestController changes preferences during tests, and if there is |
957 | // some other code doing this, it should probably be responsible for cleanup too. |
958 | resetPreferencesToConsistentValues(options); |
959 | |
960 | #if PLATFORM(GTK) |
961 | WKTextCheckerContinuousSpellCheckingEnabledStateChanged(true); |
962 | #endif |
963 | |
964 | // Make sure the view is in the window (a test can unparent it). |
965 | m_mainWebView->addToWindow(); |
966 | |
967 | // In the case that a test using the chrome input field failed, be sure to clean up for the next test. |
968 | m_mainWebView->removeChromeInputField(); |
969 | m_mainWebView->focus(); |
970 | |
971 | // Re-set to the default backing scale factor by setting the custom scale factor to 0. |
972 | WKPageSetCustomBackingScaleFactor(m_mainWebView->page(), 0); |
973 | |
974 | WKPageClearWheelEventTestTrigger(m_mainWebView->page()); |
975 | |
976 | WKPageSetMuted(m_mainWebView->page(), true); |
977 | |
978 | WKPageClearUserMediaState(m_mainWebView->page()); |
979 | |
980 | // Reset notification permissions |
981 | m_webNotificationProvider.reset(); |
982 | |
983 | // Reset Geolocation permissions. |
984 | m_geolocationPermissionRequests.clear(); |
985 | m_isGeolocationPermissionSet = false; |
986 | m_isGeolocationPermissionAllowed = false; |
987 | |
988 | // Reset UserMedia permissions. |
989 | m_userMediaPermissionRequests.clear(); |
990 | m_cachedUserMediaPermissions.clear(); |
991 | setUserMediaPermission(true); |
992 | |
993 | // Reset Custom Policy Delegate. |
994 | setCustomPolicyDelegate(false, false); |
995 | |
996 | // Reset Content Extensions. |
997 | resetContentExtensions(); |
998 | |
999 | m_shouldDownloadUndisplayableMIMETypes = false; |
1000 | |
1001 | m_shouldAllowDeviceOrientationAndMotionAccess = false; |
1002 | |
1003 | m_workQueueManager.clearWorkQueue(); |
1004 | |
1005 | m_rejectsProtectionSpaceAndContinueForAuthenticationChallenges = false; |
1006 | m_handlesAuthenticationChallenges = false; |
1007 | m_authenticationUsername = String(); |
1008 | m_authenticationPassword = String(); |
1009 | |
1010 | setBlockAllPlugins(false); |
1011 | setPluginSupportedMode({ }); |
1012 | |
1013 | m_shouldLogDownloadCallbacks = false; |
1014 | m_shouldLogHistoryClientCallbacks = false; |
1015 | m_shouldLogCanAuthenticateAgainstProtectionSpace = false; |
1016 | |
1017 | setHidden(false); |
1018 | |
1019 | platformResetStateToConsistentValues(options); |
1020 | |
1021 | m_shouldDecideNavigationPolicyAfterDelay = false; |
1022 | m_shouldDecideResponsePolicyAfterDelay = false; |
1023 | |
1024 | setNavigationGesturesEnabled(false); |
1025 | |
1026 | setIgnoresViewportScaleLimits(options.ignoresViewportScaleLimits); |
1027 | |
1028 | m_openPanelFileURLs = nullptr; |
1029 | #if PLATFORM(IOS_FAMILY) |
1030 | m_openPanelFileURLsMediaIcon = nullptr; |
1031 | #endif |
1032 | |
1033 | statisticsResetToConsistentState(); |
1034 | |
1035 | clearAdClickAttribution(); |
1036 | |
1037 | m_didReceiveServerRedirectForProvisionalNavigation = false; |
1038 | m_serverTrustEvaluationCallbackCallsCount = 0; |
1039 | m_shouldDismissJavaScriptAlertsAsynchronously = false; |
1040 | |
1041 | // Reset main page back to about:blank |
1042 | m_doneResetting = false; |
1043 | WKPageLoadURL(m_mainWebView->page(), blankURL()); |
1044 | runUntil(m_doneResetting, m_currentInvocation->shortTimeout()); |
1045 | if (!m_doneResetting) |
1046 | return false; |
1047 | |
1048 | if (resetStage == ResetStage::AfterTest) |
1049 | updateLiveDocumentsAfterTest(); |
1050 | |
1051 | return m_doneResetting; |
1052 | } |
1053 | |
1054 | void TestController::updateLiveDocumentsAfterTest() |
1055 | { |
1056 | if (!m_checkForWorldLeaks) |
1057 | return; |
1058 | |
1059 | AsyncTask([]() { |
1060 | // After each test, we update the list of live documents so that we can detect when an abandoned document first showed up. |
1061 | WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("GetLiveDocuments" )); |
1062 | WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), nullptr); |
1063 | }, 5_s).run(); |
1064 | } |
1065 | |
1066 | void TestController::checkForWorldLeaks() |
1067 | { |
1068 | if (!m_checkForWorldLeaks || !TestController::singleton().mainWebView()) |
1069 | return; |
1070 | |
1071 | AsyncTask([]() { |
1072 | // This runs at the end of a series of tests. It clears caches, runs a GC and then fetches the list of documents. |
1073 | WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CheckForWorldLeaks" )); |
1074 | WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), nullptr); |
1075 | }, 20_s).run(); |
1076 | } |
1077 | |
1078 | void TestController::dumpResponse(const String& result) |
1079 | { |
1080 | unsigned resultLength = result.length(); |
1081 | printf("Content-Type: text/plain\n" ); |
1082 | printf("Content-Length: %u\n" , resultLength); |
1083 | fwrite(result.utf8().data(), 1, resultLength, stdout); |
1084 | printf("#EOF\n" ); |
1085 | fprintf(stderr, "#EOF\n" ); |
1086 | fflush(stdout); |
1087 | fflush(stderr); |
1088 | } |
1089 | |
1090 | void TestController::findAndDumpWebKitProcessIdentifiers() |
1091 | { |
1092 | StringBuilder builder; |
1093 | |
1094 | #if PLATFORM(COCOA) |
1095 | builder.append(TestController::webProcessName()); |
1096 | builder.appendLiteral(": " ); |
1097 | pid_t webContentPID = WKPageGetProcessIdentifier(TestController::singleton().mainWebView()->page()); |
1098 | builder.appendNumber(webContentPID); |
1099 | builder.append('\n'); |
1100 | |
1101 | builder.append(TestController::networkProcessName()); |
1102 | builder.appendLiteral(": " ); |
1103 | pid_t networkingPID = WKContextGetNetworkProcessIdentifier(m_context.get()); |
1104 | builder.appendNumber(networkingPID); |
1105 | builder.append('\n'); |
1106 | #else |
1107 | builder.append('\n'); |
1108 | #endif |
1109 | |
1110 | dumpResponse(builder.toString()); |
1111 | } |
1112 | |
1113 | void TestController::findAndDumpWorldLeaks() |
1114 | { |
1115 | if (!m_checkForWorldLeaks) |
1116 | return; |
1117 | |
1118 | checkForWorldLeaks(); |
1119 | |
1120 | StringBuilder builder; |
1121 | |
1122 | if (m_abandonedDocumentInfo.size()) { |
1123 | for (const auto& it : m_abandonedDocumentInfo) { |
1124 | auto documentURL = it.value.abandonedDocumentURL; |
1125 | if (documentURL.isEmpty()) |
1126 | documentURL = "(no url)" ; |
1127 | builder.append("TEST: " ); |
1128 | builder.append(it.value.testURL); |
1129 | builder.append('\n'); |
1130 | builder.append("ABANDONED DOCUMENT: " ); |
1131 | builder.append(documentURL); |
1132 | builder.append('\n'); |
1133 | } |
1134 | } else |
1135 | builder.append("no abandoned documents\n" ); |
1136 | |
1137 | dumpResponse(builder.toString()); |
1138 | } |
1139 | |
1140 | void TestController::willDestroyWebView() |
1141 | { |
1142 | // Before we kill the web view, look for abandoned documents before that web process goes away. |
1143 | checkForWorldLeaks(); |
1144 | } |
1145 | |
1146 | void TestController::terminateWebContentProcess() |
1147 | { |
1148 | WKPageTerminate(m_mainWebView->page()); |
1149 | } |
1150 | |
1151 | void TestController::reattachPageToWebProcess() |
1152 | { |
1153 | // Loading a web page is the only way to reattach an existing page to a process. |
1154 | SetForScope<State> changeState(m_state, Resetting); |
1155 | m_doneResetting = false; |
1156 | WKPageLoadURL(m_mainWebView->page(), blankURL()); |
1157 | runUntil(m_doneResetting, noTimeout); |
1158 | } |
1159 | |
1160 | const char* TestController::webProcessName() |
1161 | { |
1162 | // FIXME: Find a way to not hardcode the process name. |
1163 | #if PLATFORM(IOS_FAMILY) && !PLATFORM(IOS_FAMILY_SIMULATOR) |
1164 | return "com.apple.WebKit.WebContent" ; |
1165 | #elif PLATFORM(COCOA) |
1166 | return "com.apple.WebKit.WebContent.Development" ; |
1167 | #elif PLATFORM(GTK) |
1168 | return "WebKitWebProcess" ; |
1169 | #elif PLATFORM(WPE) |
1170 | return "WPEWebProcess" ; |
1171 | #else |
1172 | return "WebProcess" ; |
1173 | #endif |
1174 | } |
1175 | |
1176 | const char* TestController::networkProcessName() |
1177 | { |
1178 | // FIXME: Find a way to not hardcode the process name. |
1179 | #if PLATFORM(IOS_FAMILY) && !PLATFORM(IOS_FAMILY_SIMULATOR) |
1180 | return "com.apple.WebKit.Networking" ; |
1181 | #elif PLATFORM(COCOA) |
1182 | return "com.apple.WebKit.Networking.Development" ; |
1183 | #elif PLATFORM(GTK) |
1184 | return "WebKitNetworkProcess" ; |
1185 | #elif PLATFORM(WPE) |
1186 | return "WPENetworkProcess" ; |
1187 | #else |
1188 | return "NetworkProcess" ; |
1189 | #endif |
1190 | } |
1191 | |
1192 | void TestController::setAllowsAnySSLCertificate(bool allows) |
1193 | { |
1194 | WKContextSetAllowsAnySSLCertificateForWebSocketTesting(platformContext(), allows); |
1195 | } |
1196 | |
1197 | static std::string testPath(WKURLRef url) |
1198 | { |
1199 | auto scheme = adoptWK(WKURLCopyScheme(url)); |
1200 | if (WKStringIsEqualToUTF8CStringIgnoringCase(scheme.get(), "file" )) { |
1201 | auto path = adoptWK(WKURLCopyPath(url)); |
1202 | auto buffer = std::vector<char>(WKStringGetMaximumUTF8CStringSize(path.get())); |
1203 | auto length = WKStringGetUTF8CString(path.get(), buffer.data(), buffer.size()); |
1204 | RELEASE_ASSERT(length > 0); |
1205 | #if OS(WINDOWS) |
1206 | // Remove the first '/' if it starts with something like "/C:/". |
1207 | if (length >= 4 && buffer[0] == '/' && buffer[2] == ':' && buffer[3] == '/') |
1208 | return std::string(buffer.data() + 1, length - 1); |
1209 | #endif |
1210 | return std::string(buffer.data(), length - 1); |
1211 | } |
1212 | return std::string(); |
1213 | } |
1214 | |
1215 | static WKURLRef createTestURL(const char* pathOrURL) |
1216 | { |
1217 | if (strstr(pathOrURL, "http://" ) || strstr(pathOrURL, "https://" ) || strstr(pathOrURL, "file://" )) |
1218 | return WKURLCreateWithUTF8CString(pathOrURL); |
1219 | |
1220 | // Creating from filesytem path. |
1221 | size_t length = strlen(pathOrURL); |
1222 | if (!length) |
1223 | return 0; |
1224 | |
1225 | #if PLATFORM(WIN) |
1226 | bool isAbsolutePath = false; |
1227 | if (strlen(pathOrURL) >= 3 && pathOrURL[1] == ':' && pathOrURL[2] == pathSeparator) |
1228 | isAbsolutePath = true; |
1229 | #else |
1230 | bool isAbsolutePath = pathOrURL[0] == pathSeparator; |
1231 | #endif |
1232 | const char* filePrefix = "file://" ; |
1233 | static const size_t prefixLength = strlen(filePrefix); |
1234 | |
1235 | std::unique_ptr<char[]> buffer; |
1236 | if (isAbsolutePath) { |
1237 | buffer = std::make_unique<char[]>(prefixLength + length + 1); |
1238 | strcpy(buffer.get(), filePrefix); |
1239 | strcpy(buffer.get() + prefixLength, pathOrURL); |
1240 | } else { |
1241 | buffer = std::make_unique<char[]>(prefixLength + PATH_MAX + length + 2); // 1 for the pathSeparator |
1242 | strcpy(buffer.get(), filePrefix); |
1243 | if (!getcwd(buffer.get() + prefixLength, PATH_MAX)) |
1244 | return 0; |
1245 | size_t numCharacters = strlen(buffer.get()); |
1246 | buffer[numCharacters] = pathSeparator; |
1247 | strcpy(buffer.get() + numCharacters + 1, pathOrURL); |
1248 | } |
1249 | |
1250 | return WKURLCreateWithUTF8CString(buffer.get()); |
1251 | } |
1252 | |
1253 | static bool (const std::string& value) |
1254 | { |
1255 | if (value == "true" ) |
1256 | return true; |
1257 | if (value == "false" ) |
1258 | return false; |
1259 | |
1260 | LOG_ERROR("Found unexpected value '%s' for boolean option. Expected 'true' or 'false'." , value.c_str()); |
1261 | return false; |
1262 | } |
1263 | |
1264 | static std::string (const std::string& value, const std::string& pathOrURL) |
1265 | { |
1266 | WKRetainPtr<WKURLRef> baseURL = adoptWK(createTestURL(pathOrURL.c_str())); |
1267 | WKRetainPtr<WKURLRef> relativeURL = adoptWK(WKURLCreateWithBaseURL(baseURL.get(), value.c_str())); |
1268 | return toSTD(adoptWK(WKURLCopyPath(relativeURL.get()))); |
1269 | } |
1270 | |
1271 | static void (TestOptions& testOptions, const std::string& pathOrURL, const std::string& absolutePath) |
1272 | { |
1273 | std::string filename = absolutePath; |
1274 | if (filename.empty()) { |
1275 | // Gross. Need to reduce conversions between all the string types and URLs. |
1276 | WKRetainPtr<WKURLRef> wkURL = adoptWK(createTestURL(pathOrURL.c_str())); |
1277 | filename = testPath(wkURL.get()); |
1278 | } |
1279 | |
1280 | if (filename.empty()) |
1281 | return; |
1282 | |
1283 | std::string options; |
1284 | std::ifstream testFile(filename.data()); |
1285 | if (!testFile.good()) |
1286 | return; |
1287 | getline(testFile, options); |
1288 | std::string beginString("webkit-test-runner [ " ); |
1289 | std::string endString(" ]" ); |
1290 | size_t beginLocation = options.find(beginString); |
1291 | if (beginLocation == std::string::npos) |
1292 | return; |
1293 | size_t endLocation = options.find(endString, beginLocation); |
1294 | if (endLocation == std::string::npos) { |
1295 | LOG_ERROR("Could not find end of test header in %s" , filename.c_str()); |
1296 | return; |
1297 | } |
1298 | std::string pairString = options.substr(beginLocation + beginString.size(), endLocation - (beginLocation + beginString.size())); |
1299 | size_t pairStart = 0; |
1300 | while (pairStart < pairString.size()) { |
1301 | size_t pairEnd = pairString.find(" " , pairStart); |
1302 | if (pairEnd == std::string::npos) |
1303 | pairEnd = pairString.size(); |
1304 | size_t equalsLocation = pairString.find("=" , pairStart); |
1305 | if (equalsLocation == std::string::npos) { |
1306 | LOG_ERROR("Malformed option in test header (could not find '=' character) in %s" , filename.c_str()); |
1307 | break; |
1308 | } |
1309 | auto key = pairString.substr(pairStart, equalsLocation - pairStart); |
1310 | auto value = pairString.substr(equalsLocation + 1, pairEnd - (equalsLocation + 1)); |
1311 | |
1312 | if (!key.rfind("experimental:" )) { |
1313 | key = key.substr(13); |
1314 | testOptions.experimentalFeatures.add(String(key.c_str()), parseBooleanTestHeaderValue(value)); |
1315 | } |
1316 | |
1317 | if (!key.rfind("internal:" )) { |
1318 | key = key.substr(9); |
1319 | testOptions.internalDebugFeatures.add(String(key.c_str()), parseBooleanTestHeaderValue(value)); |
1320 | } |
1321 | |
1322 | if (key == "language" ) |
1323 | testOptions.contextOptions.overrideLanguages = String(value.c_str()).split(','); |
1324 | else if (key == "useThreadedScrolling" ) |
1325 | testOptions.useThreadedScrolling = parseBooleanTestHeaderValue(value); |
1326 | else if (key == "useAcceleratedDrawing" ) |
1327 | testOptions.useAcceleratedDrawing = parseBooleanTestHeaderValue(value); |
1328 | else if (key == "useFlexibleViewport" ) |
1329 | testOptions.useFlexibleViewport = parseBooleanTestHeaderValue(value); |
1330 | else if (key == "useDataDetection" ) |
1331 | testOptions.useDataDetection = parseBooleanTestHeaderValue(value); |
1332 | else if (key == "useMockScrollbars" ) |
1333 | testOptions.useMockScrollbars = parseBooleanTestHeaderValue(value); |
1334 | else if (key == "needsSiteSpecificQuirks" ) |
1335 | testOptions.needsSiteSpecificQuirks = parseBooleanTestHeaderValue(value); |
1336 | else if (key == "ignoresViewportScaleLimits" ) |
1337 | testOptions.ignoresViewportScaleLimits = parseBooleanTestHeaderValue(value); |
1338 | else if (key == "useCharacterSelectionGranularity" ) |
1339 | testOptions.useCharacterSelectionGranularity = parseBooleanTestHeaderValue(value); |
1340 | else if (key == "enableAttachmentElement" ) |
1341 | testOptions.enableAttachmentElement = parseBooleanTestHeaderValue(value); |
1342 | else if (key == "enableIntersectionObserver" ) |
1343 | testOptions.enableIntersectionObserver = parseBooleanTestHeaderValue(value); |
1344 | else if (key == "enableMenuItemElement" ) |
1345 | testOptions.enableMenuItemElement = parseBooleanTestHeaderValue(value); |
1346 | else if (key == "enableModernMediaControls" ) |
1347 | testOptions.enableModernMediaControls = parseBooleanTestHeaderValue(value); |
1348 | else if (key == "enablePointerLock" ) |
1349 | testOptions.enablePointerLock = parseBooleanTestHeaderValue(value); |
1350 | else if (key == "enableWebAuthentication" ) |
1351 | testOptions.enableWebAuthentication = parseBooleanTestHeaderValue(value); |
1352 | else if (key == "enableWebAuthenticationLocalAuthenticator" ) |
1353 | testOptions.enableWebAuthenticationLocalAuthenticator = parseBooleanTestHeaderValue(value); |
1354 | else if (key == "enableIsSecureContextAttribute" ) |
1355 | testOptions.enableIsSecureContextAttribute = parseBooleanTestHeaderValue(value); |
1356 | else if (key == "enableInspectorAdditions" ) |
1357 | testOptions.enableInspectorAdditions = parseBooleanTestHeaderValue(value); |
1358 | else if (key == "dumpJSConsoleLogInStdErr" ) |
1359 | testOptions.dumpJSConsoleLogInStdErr = parseBooleanTestHeaderValue(value); |
1360 | else if (key == "applicationManifest" ) |
1361 | testOptions.applicationManifest = parseStringTestHeaderValueAsRelativePath(value, pathOrURL); |
1362 | else if (key == "allowCrossOriginSubresourcesToAskForCredentials" ) |
1363 | testOptions.allowCrossOriginSubresourcesToAskForCredentials = parseBooleanTestHeaderValue(value); |
1364 | else if (key == "domPasteAllowed" ) |
1365 | testOptions.domPasteAllowed = parseBooleanTestHeaderValue(value); |
1366 | else if (key == "enableProcessSwapOnNavigation" ) |
1367 | testOptions.contextOptions.enableProcessSwapOnNavigation = parseBooleanTestHeaderValue(value); |
1368 | else if (key == "enableProcessSwapOnWindowOpen" ) |
1369 | testOptions.contextOptions.enableProcessSwapOnWindowOpen = parseBooleanTestHeaderValue(value); |
1370 | else if (key == "enableColorFilter" ) |
1371 | testOptions.enableColorFilter = parseBooleanTestHeaderValue(value); |
1372 | else if (key == "punchOutWhiteBackgroundsInDarkMode" ) |
1373 | testOptions.punchOutWhiteBackgroundsInDarkMode = parseBooleanTestHeaderValue(value); |
1374 | else if (key == "jscOptions" ) |
1375 | testOptions.jscOptions = value; |
1376 | else if (key == "additionalSupportedImageTypes" ) |
1377 | testOptions.additionalSupportedImageTypes = value; |
1378 | else if (key == "runSingly" ) |
1379 | testOptions.runSingly = parseBooleanTestHeaderValue(value); |
1380 | else if (key == "shouldIgnoreMetaViewport" ) |
1381 | testOptions.shouldIgnoreMetaViewport = parseBooleanTestHeaderValue(value); |
1382 | else if (key == "spellCheckingDots" ) |
1383 | testOptions.shouldShowSpellCheckingDots = parseBooleanTestHeaderValue(value); |
1384 | else if (key == "enableEditableImages" ) |
1385 | testOptions.enableEditableImages = parseBooleanTestHeaderValue(value); |
1386 | else if (key == "editable" ) |
1387 | testOptions.editable = parseBooleanTestHeaderValue(value); |
1388 | else if (key == "enableUndoManagerAPI" ) |
1389 | testOptions.enableUndoManagerAPI = parseBooleanTestHeaderValue(value); |
1390 | else if (key == "shouldHandleRunOpenPanel" ) |
1391 | testOptions.shouldHandleRunOpenPanel = parseBooleanTestHeaderValue(value); |
1392 | else if (key == "shouldPresentPopovers" ) |
1393 | testOptions.shouldPresentPopovers = parseBooleanTestHeaderValue(value); |
1394 | else if (key == "contentInset.top" ) |
1395 | testOptions.contentInsetTop = std::stod(value); |
1396 | else if (key == "ignoreSynchronousMessagingTimeouts" ) |
1397 | testOptions.contextOptions.ignoreSynchronousMessagingTimeouts = parseBooleanTestHeaderValue(value); |
1398 | else if (key == "shouldUseModernCompatibilityMode" ) |
1399 | testOptions.shouldUseModernCompatibilityMode = parseBooleanTestHeaderValue(value); |
1400 | else if (key == "enableAppNap" ) |
1401 | testOptions.enableAppNap = parseBooleanTestHeaderValue(value); |
1402 | pairStart = pairEnd + 1; |
1403 | } |
1404 | } |
1405 | |
1406 | TestOptions TestController::testOptionsForTest(const TestCommand& command) const |
1407 | { |
1408 | TestOptions options(command.pathOrURL); |
1409 | |
1410 | options.useRemoteLayerTree = m_shouldUseRemoteLayerTree; |
1411 | options.shouldShowWebView = m_shouldShowWebView; |
1412 | |
1413 | updatePlatformSpecificTestOptionsForTest(options, command.pathOrURL); |
1414 | updateTestOptionsFromTestHeader(options, command.pathOrURL, command.absolutePath); |
1415 | platformAddTestOptions(options); |
1416 | |
1417 | return options; |
1418 | } |
1419 | |
1420 | void TestController::updateWebViewSizeForTest(const TestInvocation& test) |
1421 | { |
1422 | unsigned width = viewWidth; |
1423 | unsigned height = viewHeight; |
1424 | if (test.options().isSVGTest) { |
1425 | width = w3cSVGViewWidth; |
1426 | height = w3cSVGViewHeight; |
1427 | } |
1428 | |
1429 | mainWebView()->resizeTo(width, height); |
1430 | } |
1431 | |
1432 | void TestController::updateWindowScaleForTest(PlatformWebView* view, const TestInvocation& test) |
1433 | { |
1434 | view->changeWindowScaleIfNeeded(test.options().deviceScaleFactor); |
1435 | } |
1436 | |
1437 | void TestController::configureViewForTest(const TestInvocation& test) |
1438 | { |
1439 | ensureViewSupportsOptionsForTest(test); |
1440 | updateWebViewSizeForTest(test); |
1441 | updateWindowScaleForTest(mainWebView(), test); |
1442 | configureContentExtensionForTest(test); |
1443 | platformConfigureViewForTest(test); |
1444 | } |
1445 | |
1446 | #if ENABLE(CONTENT_EXTENSIONS) && !PLATFORM(COCOA) |
1447 | struct ContentExtensionStoreCallbackContext { |
1448 | explicit ContentExtensionStoreCallbackContext(TestController& controller) |
1449 | : testController(controller) |
1450 | { |
1451 | } |
1452 | |
1453 | TestController& testController; |
1454 | uint32_t status { kWKUserContentExtensionStoreSuccess }; |
1455 | WKRetainPtr<WKUserContentFilterRef> filter; |
1456 | bool done { false }; |
1457 | }; |
1458 | |
1459 | static void contentExtensionStoreCallback(WKUserContentFilterRef filter, uint32_t status, void* userData) |
1460 | { |
1461 | auto* context = static_cast<ContentExtensionStoreCallbackContext*>(userData); |
1462 | context->status = status; |
1463 | context->filter = filter ? adoptWK(filter) : nullptr; |
1464 | context->done = true; |
1465 | context->testController.notifyDone(); |
1466 | } |
1467 | |
1468 | static std::string contentExtensionJSONPath(WKURLRef url) |
1469 | { |
1470 | auto path = testPath(url); |
1471 | if (path.length()) |
1472 | return path + ".json" ; |
1473 | |
1474 | auto p = adoptWK(WKURLCopyPath(url)); |
1475 | auto buffer = std::vector<char>(WKStringGetMaximumUTF8CStringSize(p.get())); |
1476 | const auto length = WKStringGetUTF8CString(p.get(), buffer.data(), buffer.size()); |
1477 | return std::string("LayoutTests/http/tests" ) + std::string(buffer.data(), length - 1) + ".json" ; |
1478 | } |
1479 | #endif |
1480 | |
1481 | #if !PLATFORM(COCOA) |
1482 | #if ENABLE(CONTENT_EXTENSIONS) |
1483 | void TestController::configureContentExtensionForTest(const TestInvocation& test) |
1484 | { |
1485 | const char* contentExtensionsPath = libraryPathForTesting(); |
1486 | if (!contentExtensionsPath) |
1487 | contentExtensionsPath = "/tmp/wktr-contentextensions" ; |
1488 | |
1489 | if (!test.urlContains("contentextensions/" )) { |
1490 | WKPageSetUserContentExtensionsEnabled(m_mainWebView->page(), false); |
1491 | return; |
1492 | } |
1493 | |
1494 | std::string jsonFilePath(contentExtensionJSONPath(test.url())); |
1495 | std::ifstream jsonFile(jsonFilePath); |
1496 | if (!jsonFile.good()) { |
1497 | WTFLogAlways("Could not open file '%s'" , jsonFilePath.c_str()); |
1498 | return; |
1499 | } |
1500 | |
1501 | std::string jsonFileContents {std::istreambuf_iterator<char>(jsonFile), std::istreambuf_iterator<char>()}; |
1502 | auto jsonSource = adoptWK(WKStringCreateWithUTF8CString(jsonFileContents.c_str())); |
1503 | |
1504 | auto storePath = adoptWK(WKStringCreateWithUTF8CString(contentExtensionsPath)); |
1505 | auto extensionStore = adoptWK(WKUserContentExtensionStoreCreate(storePath.get())); |
1506 | ASSERT(extensionStore); |
1507 | |
1508 | auto filterIdentifier = adoptWK(WKStringCreateWithUTF8CString("TestContentExtension" )); |
1509 | |
1510 | ContentExtensionStoreCallbackContext context(*this); |
1511 | WKUserContentExtensionStoreCompile(extensionStore.get(), filterIdentifier.get(), jsonSource.get(), &context, contentExtensionStoreCallback); |
1512 | runUntil(context.done, noTimeout); |
1513 | ASSERT(context.status == kWKUserContentExtensionStoreSuccess); |
1514 | ASSERT(context.filter); |
1515 | |
1516 | WKPageSetUserContentExtensionsEnabled(mainWebView()->page(), true); |
1517 | WKUserContentControllerAddUserContentFilter(userContentController(), context.filter.get()); |
1518 | } |
1519 | |
1520 | void TestController::resetContentExtensions() |
1521 | { |
1522 | if (!mainWebView()) |
1523 | return; |
1524 | |
1525 | WKPageSetUserContentExtensionsEnabled(mainWebView()->page(), false); |
1526 | |
1527 | const char* contentExtensionsPath = libraryPathForTesting(); |
1528 | if (!contentExtensionsPath) |
1529 | return; |
1530 | |
1531 | WKUserContentControllerRemoveAllUserContentFilters(userContentController()); |
1532 | |
1533 | auto storePath = adoptWK(WKStringCreateWithUTF8CString(contentExtensionsPath)); |
1534 | auto extensionStore = adoptWK(WKUserContentExtensionStoreCreate(storePath.get())); |
1535 | ASSERT(extensionStore); |
1536 | |
1537 | auto filterIdentifier = adoptWK(WKStringCreateWithUTF8CString("TestContentExtension" )); |
1538 | |
1539 | ContentExtensionStoreCallbackContext context(*this); |
1540 | WKUserContentExtensionStoreRemove(extensionStore.get(), filterIdentifier.get(), &context, contentExtensionStoreCallback); |
1541 | runUntil(context.done, noTimeout); |
1542 | ASSERT(!context.filter); |
1543 | } |
1544 | #else // ENABLE(CONTENT_EXTENSIONS) |
1545 | void TestController::configureContentExtensionForTest(const TestInvocation&) |
1546 | { |
1547 | } |
1548 | |
1549 | void TestController::resetContentExtensions() |
1550 | { |
1551 | } |
1552 | #endif // ENABLE(CONTENT_EXTENSIONS) |
1553 | #endif // !PLATFORM(COCOA) |
1554 | |
1555 | class CommandTokenizer { |
1556 | public: |
1557 | explicit CommandTokenizer(const std::string& input) |
1558 | : m_input(input) |
1559 | , m_posNextSeparator(0) |
1560 | { |
1561 | pump(); |
1562 | } |
1563 | |
1564 | bool hasNext() const; |
1565 | std::string next(); |
1566 | |
1567 | private: |
1568 | void pump(); |
1569 | static const char kSeparator = '\''; |
1570 | const std::string& m_input; |
1571 | std::string m_next; |
1572 | size_t m_posNextSeparator; |
1573 | }; |
1574 | |
1575 | void CommandTokenizer::pump() |
1576 | { |
1577 | if (m_posNextSeparator == std::string::npos || m_posNextSeparator == m_input.size()) { |
1578 | m_next = std::string(); |
1579 | return; |
1580 | } |
1581 | size_t start = m_posNextSeparator ? m_posNextSeparator + 1 : 0; |
1582 | m_posNextSeparator = m_input.find(kSeparator, start); |
1583 | size_t size = m_posNextSeparator == std::string::npos ? std::string::npos : m_posNextSeparator - start; |
1584 | m_next = std::string(m_input, start, size); |
1585 | } |
1586 | |
1587 | std::string CommandTokenizer::next() |
1588 | { |
1589 | ASSERT(hasNext()); |
1590 | |
1591 | std::string oldNext = m_next; |
1592 | pump(); |
1593 | return oldNext; |
1594 | } |
1595 | |
1596 | bool CommandTokenizer::hasNext() const |
1597 | { |
1598 | return !m_next.empty(); |
1599 | } |
1600 | |
1601 | NO_RETURN static void die(const std::string& inputLine) |
1602 | { |
1603 | fprintf(stderr, "Unexpected input line: %s\n" , inputLine.c_str()); |
1604 | exit(1); |
1605 | } |
1606 | |
1607 | static TestCommand parseInputLine(const std::string& inputLine) |
1608 | { |
1609 | TestCommand result; |
1610 | CommandTokenizer tokenizer(inputLine); |
1611 | if (!tokenizer.hasNext()) |
1612 | die(inputLine); |
1613 | |
1614 | std::string arg = tokenizer.next(); |
1615 | result.pathOrURL = arg; |
1616 | while (tokenizer.hasNext()) { |
1617 | arg = tokenizer.next(); |
1618 | if (arg == std::string("--timeout" )) { |
1619 | std::string timeoutToken = tokenizer.next(); |
1620 | result.timeout = Seconds::fromMilliseconds(atoi(timeoutToken.c_str())); |
1621 | } else if (arg == std::string("-p" ) || arg == std::string("--pixel-test" )) { |
1622 | result.shouldDumpPixels = true; |
1623 | if (tokenizer.hasNext()) |
1624 | result.expectedPixelHash = tokenizer.next(); |
1625 | } else if (arg == std::string("--dump-jsconsolelog-in-stderr" )) |
1626 | result.dumpJSConsoleLogInStdErr = true; |
1627 | else if (arg == std::string("--absolutePath" )) |
1628 | result.absolutePath = tokenizer.next(); |
1629 | else |
1630 | die(inputLine); |
1631 | } |
1632 | return result; |
1633 | } |
1634 | |
1635 | bool TestController::runTest(const char* inputLine) |
1636 | { |
1637 | AutodrainedPool pool; |
1638 | |
1639 | WKTextCheckerSetTestingMode(true); |
1640 | TestCommand command = parseInputLine(std::string(inputLine)); |
1641 | |
1642 | m_state = RunningTest; |
1643 | |
1644 | TestOptions options = testOptionsForTest(command); |
1645 | |
1646 | WKRetainPtr<WKURLRef> wkURL = adoptWK(createTestURL(command.pathOrURL.c_str())); |
1647 | m_currentInvocation = std::make_unique<TestInvocation>(wkURL.get(), options); |
1648 | |
1649 | if (command.shouldDumpPixels || m_shouldDumpPixelsForAllTests) |
1650 | m_currentInvocation->setIsPixelTest(command.expectedPixelHash); |
1651 | |
1652 | if (command.timeout > 0_s) |
1653 | m_currentInvocation->setCustomTimeout(command.timeout); |
1654 | |
1655 | m_currentInvocation->setDumpJSConsoleLogInStdErr(command.dumpJSConsoleLogInStdErr || options.dumpJSConsoleLogInStdErr); |
1656 | |
1657 | platformWillRunTest(*m_currentInvocation); |
1658 | |
1659 | m_currentInvocation->invoke(); |
1660 | m_currentInvocation = nullptr; |
1661 | |
1662 | return true; |
1663 | } |
1664 | |
1665 | bool TestController::waitForCompletion(const WTF::Function<void ()>& function, WTF::Seconds timeout) |
1666 | { |
1667 | m_doneResetting = false; |
1668 | function(); |
1669 | runUntil(m_doneResetting, timeout); |
1670 | return !m_doneResetting; |
1671 | } |
1672 | |
1673 | bool TestController::handleControlCommand(const char* command) |
1674 | { |
1675 | if (!strncmp("#CHECK FOR WORLD LEAKS" , command, 22)) { |
1676 | if (m_checkForWorldLeaks) |
1677 | findAndDumpWorldLeaks(); |
1678 | else |
1679 | WTFLogAlways("WebKitTestRunner asked to check for world leaks, but was not run with --world-leaks" ); |
1680 | return true; |
1681 | } |
1682 | |
1683 | if (!strncmp("#LIST CHILD PROCESSES" , command, 21)) { |
1684 | findAndDumpWebKitProcessIdentifiers(); |
1685 | return true; |
1686 | } |
1687 | |
1688 | return false; |
1689 | } |
1690 | |
1691 | void TestController::runTestingServerLoop() |
1692 | { |
1693 | char filenameBuffer[2048]; |
1694 | while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) { |
1695 | char* newLineCharacter = strchr(filenameBuffer, '\n'); |
1696 | if (newLineCharacter) |
1697 | *newLineCharacter = '\0'; |
1698 | |
1699 | if (strlen(filenameBuffer) == 0) |
1700 | continue; |
1701 | |
1702 | if (handleControlCommand(filenameBuffer)) |
1703 | continue; |
1704 | |
1705 | if (!runTest(filenameBuffer)) |
1706 | break; |
1707 | } |
1708 | } |
1709 | |
1710 | void TestController::run() |
1711 | { |
1712 | if (m_usingServerMode) |
1713 | runTestingServerLoop(); |
1714 | else { |
1715 | for (size_t i = 0; i < m_paths.size(); ++i) { |
1716 | if (!runTest(m_paths[i].c_str())) |
1717 | break; |
1718 | } |
1719 | if (m_checkForWorldLeaks) |
1720 | findAndDumpWorldLeaks(); |
1721 | } |
1722 | } |
1723 | |
1724 | void TestController::runUntil(bool& done, WTF::Seconds timeout) |
1725 | { |
1726 | if (m_forceNoTimeout) |
1727 | timeout = noTimeout; |
1728 | |
1729 | platformRunUntil(done, timeout); |
1730 | } |
1731 | |
1732 | // WKContextInjectedBundleClient |
1733 | |
1734 | void TestController::didReceiveMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo) |
1735 | { |
1736 | static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveMessageFromInjectedBundle(messageName, messageBody); |
1737 | } |
1738 | |
1739 | void TestController::didReceiveSynchronousMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, WKTypeRef* returnData, const void* clientInfo) |
1740 | { |
1741 | *returnData = static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody).leakRef(); |
1742 | } |
1743 | |
1744 | WKTypeRef TestController::getInjectedBundleInitializationUserData(WKContextRef, const void* clientInfo) |
1745 | { |
1746 | return static_cast<TestController*>(const_cast<void*>(clientInfo))->getInjectedBundleInitializationUserData().leakRef(); |
1747 | } |
1748 | |
1749 | // WKPageInjectedBundleClient |
1750 | |
1751 | void TestController::didReceivePageMessageFromInjectedBundle(WKPageRef page, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo) |
1752 | { |
1753 | static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveMessageFromInjectedBundle(messageName, messageBody); |
1754 | } |
1755 | |
1756 | void TestController::didReceiveSynchronousPageMessageFromInjectedBundle(WKPageRef page, WKStringRef messageName, WKTypeRef messageBody, WKTypeRef* returnData, const void* clientInfo) |
1757 | { |
1758 | *returnData = static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody).leakRef(); |
1759 | } |
1760 | |
1761 | void TestController::networkProcessDidCrash(WKContextRef context, const void *clientInfo) |
1762 | { |
1763 | static_cast<TestController*>(const_cast<void*>(clientInfo))->networkProcessDidCrash(); |
1764 | } |
1765 | |
1766 | void TestController::didReceiveKeyDownMessageFromInjectedBundle(WKDictionaryRef messageBodyDictionary, bool synchronous) |
1767 | { |
1768 | WKRetainPtr<WKStringRef> keyKey = adoptWK(WKStringCreateWithUTF8CString("Key" )); |
1769 | WKStringRef key = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, keyKey.get())); |
1770 | |
1771 | WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers" )); |
1772 | WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get())))); |
1773 | |
1774 | WKRetainPtr<WKStringRef> locationKey = adoptWK(WKStringCreateWithUTF8CString("Location" )); |
1775 | unsigned location = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, locationKey.get())))); |
1776 | |
1777 | m_eventSenderProxy->keyDown(key, modifiers, location); |
1778 | } |
1779 | |
1780 | void TestController::didReceiveLiveDocumentsList(WKArrayRef liveDocumentList) |
1781 | { |
1782 | auto numDocuments = WKArrayGetSize(liveDocumentList); |
1783 | |
1784 | HashMap<uint64_t, String> documentInfo; |
1785 | for (size_t i = 0; i < numDocuments; ++i) { |
1786 | WKTypeRef item = WKArrayGetItemAtIndex(liveDocumentList, i); |
1787 | if (item && WKGetTypeID(item) == WKDictionaryGetTypeID()) { |
1788 | WKDictionaryRef liveDocumentItem = static_cast<WKDictionaryRef>(item); |
1789 | |
1790 | WKRetainPtr<WKStringRef> idKey = adoptWK(WKStringCreateWithUTF8CString("id" )); |
1791 | WKUInt64Ref documentID = static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(liveDocumentItem, idKey.get())); |
1792 | |
1793 | WKRetainPtr<WKStringRef> urlKey = adoptWK(WKStringCreateWithUTF8CString("url" )); |
1794 | WKStringRef documentURL = static_cast<WKStringRef>(WKDictionaryGetItemForKey(liveDocumentItem, urlKey.get())); |
1795 | |
1796 | documentInfo.add(WKUInt64GetValue(documentID), toWTFString(documentURL)); |
1797 | } |
1798 | } |
1799 | |
1800 | if (!documentInfo.size()) { |
1801 | m_abandonedDocumentInfo.clear(); |
1802 | return; |
1803 | } |
1804 | |
1805 | // Remove any documents which are no longer live. |
1806 | m_abandonedDocumentInfo.removeIf([&](auto& keyAndValue) { |
1807 | return !documentInfo.contains(keyAndValue.key); |
1808 | }); |
1809 | |
1810 | // Add newly abandoned documents. |
1811 | String currentTestURL = m_currentInvocation ? toWTFString(adoptWK(WKURLCopyString(m_currentInvocation->url()))) : "no test" ; |
1812 | for (const auto& it : documentInfo) |
1813 | m_abandonedDocumentInfo.add(it.key, AbandonedDocumentInfo(currentTestURL, it.value)); |
1814 | } |
1815 | |
1816 | void TestController::didReceiveMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody) |
1817 | { |
1818 | if (WKStringIsEqualToUTF8CString(messageName, "LiveDocuments" )) { |
1819 | ASSERT(WKGetTypeID(messageBody) == WKArrayGetTypeID()); |
1820 | didReceiveLiveDocumentsList(static_cast<WKArrayRef>(messageBody)); |
1821 | AsyncTask::currentTask()->taskComplete(); |
1822 | return; |
1823 | } |
1824 | |
1825 | if (WKStringIsEqualToUTF8CString(messageName, "EventSender" )) { |
1826 | if (m_state != RunningTest) |
1827 | return; |
1828 | |
1829 | ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID()); |
1830 | WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody); |
1831 | |
1832 | WKRetainPtr<WKStringRef> subMessageKey = adoptWK(WKStringCreateWithUTF8CString("SubMessage" )); |
1833 | WKStringRef subMessageName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, subMessageKey.get())); |
1834 | |
1835 | if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown" ) || WKStringIsEqualToUTF8CString(subMessageName, "MouseUp" )) { |
1836 | WKRetainPtr<WKStringRef> buttonKey = adoptWK(WKStringCreateWithUTF8CString("Button" )); |
1837 | unsigned button = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, buttonKey.get())))); |
1838 | |
1839 | WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers" )); |
1840 | WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get())))); |
1841 | |
1842 | // Forward to WebProcess |
1843 | if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown" )) |
1844 | m_eventSenderProxy->mouseDown(button, modifiers); |
1845 | else |
1846 | m_eventSenderProxy->mouseUp(button, modifiers); |
1847 | |
1848 | return; |
1849 | } |
1850 | |
1851 | if (WKStringIsEqualToUTF8CString(subMessageName, "KeyDown" )) { |
1852 | didReceiveKeyDownMessageFromInjectedBundle(messageBodyDictionary, false); |
1853 | return; |
1854 | } |
1855 | |
1856 | if (WKStringIsEqualToUTF8CString(subMessageName, "MouseScrollBy" )) { |
1857 | WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X" )); |
1858 | double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))); |
1859 | |
1860 | WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y" )); |
1861 | double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))); |
1862 | |
1863 | // Forward to WebProcess |
1864 | m_eventSenderProxy->mouseScrollBy(x, y); |
1865 | return; |
1866 | } |
1867 | |
1868 | if (WKStringIsEqualToUTF8CString(subMessageName, "MouseScrollByWithWheelAndMomentumPhases" )) { |
1869 | WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X" )); |
1870 | double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))); |
1871 | |
1872 | WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y" )); |
1873 | double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))); |
1874 | |
1875 | WKRetainPtr<WKStringRef> phaseKey = adoptWK(WKStringCreateWithUTF8CString("Phase" )); |
1876 | int phase = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, phaseKey.get())))); |
1877 | WKRetainPtr<WKStringRef> momentumKey = adoptWK(WKStringCreateWithUTF8CString("Momentum" )); |
1878 | int momentum = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, momentumKey.get())))); |
1879 | |
1880 | // Forward to WebProcess |
1881 | m_eventSenderProxy->mouseScrollByWithWheelAndMomentumPhases(x, y, phase, momentum); |
1882 | |
1883 | return; |
1884 | } |
1885 | |
1886 | ASSERT_NOT_REACHED(); |
1887 | } |
1888 | |
1889 | if (!m_currentInvocation) |
1890 | return; |
1891 | |
1892 | m_currentInvocation->didReceiveMessageFromInjectedBundle(messageName, messageBody); |
1893 | } |
1894 | |
1895 | WKRetainPtr<WKTypeRef> TestController::didReceiveSynchronousMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody) |
1896 | { |
1897 | if (WKStringIsEqualToUTF8CString(messageName, "EventSender" )) { |
1898 | if (m_state != RunningTest) |
1899 | return nullptr; |
1900 | |
1901 | ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID()); |
1902 | WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody); |
1903 | |
1904 | WKRetainPtr<WKStringRef> subMessageKey = adoptWK(WKStringCreateWithUTF8CString("SubMessage" )); |
1905 | WKStringRef subMessageName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, subMessageKey.get())); |
1906 | |
1907 | if (WKStringIsEqualToUTF8CString(subMessageName, "KeyDown" )) { |
1908 | didReceiveKeyDownMessageFromInjectedBundle(messageBodyDictionary, true); |
1909 | |
1910 | return 0; |
1911 | } |
1912 | |
1913 | if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown" ) || WKStringIsEqualToUTF8CString(subMessageName, "MouseUp" )) { |
1914 | WKRetainPtr<WKStringRef> buttonKey = adoptWK(WKStringCreateWithUTF8CString("Button" )); |
1915 | unsigned button = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, buttonKey.get())))); |
1916 | |
1917 | WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers" )); |
1918 | WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get())))); |
1919 | |
1920 | // Forward to WebProcess |
1921 | if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown" )) |
1922 | m_eventSenderProxy->mouseDown(button, modifiers); |
1923 | else |
1924 | m_eventSenderProxy->mouseUp(button, modifiers); |
1925 | return 0; |
1926 | } |
1927 | |
1928 | if (WKStringIsEqualToUTF8CString(subMessageName, "MouseMoveTo" )) { |
1929 | WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X" )); |
1930 | double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))); |
1931 | |
1932 | WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y" )); |
1933 | double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))); |
1934 | |
1935 | // Forward to WebProcess |
1936 | m_eventSenderProxy->mouseMoveTo(x, y); |
1937 | return 0; |
1938 | } |
1939 | |
1940 | #if PLATFORM(MAC) |
1941 | if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceClick" )) { |
1942 | m_eventSenderProxy->mouseForceClick(); |
1943 | return 0; |
1944 | } |
1945 | |
1946 | if (WKStringIsEqualToUTF8CString(subMessageName, "StartAndCancelMouseForceClick" )) { |
1947 | m_eventSenderProxy->startAndCancelMouseForceClick(); |
1948 | return 0; |
1949 | } |
1950 | |
1951 | if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceDown" )) { |
1952 | m_eventSenderProxy->mouseForceDown(); |
1953 | return 0; |
1954 | } |
1955 | |
1956 | if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceUp" )) { |
1957 | m_eventSenderProxy->mouseForceUp(); |
1958 | return 0; |
1959 | } |
1960 | |
1961 | if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceChanged" )) { |
1962 | WKRetainPtr<WKStringRef> forceKey = adoptWK(WKStringCreateWithUTF8CString("Force" )); |
1963 | double force = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, forceKey.get()))); |
1964 | |
1965 | m_eventSenderProxy->mouseForceChanged(force); |
1966 | return 0; |
1967 | } |
1968 | #endif // PLATFORM(MAC) |
1969 | |
1970 | if (WKStringIsEqualToUTF8CString(subMessageName, "ContinuousMouseScrollBy" )) { |
1971 | WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X" )); |
1972 | double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))); |
1973 | |
1974 | WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y" )); |
1975 | double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))); |
1976 | |
1977 | WKRetainPtr<WKStringRef> pagedKey = adoptWK(WKStringCreateWithUTF8CString("Paged" )); |
1978 | bool paged = static_cast<bool>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, pagedKey.get())))); |
1979 | |
1980 | // Forward to WebProcess |
1981 | m_eventSenderProxy->continuousMouseScrollBy(x, y, paged); |
1982 | return 0; |
1983 | } |
1984 | |
1985 | if (WKStringIsEqualToUTF8CString(subMessageName, "LeapForward" )) { |
1986 | WKRetainPtr<WKStringRef> timeKey = adoptWK(WKStringCreateWithUTF8CString("TimeInMilliseconds" )); |
1987 | unsigned time = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, timeKey.get())))); |
1988 | |
1989 | m_eventSenderProxy->leapForward(time); |
1990 | return 0; |
1991 | } |
1992 | |
1993 | #if ENABLE(TOUCH_EVENTS) |
1994 | if (WKStringIsEqualToUTF8CString(subMessageName, "AddTouchPoint" )) { |
1995 | WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X" )); |
1996 | int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())))); |
1997 | |
1998 | WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y" )); |
1999 | int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())))); |
2000 | |
2001 | m_eventSenderProxy->addTouchPoint(x, y); |
2002 | return 0; |
2003 | } |
2004 | |
2005 | if (WKStringIsEqualToUTF8CString(subMessageName, "UpdateTouchPoint" )) { |
2006 | WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index" )); |
2007 | int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get())))); |
2008 | |
2009 | WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X" )); |
2010 | int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())))); |
2011 | |
2012 | WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y" )); |
2013 | int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())))); |
2014 | |
2015 | m_eventSenderProxy->updateTouchPoint(index, x, y); |
2016 | return 0; |
2017 | } |
2018 | |
2019 | if (WKStringIsEqualToUTF8CString(subMessageName, "SetTouchModifier" )) { |
2020 | WKRetainPtr<WKStringRef> modifierKey = adoptWK(WKStringCreateWithUTF8CString("Modifier" )); |
2021 | WKEventModifiers modifier = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifierKey.get())))); |
2022 | |
2023 | WKRetainPtr<WKStringRef> enableKey = adoptWK(WKStringCreateWithUTF8CString("Enable" )); |
2024 | bool enable = static_cast<bool>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, enableKey.get())))); |
2025 | |
2026 | m_eventSenderProxy->setTouchModifier(modifier, enable); |
2027 | return 0; |
2028 | } |
2029 | |
2030 | if (WKStringIsEqualToUTF8CString(subMessageName, "SetTouchPointRadius" )) { |
2031 | WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("RadiusX" )); |
2032 | int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())))); |
2033 | |
2034 | WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("RadiusY" )); |
2035 | int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())))); |
2036 | |
2037 | m_eventSenderProxy->setTouchPointRadius(x, y); |
2038 | return 0; |
2039 | } |
2040 | |
2041 | if (WKStringIsEqualToUTF8CString(subMessageName, "TouchStart" )) { |
2042 | m_eventSenderProxy->touchStart(); |
2043 | return 0; |
2044 | } |
2045 | |
2046 | if (WKStringIsEqualToUTF8CString(subMessageName, "TouchMove" )) { |
2047 | m_eventSenderProxy->touchMove(); |
2048 | return 0; |
2049 | } |
2050 | |
2051 | if (WKStringIsEqualToUTF8CString(subMessageName, "TouchEnd" )) { |
2052 | m_eventSenderProxy->touchEnd(); |
2053 | return 0; |
2054 | } |
2055 | |
2056 | if (WKStringIsEqualToUTF8CString(subMessageName, "TouchCancel" )) { |
2057 | m_eventSenderProxy->touchCancel(); |
2058 | return 0; |
2059 | } |
2060 | |
2061 | if (WKStringIsEqualToUTF8CString(subMessageName, "ClearTouchPoints" )) { |
2062 | m_eventSenderProxy->clearTouchPoints(); |
2063 | return 0; |
2064 | } |
2065 | |
2066 | if (WKStringIsEqualToUTF8CString(subMessageName, "ReleaseTouchPoint" )) { |
2067 | WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index" )); |
2068 | int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get())))); |
2069 | m_eventSenderProxy->releaseTouchPoint(index); |
2070 | return 0; |
2071 | } |
2072 | |
2073 | if (WKStringIsEqualToUTF8CString(subMessageName, "CancelTouchPoint" )) { |
2074 | WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index" )); |
2075 | int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get())))); |
2076 | m_eventSenderProxy->cancelTouchPoint(index); |
2077 | return 0; |
2078 | } |
2079 | #endif |
2080 | ASSERT_NOT_REACHED(); |
2081 | } |
2082 | return m_currentInvocation->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody); |
2083 | } |
2084 | |
2085 | WKRetainPtr<WKTypeRef> TestController::getInjectedBundleInitializationUserData() |
2086 | { |
2087 | return nullptr; |
2088 | } |
2089 | |
2090 | // WKContextClient |
2091 | |
2092 | void TestController::networkProcessDidCrash() |
2093 | { |
2094 | pid_t pid = WKContextGetNetworkProcessIdentifier(m_context.get()); |
2095 | fprintf(stderr, "#CRASHED - %s (pid %ld)\n" , networkProcessName(), static_cast<long>(pid)); |
2096 | exit(1); |
2097 | } |
2098 | |
2099 | // WKPageNavigationClient |
2100 | |
2101 | void TestController::didCommitNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef, const void* clientInfo) |
2102 | { |
2103 | static_cast<TestController*>(const_cast<void*>(clientInfo))->didCommitNavigation(page, navigation); |
2104 | } |
2105 | |
2106 | void TestController::didFinishNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef, const void* clientInfo) |
2107 | { |
2108 | static_cast<TestController*>(const_cast<void*>(clientInfo))->didFinishNavigation(page, navigation); |
2109 | } |
2110 | |
2111 | void TestController::didReceiveServerRedirectForProvisionalNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef userData, const void* clientInfo) |
2112 | { |
2113 | static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveServerRedirectForProvisionalNavigation(page, navigation, userData); |
2114 | } |
2115 | |
2116 | bool TestController::canAuthenticateAgainstProtectionSpace(WKPageRef page, WKProtectionSpaceRef protectionSpace, const void* clientInfo) |
2117 | { |
2118 | return static_cast<TestController*>(const_cast<void*>(clientInfo))->canAuthenticateAgainstProtectionSpace(page, protectionSpace); |
2119 | } |
2120 | |
2121 | void TestController::didReceiveAuthenticationChallenge(WKPageRef page, WKAuthenticationChallengeRef authenticationChallenge, const void *clientInfo) |
2122 | { |
2123 | static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveAuthenticationChallenge(page, /*frame,*/ authenticationChallenge); |
2124 | } |
2125 | |
2126 | void TestController::processDidCrash(WKPageRef page, const void* clientInfo) |
2127 | { |
2128 | static_cast<TestController*>(const_cast<void*>(clientInfo))->processDidCrash(); |
2129 | } |
2130 | |
2131 | void TestController::didBeginNavigationGesture(WKPageRef page, const void *clientInfo) |
2132 | { |
2133 | static_cast<TestController*>(const_cast<void*>(clientInfo))->didBeginNavigationGesture(page); |
2134 | } |
2135 | |
2136 | void TestController::willEndNavigationGesture(WKPageRef page, WKBackForwardListItemRef backForwardListItem, const void *clientInfo) |
2137 | { |
2138 | static_cast<TestController*>(const_cast<void*>(clientInfo))->willEndNavigationGesture(page, backForwardListItem); |
2139 | } |
2140 | |
2141 | void TestController::didEndNavigationGesture(WKPageRef page, WKBackForwardListItemRef backForwardListItem, const void *clientInfo) |
2142 | { |
2143 | static_cast<TestController*>(const_cast<void*>(clientInfo))->didEndNavigationGesture(page, backForwardListItem); |
2144 | } |
2145 | |
2146 | void TestController::didRemoveNavigationGestureSnapshot(WKPageRef page, const void *clientInfo) |
2147 | { |
2148 | static_cast<TestController*>(const_cast<void*>(clientInfo))->didRemoveNavigationGestureSnapshot(page); |
2149 | } |
2150 | |
2151 | WKPluginLoadPolicy TestController::decidePolicyForPluginLoad(WKPageRef page, WKPluginLoadPolicy currentPluginLoadPolicy, WKDictionaryRef pluginInformation, WKStringRef* unavailabilityDescription, const void* clientInfo) |
2152 | { |
2153 | return static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForPluginLoad(page, currentPluginLoadPolicy, pluginInformation, unavailabilityDescription); |
2154 | } |
2155 | |
2156 | WKPluginLoadPolicy TestController::decidePolicyForPluginLoad(WKPageRef, WKPluginLoadPolicy currentPluginLoadPolicy, WKDictionaryRef pluginInformation, WKStringRef* unavailabilityDescription) |
2157 | { |
2158 | if (m_shouldBlockAllPlugins) |
2159 | return kWKPluginLoadPolicyBlocked; |
2160 | |
2161 | #if PLATFORM(MAC) |
2162 | WKStringRef bundleIdentifier = (WKStringRef)WKDictionaryGetItemForKey(pluginInformation, WKPluginInformationBundleIdentifierKey()); |
2163 | if (!bundleIdentifier) |
2164 | return currentPluginLoadPolicy; |
2165 | |
2166 | if (WKStringIsEqualToUTF8CString(bundleIdentifier, "com.apple.QuickTime Plugin.plugin" )) |
2167 | return currentPluginLoadPolicy; |
2168 | |
2169 | if (WKStringIsEqualToUTF8CString(bundleIdentifier, "com.apple.testnetscapeplugin" )) |
2170 | return currentPluginLoadPolicy; |
2171 | |
2172 | RELEASE_ASSERT_NOT_REACHED(); // Please don't use any other plug-ins in tests, as they will not be installed on all machines. |
2173 | #else |
2174 | return currentPluginLoadPolicy; |
2175 | #endif |
2176 | } |
2177 | |
2178 | void TestController::setBlockAllPlugins(bool shouldBlock) |
2179 | { |
2180 | m_shouldBlockAllPlugins = shouldBlock; |
2181 | |
2182 | #if PLATFORM(MAC) |
2183 | auto policy = shouldBlock ? kWKPluginLoadClientPolicyBlock : kWKPluginLoadClientPolicyAllow; |
2184 | |
2185 | WKRetainPtr<WKStringRef> nameNetscape = adoptWK(WKStringCreateWithUTF8CString("com.apple.testnetscapeplugin" )); |
2186 | WKRetainPtr<WKStringRef> nameFlash = adoptWK(WKStringCreateWithUTF8CString("com.macromedia.Flash Player.plugin" )); |
2187 | WKRetainPtr<WKStringRef> emptyString = adoptWK(WKStringCreateWithUTF8CString("" )); |
2188 | WKContextSetPluginLoadClientPolicy(m_context.get(), policy, emptyString.get(), nameNetscape.get(), emptyString.get()); |
2189 | WKContextSetPluginLoadClientPolicy(m_context.get(), policy, emptyString.get(), nameFlash.get(), emptyString.get()); |
2190 | #endif |
2191 | } |
2192 | |
2193 | void TestController::setPluginSupportedMode(const String& mode) |
2194 | { |
2195 | if (m_unsupportedPluginMode == mode) |
2196 | return; |
2197 | |
2198 | m_unsupportedPluginMode = mode; |
2199 | if (m_unsupportedPluginMode.isEmpty()) { |
2200 | WKContextClearSupportedPlugins(m_context.get()); |
2201 | return; |
2202 | } |
2203 | |
2204 | WKRetainPtr<WKMutableArrayRef> emptyArray = adoptWK(WKMutableArrayCreate()); |
2205 | WKRetainPtr<WKStringRef> allOrigins = adoptWK(WKStringCreateWithUTF8CString("" )); |
2206 | WKRetainPtr<WKStringRef> specificOrigin = adoptWK(WKStringCreateWithUTF8CString("localhost" )); |
2207 | |
2208 | WKRetainPtr<WKStringRef> pdfName = adoptWK(WKStringCreateWithUTF8CString("My personal PDF" )); |
2209 | WKContextAddSupportedPlugin(m_context.get(), allOrigins.get(), pdfName.get(), emptyArray.get(), emptyArray.get()); |
2210 | |
2211 | WKRetainPtr<WKStringRef> nameNetscape = adoptWK(WKStringCreateWithUTF8CString("com.apple.testnetscapeplugin" )); |
2212 | WKRetainPtr<WKStringRef> mimeTypeNetscape = adoptWK(WKStringCreateWithUTF8CString("application/x-webkit-test-netscape" )); |
2213 | WKRetainPtr<WKMutableArrayRef> mimeTypesNetscape = adoptWK(WKMutableArrayCreate()); |
2214 | WKArrayAppendItem(mimeTypesNetscape.get(), mimeTypeNetscape.get()); |
2215 | |
2216 | WKRetainPtr<WKStringRef> namePdf = adoptWK(WKStringCreateWithUTF8CString("WebKit built-in PDF" )); |
2217 | |
2218 | if (m_unsupportedPluginMode == "allOrigins" ) { |
2219 | WKContextAddSupportedPlugin(m_context.get(), allOrigins.get(), nameNetscape.get(), mimeTypesNetscape.get(), emptyArray.get()); |
2220 | WKContextAddSupportedPlugin(m_context.get(), allOrigins.get(), namePdf.get(), emptyArray.get(), emptyArray.get()); |
2221 | return; |
2222 | } |
2223 | |
2224 | if (m_unsupportedPluginMode == "specificOrigin" ) { |
2225 | WKContextAddSupportedPlugin(m_context.get(), specificOrigin.get(), nameNetscape.get(), mimeTypesNetscape.get(), emptyArray.get()); |
2226 | WKContextAddSupportedPlugin(m_context.get(), specificOrigin.get(), namePdf.get(), emptyArray.get(), emptyArray.get()); |
2227 | return; |
2228 | } |
2229 | } |
2230 | |
2231 | void TestController::didCommitNavigation(WKPageRef page, WKNavigationRef navigation) |
2232 | { |
2233 | mainWebView()->focus(); |
2234 | } |
2235 | |
2236 | void TestController::didReceiveServerRedirectForProvisionalNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef userData) |
2237 | { |
2238 | m_didReceiveServerRedirectForProvisionalNavigation = true; |
2239 | return; |
2240 | } |
2241 | |
2242 | static const char* toString(WKProtectionSpaceAuthenticationScheme scheme) |
2243 | { |
2244 | switch (scheme) { |
2245 | case kWKProtectionSpaceAuthenticationSchemeDefault: |
2246 | return "ProtectionSpaceAuthenticationSchemeDefault" ; |
2247 | case kWKProtectionSpaceAuthenticationSchemeHTTPBasic: |
2248 | return "ProtectionSpaceAuthenticationSchemeHTTPBasic" ; |
2249 | case kWKProtectionSpaceAuthenticationSchemeHTMLForm: |
2250 | return "ProtectionSpaceAuthenticationSchemeHTMLForm" ; |
2251 | case kWKProtectionSpaceAuthenticationSchemeNTLM: |
2252 | return "ProtectionSpaceAuthenticationSchemeNTLM" ; |
2253 | case kWKProtectionSpaceAuthenticationSchemeNegotiate: |
2254 | return "ProtectionSpaceAuthenticationSchemeNegotiate" ; |
2255 | case kWKProtectionSpaceAuthenticationSchemeClientCertificateRequested: |
2256 | return "ProtectionSpaceAuthenticationSchemeClientCertificateRequested" ; |
2257 | case kWKProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested: |
2258 | return "ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested" ; |
2259 | case kWKProtectionSpaceAuthenticationSchemeOAuth: |
2260 | return "ProtectionSpaceAuthenticationSchemeOAuth" ; |
2261 | case kWKProtectionSpaceAuthenticationSchemeUnknown: |
2262 | return "ProtectionSpaceAuthenticationSchemeUnknown" ; |
2263 | } |
2264 | ASSERT_NOT_REACHED(); |
2265 | return "ProtectionSpaceAuthenticationSchemeUnknown" ; |
2266 | } |
2267 | |
2268 | bool TestController::canAuthenticateAgainstProtectionSpace(WKPageRef page, WKProtectionSpaceRef protectionSpace) |
2269 | { |
2270 | if (m_shouldLogCanAuthenticateAgainstProtectionSpace) |
2271 | m_currentInvocation->outputText("canAuthenticateAgainstProtectionSpace\n" ); |
2272 | WKProtectionSpaceAuthenticationScheme authenticationScheme = WKProtectionSpaceGetAuthenticationScheme(protectionSpace); |
2273 | |
2274 | if (authenticationScheme == kWKProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested) { |
2275 | std::string host = toSTD(adoptWK(WKProtectionSpaceCopyHost(protectionSpace)).get()); |
2276 | return host == "localhost" || host == "127.0.0.1" || (m_allowAnyHTTPSCertificateForAllowedHosts && m_allowedHosts.find(host) != m_allowedHosts.end()); |
2277 | } |
2278 | |
2279 | return authenticationScheme <= kWKProtectionSpaceAuthenticationSchemeHTTPDigest || authenticationScheme == kWKProtectionSpaceAuthenticationSchemeOAuth; |
2280 | } |
2281 | |
2282 | void TestController::didFinishNavigation(WKPageRef page, WKNavigationRef navigation) |
2283 | { |
2284 | if (m_state != Resetting) |
2285 | return; |
2286 | |
2287 | WKRetainPtr<WKURLRef> wkURL = adoptWK(WKFrameCopyURL(WKPageGetMainFrame(page))); |
2288 | if (!WKURLIsEqual(wkURL.get(), blankURL())) |
2289 | return; |
2290 | |
2291 | m_doneResetting = true; |
2292 | singleton().notifyDone(); |
2293 | } |
2294 | |
2295 | void TestController::didReceiveAuthenticationChallenge(WKPageRef page, WKAuthenticationChallengeRef authenticationChallenge) |
2296 | { |
2297 | WKProtectionSpaceRef protectionSpace = WKAuthenticationChallengeGetProtectionSpace(authenticationChallenge); |
2298 | WKAuthenticationDecisionListenerRef decisionListener = WKAuthenticationChallengeGetDecisionListener(authenticationChallenge); |
2299 | WKProtectionSpaceAuthenticationScheme authenticationScheme = WKProtectionSpaceGetAuthenticationScheme(protectionSpace); |
2300 | |
2301 | if (authenticationScheme == kWKProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested) { |
2302 | // Any non-empty credential signals to accept the server trust. Since the cross-platform API |
2303 | // doesn't expose a way to create a credential from server trust, we use a password credential. |
2304 | |
2305 | m_serverTrustEvaluationCallbackCallsCount++; |
2306 | |
2307 | WKRetainPtr<WKCredentialRef> credential = adoptWK(WKCredentialCreate(toWK("accept server trust" ).get(), toWK("" ).get(), kWKCredentialPersistenceNone)); |
2308 | WKAuthenticationDecisionListenerUseCredential(decisionListener, credential.get()); |
2309 | return; |
2310 | } |
2311 | |
2312 | if (m_rejectsProtectionSpaceAndContinueForAuthenticationChallenges) { |
2313 | m_currentInvocation->outputText("Simulating reject protection space and continue for authentication challenge\n" ); |
2314 | WKAuthenticationDecisionListenerRejectProtectionSpaceAndContinue(decisionListener); |
2315 | return; |
2316 | } |
2317 | |
2318 | std::string host = toSTD(adoptWK(WKProtectionSpaceCopyHost(protectionSpace)).get()); |
2319 | int port = WKProtectionSpaceGetPort(protectionSpace); |
2320 | String message = makeString(host.c_str(), ':', port, " - didReceiveAuthenticationChallenge - " , toString(authenticationScheme), " - " ); |
2321 | if (!m_handlesAuthenticationChallenges) |
2322 | message.append("Simulating cancelled authentication sheet\n" ); |
2323 | else |
2324 | message.append("Responding with " + m_authenticationUsername + ":" + m_authenticationPassword + "\n" ); |
2325 | m_currentInvocation->outputText(message); |
2326 | |
2327 | if (!m_handlesAuthenticationChallenges) { |
2328 | WKAuthenticationDecisionListenerUseCredential(decisionListener, 0); |
2329 | return; |
2330 | } |
2331 | WKRetainPtr<WKStringRef> username = adoptWK(WKStringCreateWithUTF8CString(m_authenticationUsername.utf8().data())); |
2332 | WKRetainPtr<WKStringRef> password = adoptWK(WKStringCreateWithUTF8CString(m_authenticationPassword.utf8().data())); |
2333 | WKRetainPtr<WKCredentialRef> credential = adoptWK(WKCredentialCreate(username.get(), password.get(), kWKCredentialPersistenceForSession)); |
2334 | WKAuthenticationDecisionListenerUseCredential(decisionListener, credential.get()); |
2335 | } |
2336 | |
2337 | |
2338 | // WKContextDownloadClient |
2339 | |
2340 | void TestController::downloadDidStart(WKContextRef context, WKDownloadRef download, const void* clientInfo) |
2341 | { |
2342 | static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidStart(context, download); |
2343 | } |
2344 | |
2345 | WKStringRef TestController::decideDestinationWithSuggestedFilename(WKContextRef context, WKDownloadRef download, WKStringRef filename, bool* allowOverwrite, const void* clientInfo) |
2346 | { |
2347 | return static_cast<TestController*>(const_cast<void*>(clientInfo))->decideDestinationWithSuggestedFilename(context, download, filename, allowOverwrite); |
2348 | } |
2349 | |
2350 | void TestController::downloadDidFinish(WKContextRef context, WKDownloadRef download, const void* clientInfo) |
2351 | { |
2352 | static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidFinish(context, download); |
2353 | } |
2354 | |
2355 | void TestController::downloadDidFail(WKContextRef context, WKDownloadRef download, WKErrorRef error, const void* clientInfo) |
2356 | { |
2357 | static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidFail(context, download, error); |
2358 | } |
2359 | |
2360 | void TestController::downloadDidCancel(WKContextRef context, WKDownloadRef download, const void* clientInfo) |
2361 | { |
2362 | static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidCancel(context, download); |
2363 | } |
2364 | |
2365 | void TestController::downloadDidReceiveServerRedirectToURL(WKContextRef context, WKDownloadRef download, WKURLRef url, const void* clientInfo) |
2366 | { |
2367 | static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidReceiveServerRedirectToURL(context, download, url); |
2368 | } |
2369 | |
2370 | void TestController::downloadDidStart(WKContextRef context, WKDownloadRef download) |
2371 | { |
2372 | if (m_shouldLogDownloadCallbacks) |
2373 | m_currentInvocation->outputText("Download started.\n" ); |
2374 | } |
2375 | |
2376 | WKStringRef TestController::decideDestinationWithSuggestedFilename(WKContextRef, WKDownloadRef, WKStringRef filename, bool*& allowOverwrite) |
2377 | { |
2378 | String suggestedFilename = toWTFString(filename); |
2379 | |
2380 | if (m_shouldLogDownloadCallbacks) { |
2381 | StringBuilder builder; |
2382 | builder.append("Downloading URL with suggested filename \"" ); |
2383 | builder.append(suggestedFilename); |
2384 | builder.append("\"\n" ); |
2385 | m_currentInvocation->outputText(builder.toString()); |
2386 | } |
2387 | |
2388 | const char* dumpRenderTreeTemp = libraryPathForTesting(); |
2389 | if (!dumpRenderTreeTemp) |
2390 | return nullptr; |
2391 | |
2392 | *allowOverwrite = true; |
2393 | String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp); |
2394 | if (suggestedFilename.isEmpty()) |
2395 | suggestedFilename = "Unknown" ; |
2396 | |
2397 | return toWK(temporaryFolder + "/" + suggestedFilename).leakRef(); |
2398 | } |
2399 | |
2400 | void TestController::downloadDidFinish(WKContextRef, WKDownloadRef) |
2401 | { |
2402 | if (m_shouldLogDownloadCallbacks) |
2403 | m_currentInvocation->outputText("Download completed.\n" ); |
2404 | m_currentInvocation->notifyDownloadDone(); |
2405 | } |
2406 | |
2407 | void TestController::downloadDidReceiveServerRedirectToURL(WKContextRef, WKDownloadRef, WKURLRef url) |
2408 | { |
2409 | if (m_shouldLogDownloadCallbacks) { |
2410 | StringBuilder builder; |
2411 | builder.appendLiteral("Download was redirected to \"" ); |
2412 | WKRetainPtr<WKStringRef> urlStringWK = adoptWK(WKURLCopyString(url)); |
2413 | builder.append(toSTD(urlStringWK).c_str()); |
2414 | builder.appendLiteral("\".\n" ); |
2415 | m_currentInvocation->outputText(builder.toString()); |
2416 | } |
2417 | } |
2418 | |
2419 | void TestController::downloadDidFail(WKContextRef, WKDownloadRef, WKErrorRef error) |
2420 | { |
2421 | if (m_shouldLogDownloadCallbacks) { |
2422 | m_currentInvocation->outputText("Download failed.\n"_s ); |
2423 | |
2424 | WKRetainPtr<WKStringRef> errorDomain = adoptWK(WKErrorCopyDomain(error)); |
2425 | WKRetainPtr<WKStringRef> errorDescription = adoptWK(WKErrorCopyLocalizedDescription(error)); |
2426 | int errorCode = WKErrorGetErrorCode(error); |
2427 | |
2428 | StringBuilder errorBuilder; |
2429 | errorBuilder.append("Failed: " ); |
2430 | errorBuilder.append(toWTFString(errorDomain)); |
2431 | errorBuilder.append(", code=" ); |
2432 | errorBuilder.appendNumber(errorCode); |
2433 | errorBuilder.append(", description=" ); |
2434 | errorBuilder.append(toWTFString(errorDescription)); |
2435 | errorBuilder.append("\n" ); |
2436 | |
2437 | m_currentInvocation->outputText(errorBuilder.toString()); |
2438 | } |
2439 | m_currentInvocation->notifyDownloadDone(); |
2440 | } |
2441 | |
2442 | void TestController::downloadDidCancel(WKContextRef, WKDownloadRef) |
2443 | { |
2444 | if (m_shouldLogDownloadCallbacks) |
2445 | m_currentInvocation->outputText("Download cancelled.\n" ); |
2446 | m_currentInvocation->notifyDownloadDone(); |
2447 | } |
2448 | |
2449 | void TestController::processDidCrash() |
2450 | { |
2451 | // This function can be called multiple times when crash logs are being saved on Windows, so |
2452 | // ensure we only print the crashed message once. |
2453 | if (!m_didPrintWebProcessCrashedMessage) { |
2454 | pid_t pid = WKPageGetProcessIdentifier(m_mainWebView->page()); |
2455 | fprintf(stderr, "#CRASHED - %s (pid %ld)\n" , webProcessName(), static_cast<long>(pid)); |
2456 | fflush(stderr); |
2457 | m_didPrintWebProcessCrashedMessage = true; |
2458 | } |
2459 | |
2460 | if (m_shouldExitWhenWebProcessCrashes) |
2461 | exit(1); |
2462 | } |
2463 | |
2464 | void TestController::didBeginNavigationGesture(WKPageRef) |
2465 | { |
2466 | m_currentInvocation->didBeginSwipe(); |
2467 | } |
2468 | |
2469 | void TestController::willEndNavigationGesture(WKPageRef, WKBackForwardListItemRef) |
2470 | { |
2471 | m_currentInvocation->willEndSwipe(); |
2472 | } |
2473 | |
2474 | void TestController::didEndNavigationGesture(WKPageRef, WKBackForwardListItemRef) |
2475 | { |
2476 | m_currentInvocation->didEndSwipe(); |
2477 | } |
2478 | |
2479 | void TestController::didRemoveNavigationGestureSnapshot(WKPageRef) |
2480 | { |
2481 | m_currentInvocation->didRemoveSwipeSnapshot(); |
2482 | } |
2483 | |
2484 | void TestController::simulateWebNotificationClick(uint64_t notificationID) |
2485 | { |
2486 | m_webNotificationProvider.simulateWebNotificationClick(mainWebView()->page(), notificationID); |
2487 | } |
2488 | |
2489 | void TestController::setGeolocationPermission(bool enabled) |
2490 | { |
2491 | m_isGeolocationPermissionSet = true; |
2492 | m_isGeolocationPermissionAllowed = enabled; |
2493 | decidePolicyForGeolocationPermissionRequestIfPossible(); |
2494 | } |
2495 | |
2496 | void TestController::setMockGeolocationPosition(double latitude, double longitude, double accuracy, bool providesAltitude, double altitude, bool providesAltitudeAccuracy, double altitudeAccuracy, bool providesHeading, double heading, bool providesSpeed, double speed, bool providesFloorLevel, double floorLevel) |
2497 | { |
2498 | m_geolocationProvider->setPosition(latitude, longitude, accuracy, providesAltitude, altitude, providesAltitudeAccuracy, altitudeAccuracy, providesHeading, heading, providesSpeed, speed, providesFloorLevel, floorLevel); |
2499 | } |
2500 | |
2501 | void TestController::setMockGeolocationPositionUnavailableError(WKStringRef errorMessage) |
2502 | { |
2503 | m_geolocationProvider->setPositionUnavailableError(errorMessage); |
2504 | } |
2505 | |
2506 | void TestController::handleGeolocationPermissionRequest(WKGeolocationPermissionRequestRef geolocationPermissionRequest) |
2507 | { |
2508 | m_geolocationPermissionRequests.append(geolocationPermissionRequest); |
2509 | decidePolicyForGeolocationPermissionRequestIfPossible(); |
2510 | } |
2511 | |
2512 | bool TestController::isGeolocationProviderActive() const |
2513 | { |
2514 | return m_geolocationProvider->isActive(); |
2515 | } |
2516 | |
2517 | static String originUserVisibleName(WKSecurityOriginRef origin) |
2518 | { |
2519 | if (!origin) |
2520 | return emptyString(); |
2521 | |
2522 | auto host = toWTFString(adoptWK(WKSecurityOriginCopyHost(origin))); |
2523 | auto protocol = toWTFString(adoptWK(WKSecurityOriginCopyProtocol(origin))); |
2524 | |
2525 | if (host.isEmpty() || protocol.isEmpty()) |
2526 | return emptyString(); |
2527 | |
2528 | if (int port = WKSecurityOriginGetPort(origin)) |
2529 | return makeString(protocol, "://" , host, ':', port); |
2530 | |
2531 | return makeString(protocol, "://" , host); |
2532 | } |
2533 | |
2534 | static String userMediaOriginHash(WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin) |
2535 | { |
2536 | String userMediaDocumentOriginString = originUserVisibleName(userMediaDocumentOrigin); |
2537 | String topLevelDocumentOriginString = originUserVisibleName(topLevelDocumentOrigin); |
2538 | |
2539 | if (topLevelDocumentOriginString.isEmpty()) |
2540 | return userMediaDocumentOriginString; |
2541 | |
2542 | return makeString(userMediaDocumentOriginString, '-', topLevelDocumentOriginString); |
2543 | } |
2544 | |
2545 | static String userMediaOriginHash(WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString) |
2546 | { |
2547 | auto userMediaDocumentOrigin = adoptWK(WKSecurityOriginCreateFromString(userMediaDocumentOriginString)); |
2548 | if (!WKStringGetLength(topLevelDocumentOriginString)) |
2549 | return userMediaOriginHash(userMediaDocumentOrigin.get(), nullptr); |
2550 | |
2551 | auto topLevelDocumentOrigin = adoptWK(WKSecurityOriginCreateFromString(topLevelDocumentOriginString)); |
2552 | return userMediaOriginHash(userMediaDocumentOrigin.get(), topLevelDocumentOrigin.get()); |
2553 | } |
2554 | |
2555 | void TestController::setUserMediaPermission(bool enabled) |
2556 | { |
2557 | m_isUserMediaPermissionSet = true; |
2558 | m_isUserMediaPermissionAllowed = enabled; |
2559 | decidePolicyForUserMediaPermissionRequestIfPossible(); |
2560 | } |
2561 | |
2562 | void TestController::resetUserMediaPermission() |
2563 | { |
2564 | m_isUserMediaPermissionSet = false; |
2565 | } |
2566 | |
2567 | void TestController::setShouldDismissJavaScriptAlertsAsynchronously(bool value) |
2568 | { |
2569 | m_shouldDismissJavaScriptAlertsAsynchronously = value; |
2570 | } |
2571 | |
2572 | void TestController::handleJavaScriptAlert(WKPageRunJavaScriptAlertResultListenerRef listener) |
2573 | { |
2574 | if (!m_shouldDismissJavaScriptAlertsAsynchronously) { |
2575 | WKPageRunJavaScriptAlertResultListenerCall(listener); |
2576 | return; |
2577 | } |
2578 | |
2579 | WKRetain(listener); |
2580 | callOnMainThread([listener] { |
2581 | WKPageRunJavaScriptAlertResultListenerCall(listener); |
2582 | WKRelease(listener); |
2583 | }); |
2584 | } |
2585 | |
2586 | class OriginSettings : public RefCounted<OriginSettings> { |
2587 | public: |
2588 | explicit OriginSettings() |
2589 | { |
2590 | } |
2591 | |
2592 | bool persistentPermission() const { return m_persistentPermission; } |
2593 | void setPersistentPermission(bool permission) { m_persistentPermission = permission; } |
2594 | |
2595 | String persistentSalt() const { return m_persistentSalt; } |
2596 | void setPersistentSalt(const String& salt) { m_persistentSalt = salt; } |
2597 | |
2598 | HashMap<uint64_t, String>& ephemeralSalts() { return m_ephemeralSalts; } |
2599 | |
2600 | void incrementRequestCount() { ++m_requestCount; } |
2601 | void resetRequestCount() { m_requestCount = 0; } |
2602 | unsigned requestCount() const { return m_requestCount; } |
2603 | |
2604 | private: |
2605 | HashMap<uint64_t, String> m_ephemeralSalts; |
2606 | String m_persistentSalt; |
2607 | unsigned m_requestCount { 0 }; |
2608 | bool m_persistentPermission { false }; |
2609 | }; |
2610 | |
2611 | String TestController::saltForOrigin(WKFrameRef frame, String originHash) |
2612 | { |
2613 | auto& settings = settingsForOrigin(originHash); |
2614 | auto& ephemeralSalts = settings.ephemeralSalts(); |
2615 | auto frameInfo = adoptWK(WKFrameCreateFrameInfo(frame)); |
2616 | auto frameHandle = WKFrameInfoGetFrameHandleRef(frameInfo.get()); |
2617 | uint64_t frameIdentifier = WKFrameHandleGetFrameID(frameHandle); |
2618 | String frameSalt = ephemeralSalts.get(frameIdentifier); |
2619 | |
2620 | if (settings.persistentPermission()) { |
2621 | if (frameSalt.length()) |
2622 | return frameSalt; |
2623 | |
2624 | if (!settings.persistentSalt().length()) |
2625 | settings.setPersistentSalt(createCanonicalUUIDString()); |
2626 | |
2627 | return settings.persistentSalt(); |
2628 | } |
2629 | |
2630 | if (!frameSalt.length()) { |
2631 | frameSalt = createCanonicalUUIDString(); |
2632 | ephemeralSalts.add(frameIdentifier, frameSalt); |
2633 | } |
2634 | |
2635 | return frameSalt; |
2636 | } |
2637 | |
2638 | void TestController::setUserMediaPersistentPermissionForOrigin(bool permission, WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString) |
2639 | { |
2640 | auto originHash = userMediaOriginHash(userMediaDocumentOriginString, topLevelDocumentOriginString); |
2641 | auto& settings = settingsForOrigin(originHash); |
2642 | settings.setPersistentPermission(permission); |
2643 | } |
2644 | |
2645 | void TestController::handleCheckOfUserMediaPermissionForOrigin(WKFrameRef frame, WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin, const WKUserMediaPermissionCheckRef& checkRequest) |
2646 | { |
2647 | auto originHash = userMediaOriginHash(userMediaDocumentOrigin, topLevelDocumentOrigin); |
2648 | auto salt = saltForOrigin(frame, originHash); |
2649 | WKRetainPtr<WKStringRef> saltString = adoptWK(WKStringCreateWithUTF8CString(salt.utf8().data())); |
2650 | |
2651 | WKUserMediaPermissionCheckSetUserMediaAccessInfo(checkRequest, saltString.get(), settingsForOrigin(originHash).persistentPermission()); |
2652 | } |
2653 | |
2654 | bool TestController::handleDeviceOrientationAndMotionAccessRequest(WKSecurityOriginRef origin) |
2655 | { |
2656 | m_currentInvocation->outputText(makeString("Received device orientation & motion access request for security origin \"" , originUserVisibleName(origin), "\".\n" )); |
2657 | return m_shouldAllowDeviceOrientationAndMotionAccess; |
2658 | } |
2659 | |
2660 | void TestController::handleUserMediaPermissionRequest(WKFrameRef frame, WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin, WKUserMediaPermissionRequestRef request) |
2661 | { |
2662 | auto originHash = userMediaOriginHash(userMediaDocumentOrigin, topLevelDocumentOrigin); |
2663 | m_userMediaPermissionRequests.append(std::make_pair(originHash, request)); |
2664 | decidePolicyForUserMediaPermissionRequestIfPossible(); |
2665 | } |
2666 | |
2667 | OriginSettings& TestController::settingsForOrigin(const String& originHash) |
2668 | { |
2669 | RefPtr<OriginSettings> settings = m_cachedUserMediaPermissions.get(originHash); |
2670 | if (!settings) { |
2671 | settings = adoptRef(*new OriginSettings()); |
2672 | m_cachedUserMediaPermissions.add(originHash, settings); |
2673 | } |
2674 | |
2675 | return *settings; |
2676 | } |
2677 | |
2678 | unsigned TestController::userMediaPermissionRequestCountForOrigin(WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString) |
2679 | { |
2680 | auto originHash = userMediaOriginHash(userMediaDocumentOriginString, topLevelDocumentOriginString); |
2681 | return settingsForOrigin(originHash).requestCount(); |
2682 | } |
2683 | |
2684 | void TestController::resetUserMediaPermissionRequestCountForOrigin(WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString) |
2685 | { |
2686 | auto originHash = userMediaOriginHash(userMediaDocumentOriginString, topLevelDocumentOriginString); |
2687 | settingsForOrigin(originHash).resetRequestCount(); |
2688 | } |
2689 | |
2690 | void TestController::decidePolicyForUserMediaPermissionRequestIfPossible() |
2691 | { |
2692 | if (!m_isUserMediaPermissionSet) |
2693 | return; |
2694 | |
2695 | for (auto& pair : m_userMediaPermissionRequests) { |
2696 | auto originHash = pair.first; |
2697 | auto request = pair.second.get(); |
2698 | |
2699 | auto& settings = settingsForOrigin(originHash); |
2700 | settings.incrementRequestCount(); |
2701 | |
2702 | if (!m_isUserMediaPermissionAllowed && !settings.persistentPermission()) { |
2703 | WKUserMediaPermissionRequestDeny(request, kWKPermissionDenied); |
2704 | continue; |
2705 | } |
2706 | |
2707 | WKRetainPtr<WKArrayRef> audioDeviceUIDs = adoptWK(WKUserMediaPermissionRequestAudioDeviceUIDs(request)); |
2708 | WKRetainPtr<WKArrayRef> videoDeviceUIDs = adoptWK(WKUserMediaPermissionRequestVideoDeviceUIDs(request)); |
2709 | |
2710 | if (!WKArrayGetSize(videoDeviceUIDs.get()) && !WKArrayGetSize(audioDeviceUIDs.get())) { |
2711 | WKUserMediaPermissionRequestDeny(request, kWKNoConstraints); |
2712 | continue; |
2713 | } |
2714 | |
2715 | WKRetainPtr<WKStringRef> videoDeviceUID; |
2716 | if (WKArrayGetSize(videoDeviceUIDs.get())) |
2717 | videoDeviceUID = reinterpret_cast<WKStringRef>(WKArrayGetItemAtIndex(videoDeviceUIDs.get(), 0)); |
2718 | else |
2719 | videoDeviceUID = adoptWK(WKStringCreateWithUTF8CString("" )); |
2720 | |
2721 | WKRetainPtr<WKStringRef> audioDeviceUID; |
2722 | if (WKArrayGetSize(audioDeviceUIDs.get())) |
2723 | audioDeviceUID = reinterpret_cast<WKStringRef>(WKArrayGetItemAtIndex(audioDeviceUIDs.get(), 0)); |
2724 | else |
2725 | audioDeviceUID = adoptWK(WKStringCreateWithUTF8CString("" )); |
2726 | |
2727 | WKUserMediaPermissionRequestAllow(request, audioDeviceUID.get(), videoDeviceUID.get()); |
2728 | } |
2729 | m_userMediaPermissionRequests.clear(); |
2730 | } |
2731 | |
2732 | void TestController::setCustomPolicyDelegate(bool enabled, bool permissive) |
2733 | { |
2734 | m_policyDelegateEnabled = enabled; |
2735 | m_policyDelegatePermissive = permissive; |
2736 | } |
2737 | |
2738 | void TestController::decidePolicyForGeolocationPermissionRequestIfPossible() |
2739 | { |
2740 | if (!m_isGeolocationPermissionSet) |
2741 | return; |
2742 | |
2743 | for (size_t i = 0; i < m_geolocationPermissionRequests.size(); ++i) { |
2744 | WKGeolocationPermissionRequestRef permissionRequest = m_geolocationPermissionRequests[i].get(); |
2745 | if (m_isGeolocationPermissionAllowed) |
2746 | WKGeolocationPermissionRequestAllow(permissionRequest); |
2747 | else |
2748 | WKGeolocationPermissionRequestDeny(permissionRequest); |
2749 | } |
2750 | m_geolocationPermissionRequests.clear(); |
2751 | } |
2752 | |
2753 | void TestController::decidePolicyForNotificationPermissionRequest(WKPageRef page, WKSecurityOriginRef origin, WKNotificationPermissionRequestRef request, const void*) |
2754 | { |
2755 | TestController::singleton().decidePolicyForNotificationPermissionRequest(page, origin, request); |
2756 | } |
2757 | |
2758 | void TestController::decidePolicyForNotificationPermissionRequest(WKPageRef, WKSecurityOriginRef, WKNotificationPermissionRequestRef request) |
2759 | { |
2760 | WKNotificationPermissionRequestAllow(request); |
2761 | } |
2762 | |
2763 | void TestController::unavailablePluginButtonClicked(WKPageRef, WKPluginUnavailabilityReason, WKDictionaryRef, const void*) |
2764 | { |
2765 | printf("MISSING PLUGIN BUTTON PRESSED\n" ); |
2766 | } |
2767 | |
2768 | void TestController::decidePolicyForNavigationAction(WKPageRef, WKNavigationActionRef navigationAction, WKFramePolicyListenerRef listener, WKTypeRef, const void* clientInfo) |
2769 | { |
2770 | static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForNavigationAction(navigationAction, listener); |
2771 | } |
2772 | |
2773 | void TestController::decidePolicyForNavigationAction(WKNavigationActionRef navigationAction, WKFramePolicyListenerRef listener) |
2774 | { |
2775 | WKRetainPtr<WKFramePolicyListenerRef> retainedListener { listener }; |
2776 | WKRetainPtr<WKNavigationActionRef> retainedNavigationAction { navigationAction }; |
2777 | const bool shouldIgnore { m_policyDelegateEnabled && !m_policyDelegatePermissive }; |
2778 | auto decisionFunction = [shouldIgnore, retainedListener, retainedNavigationAction]() { |
2779 | if (shouldIgnore) |
2780 | WKFramePolicyListenerIgnore(retainedListener.get()); |
2781 | else if (WKNavigationActionShouldPerformDownload(retainedNavigationAction.get())) |
2782 | WKFramePolicyListenerDownload(retainedListener.get()); |
2783 | else |
2784 | WKFramePolicyListenerUse(retainedListener.get()); |
2785 | }; |
2786 | |
2787 | if (m_shouldDecideNavigationPolicyAfterDelay) |
2788 | RunLoop::main().dispatch(WTFMove(decisionFunction)); |
2789 | else |
2790 | decisionFunction(); |
2791 | } |
2792 | |
2793 | void TestController::decidePolicyForNavigationResponse(WKPageRef, WKNavigationResponseRef navigationResponse, WKFramePolicyListenerRef listener, WKTypeRef, const void* clientInfo) |
2794 | { |
2795 | static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForNavigationResponse(navigationResponse, listener); |
2796 | } |
2797 | |
2798 | void TestController::decidePolicyForNavigationResponse(WKNavigationResponseRef navigationResponse, WKFramePolicyListenerRef listener) |
2799 | { |
2800 | WKRetainPtr<WKNavigationResponseRef> retainedNavigationResponse { navigationResponse }; |
2801 | WKRetainPtr<WKFramePolicyListenerRef> retainedListener { listener }; |
2802 | |
2803 | bool shouldDownloadUndisplayableMIMETypes = m_shouldDownloadUndisplayableMIMETypes; |
2804 | auto decisionFunction = [shouldDownloadUndisplayableMIMETypes, retainedNavigationResponse, retainedListener]() { |
2805 | // Even though Response was already checked by WKBundlePagePolicyClient, the check did not include plugins |
2806 | // so we have to re-check again. |
2807 | if (WKNavigationResponseCanShowMIMEType(retainedNavigationResponse.get())) { |
2808 | WKFramePolicyListenerUse(retainedListener.get()); |
2809 | return; |
2810 | } |
2811 | |
2812 | if (shouldDownloadUndisplayableMIMETypes) |
2813 | WKFramePolicyListenerDownload(retainedListener.get()); |
2814 | else |
2815 | WKFramePolicyListenerIgnore(retainedListener.get()); |
2816 | }; |
2817 | |
2818 | if (m_shouldDecideResponsePolicyAfterDelay) |
2819 | RunLoop::main().dispatch(WTFMove(decisionFunction)); |
2820 | else |
2821 | decisionFunction(); |
2822 | } |
2823 | |
2824 | void TestController::didNavigateWithNavigationData(WKContextRef, WKPageRef, WKNavigationDataRef navigationData, WKFrameRef frame, const void* clientInfo) |
2825 | { |
2826 | static_cast<TestController*>(const_cast<void*>(clientInfo))->didNavigateWithNavigationData(navigationData, frame); |
2827 | } |
2828 | |
2829 | void TestController::didNavigateWithNavigationData(WKNavigationDataRef navigationData, WKFrameRef) |
2830 | { |
2831 | if (m_state != RunningTest) |
2832 | return; |
2833 | |
2834 | if (!m_shouldLogHistoryClientCallbacks) |
2835 | return; |
2836 | |
2837 | // URL |
2838 | auto url = adoptWK(WKNavigationDataCopyURL(navigationData)); |
2839 | auto urlString = toWTFString(adoptWK(WKURLCopyString(url.get()))); |
2840 | // Title |
2841 | auto title = toWTFString(adoptWK(WKNavigationDataCopyTitle(navigationData))); |
2842 | // HTTP method |
2843 | auto request = adoptWK(WKNavigationDataCopyOriginalRequest(navigationData)); |
2844 | auto method = toWTFString(adoptWK(WKURLRequestCopyHTTPMethod(request.get()))); |
2845 | |
2846 | // FIXME: Determine whether the navigation was successful / a client redirect rather than hard-coding the message here. |
2847 | m_currentInvocation->outputText(makeString("WebView navigated to url \"" , urlString, "\" with title \"" , title, "\" with HTTP equivalent method \"" , method, |
2848 | "\". The navigation was successful and was not a client redirect.\n" )); |
2849 | } |
2850 | |
2851 | void TestController::didPerformClientRedirect(WKContextRef, WKPageRef, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void* clientInfo) |
2852 | { |
2853 | static_cast<TestController*>(const_cast<void*>(clientInfo))->didPerformClientRedirect(sourceURL, destinationURL, frame); |
2854 | } |
2855 | |
2856 | void TestController::didPerformClientRedirect(WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef) |
2857 | { |
2858 | if (m_state != RunningTest) |
2859 | return; |
2860 | |
2861 | if (!m_shouldLogHistoryClientCallbacks) |
2862 | return; |
2863 | |
2864 | auto source = toWTFString(adoptWK(WKURLCopyString(sourceURL))); |
2865 | auto destination = toWTFString(adoptWK(WKURLCopyString(destinationURL))); |
2866 | |
2867 | m_currentInvocation->outputText(makeString("WebView performed a client redirect from \"" , source, "\" to \"" , destination, "\".\n" )); |
2868 | } |
2869 | |
2870 | void TestController::didPerformServerRedirect(WKContextRef, WKPageRef, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void* clientInfo) |
2871 | { |
2872 | static_cast<TestController*>(const_cast<void*>(clientInfo))->didPerformServerRedirect(sourceURL, destinationURL, frame); |
2873 | } |
2874 | |
2875 | void TestController::didPerformServerRedirect(WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef) |
2876 | { |
2877 | if (m_state != RunningTest) |
2878 | return; |
2879 | |
2880 | if (!m_shouldLogHistoryClientCallbacks) |
2881 | return; |
2882 | |
2883 | auto source = toWTFString(adoptWK(WKURLCopyString(sourceURL))); |
2884 | auto destination = toWTFString(adoptWK(WKURLCopyString(destinationURL))); |
2885 | |
2886 | m_currentInvocation->outputText(makeString("WebView performed a server redirect from \"" , source, "\" to \"" , destination, "\".\n" )); |
2887 | } |
2888 | |
2889 | void TestController::didUpdateHistoryTitle(WKContextRef, WKPageRef, WKStringRef title, WKURLRef URL, WKFrameRef frame, const void* clientInfo) |
2890 | { |
2891 | static_cast<TestController*>(const_cast<void*>(clientInfo))->didUpdateHistoryTitle(title, URL, frame); |
2892 | } |
2893 | |
2894 | void TestController::didUpdateHistoryTitle(WKStringRef title, WKURLRef URL, WKFrameRef) |
2895 | { |
2896 | if (m_state != RunningTest) |
2897 | return; |
2898 | |
2899 | if (!m_shouldLogHistoryClientCallbacks) |
2900 | return; |
2901 | |
2902 | auto urlString = toWTFString(adoptWK(WKURLCopyString(URL))); |
2903 | m_currentInvocation->outputText(makeString("WebView updated the title for history URL \"" , urlString, "\" to \"" , toWTFString(title), "\".\n" )); |
2904 | } |
2905 | |
2906 | void TestController::setNavigationGesturesEnabled(bool value) |
2907 | { |
2908 | m_mainWebView->setNavigationGesturesEnabled(value); |
2909 | } |
2910 | |
2911 | void TestController::setIgnoresViewportScaleLimits(bool ignoresViewportScaleLimits) |
2912 | { |
2913 | WKPageSetIgnoresViewportScaleLimits(m_mainWebView->page(), ignoresViewportScaleLimits); |
2914 | } |
2915 | |
2916 | void TestController::terminateNetworkProcess() |
2917 | { |
2918 | WKContextTerminateNetworkProcess(platformContext()); |
2919 | } |
2920 | |
2921 | void TestController::terminateServiceWorkerProcess() |
2922 | { |
2923 | WKContextTerminateServiceWorkerProcess(platformContext()); |
2924 | } |
2925 | |
2926 | #if !PLATFORM(COCOA) |
2927 | void TestController::platformWillRunTest(const TestInvocation&) |
2928 | { |
2929 | } |
2930 | |
2931 | void TestController::platformCreateWebView(WKPageConfigurationRef configuration, const TestOptions& options) |
2932 | { |
2933 | m_mainWebView = std::make_unique<PlatformWebView>(configuration, options); |
2934 | } |
2935 | |
2936 | PlatformWebView* TestController::platformCreateOtherPage(PlatformWebView* parentView, WKPageConfigurationRef configuration, const TestOptions& options) |
2937 | { |
2938 | return new PlatformWebView(configuration, options); |
2939 | } |
2940 | |
2941 | WKContextRef TestController::platformAdjustContext(WKContextRef context, WKContextConfigurationRef contextConfiguration) |
2942 | { |
2943 | auto* dataStore = WKContextGetWebsiteDataStore(context); |
2944 | WKWebsiteDataStoreSetResourceLoadStatisticsEnabled(dataStore, true); |
2945 | |
2946 | if (const char* dumpRenderTreeTemp = libraryPathForTesting()) { |
2947 | String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp); |
2948 | |
2949 | WKWebsiteDataStoreSetServiceWorkerRegistrationDirectory(dataStore, toWK(temporaryFolder + pathSeparator + "ServiceWorkers" ).get()); |
2950 | } |
2951 | |
2952 | return context; |
2953 | } |
2954 | |
2955 | void TestController::platformResetStateToConsistentValues(const TestOptions&) |
2956 | { |
2957 | } |
2958 | |
2959 | unsigned TestController::imageCountInGeneralPasteboard() const |
2960 | { |
2961 | return 0; |
2962 | } |
2963 | |
2964 | void TestController::removeAllSessionCredentials() |
2965 | { |
2966 | } |
2967 | |
2968 | void TestController::getAllStorageAccessEntries() |
2969 | { |
2970 | } |
2971 | |
2972 | #endif |
2973 | |
2974 | struct ClearServiceWorkerRegistrationsCallbackContext { |
2975 | explicit ClearServiceWorkerRegistrationsCallbackContext(TestController& controller) |
2976 | : testController(controller) |
2977 | { |
2978 | } |
2979 | |
2980 | TestController& testController; |
2981 | bool done { false }; |
2982 | }; |
2983 | |
2984 | static void clearServiceWorkerRegistrationsCallback(void* userData) |
2985 | { |
2986 | auto* context = static_cast<ClearServiceWorkerRegistrationsCallbackContext*>(userData); |
2987 | context->done = true; |
2988 | context->testController.notifyDone(); |
2989 | } |
2990 | |
2991 | void TestController::clearServiceWorkerRegistrations() |
2992 | { |
2993 | auto websiteDataStore = WKContextGetWebsiteDataStore(platformContext()); |
2994 | ClearServiceWorkerRegistrationsCallbackContext context(*this); |
2995 | |
2996 | WKWebsiteDataStoreRemoveAllServiceWorkerRegistrations(websiteDataStore, &context, clearServiceWorkerRegistrationsCallback); |
2997 | runUntil(context.done, noTimeout); |
2998 | } |
2999 | |
3000 | struct ClearDOMCacheCallbackContext { |
3001 | explicit ClearDOMCacheCallbackContext(TestController& controller) |
3002 | : testController(controller) |
3003 | { |
3004 | } |
3005 | |
3006 | TestController& testController; |
3007 | bool done { false }; |
3008 | }; |
3009 | |
3010 | static void clearDOMCacheCallback(void* userData) |
3011 | { |
3012 | auto* context = static_cast<ClearDOMCacheCallbackContext*>(userData); |
3013 | context->done = true; |
3014 | context->testController.notifyDone(); |
3015 | } |
3016 | |
3017 | void TestController::clearDOMCache(WKStringRef origin) |
3018 | { |
3019 | auto websiteDataStore = WKContextGetWebsiteDataStore(platformContext()); |
3020 | ClearDOMCacheCallbackContext context(*this); |
3021 | |
3022 | auto cacheOrigin = adoptWK(WKSecurityOriginCreateFromString(origin)); |
3023 | WKWebsiteDataStoreRemoveFetchCacheForOrigin(websiteDataStore, cacheOrigin.get(), &context, clearDOMCacheCallback); |
3024 | runUntil(context.done, noTimeout); |
3025 | } |
3026 | |
3027 | void TestController::clearDOMCaches() |
3028 | { |
3029 | auto websiteDataStore = WKContextGetWebsiteDataStore(platformContext()); |
3030 | ClearDOMCacheCallbackContext context(*this); |
3031 | |
3032 | WKWebsiteDataStoreRemoveAllFetchCaches(websiteDataStore, &context, clearDOMCacheCallback); |
3033 | runUntil(context.done, noTimeout); |
3034 | } |
3035 | |
3036 | void TestController::setIDBPerOriginQuota(uint64_t quota) |
3037 | { |
3038 | WKContextSetIDBPerOriginQuota(platformContext(), quota); |
3039 | } |
3040 | |
3041 | struct RemoveAllIndexedDatabasesCallbackContext { |
3042 | explicit RemoveAllIndexedDatabasesCallbackContext(TestController& controller) |
3043 | : testController(controller) |
3044 | { |
3045 | } |
3046 | |
3047 | TestController& testController; |
3048 | bool done { false }; |
3049 | }; |
3050 | |
3051 | static void RemoveAllIndexedDatabasesCallback(void* userData) |
3052 | { |
3053 | auto* context = static_cast<RemoveAllIndexedDatabasesCallbackContext*>(userData); |
3054 | context->done = true; |
3055 | context->testController.notifyDone(); |
3056 | } |
3057 | |
3058 | void TestController::ClearIndexedDatabases() |
3059 | { |
3060 | auto websiteDataStore = WKContextGetWebsiteDataStore(platformContext()); |
3061 | RemoveAllIndexedDatabasesCallbackContext context(*this); |
3062 | WKWebsiteDataStoreRemoveAllIndexedDatabases(websiteDataStore, &context, RemoveAllIndexedDatabasesCallback); |
3063 | runUntil(context.done, noTimeout); |
3064 | } |
3065 | |
3066 | struct FetchCacheOriginsCallbackContext { |
3067 | FetchCacheOriginsCallbackContext(TestController& controller, WKStringRef origin) |
3068 | : testController(controller) |
3069 | , origin(origin) |
3070 | { |
3071 | } |
3072 | |
3073 | TestController& testController; |
3074 | WKStringRef origin; |
3075 | |
3076 | bool done { false }; |
3077 | bool result { false }; |
3078 | }; |
3079 | |
3080 | static void fetchCacheOriginsCallback(WKArrayRef origins, void* userData) |
3081 | { |
3082 | auto* context = static_cast<FetchCacheOriginsCallbackContext*>(userData); |
3083 | context->done = true; |
3084 | |
3085 | auto size = WKArrayGetSize(origins); |
3086 | for (size_t index = 0; index < size && !context->result; ++index) { |
3087 | WKSecurityOriginRef securityOrigin = reinterpret_cast<WKSecurityOriginRef>(WKArrayGetItemAtIndex(origins, index)); |
3088 | if (WKStringIsEqual(context->origin, adoptWK(WKSecurityOriginCopyToString(securityOrigin)).get())) |
3089 | context->result = true; |
3090 | } |
3091 | context->testController.notifyDone(); |
3092 | } |
3093 | |
3094 | bool TestController::hasDOMCache(WKStringRef origin) |
3095 | { |
3096 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3097 | FetchCacheOriginsCallbackContext context(*this, origin); |
3098 | WKWebsiteDataStoreGetFetchCacheOrigins(dataStore, &context, fetchCacheOriginsCallback); |
3099 | runUntil(context.done, noTimeout); |
3100 | return context.result; |
3101 | } |
3102 | |
3103 | struct FetchCacheSizeForOriginCallbackContext { |
3104 | explicit FetchCacheSizeForOriginCallbackContext(TestController& controller) |
3105 | : testController(controller) |
3106 | { |
3107 | } |
3108 | |
3109 | TestController& testController; |
3110 | |
3111 | bool done { false }; |
3112 | uint64_t result { 0 }; |
3113 | }; |
3114 | |
3115 | static void fetchCacheSizeForOriginCallback(uint64_t size, void* userData) |
3116 | { |
3117 | auto* context = static_cast<FetchCacheSizeForOriginCallbackContext*>(userData); |
3118 | context->done = true; |
3119 | context->result = size; |
3120 | context->testController.notifyDone(); |
3121 | } |
3122 | |
3123 | uint64_t TestController::domCacheSize(WKStringRef origin) |
3124 | { |
3125 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3126 | FetchCacheSizeForOriginCallbackContext context(*this); |
3127 | WKWebsiteDataStoreGetFetchCacheSizeForOrigin(dataStore, origin, &context, fetchCacheSizeForOriginCallback); |
3128 | runUntil(context.done, noTimeout); |
3129 | return context.result; |
3130 | } |
3131 | |
3132 | #if !PLATFORM(COCOA) |
3133 | void TestController::setAllowStorageQuotaIncrease(bool) |
3134 | { |
3135 | // FIXME: To implement. |
3136 | } |
3137 | |
3138 | bool TestController::isDoingMediaCapture() const |
3139 | { |
3140 | return false; |
3141 | } |
3142 | |
3143 | #endif |
3144 | |
3145 | struct ResourceStatisticsCallbackContext { |
3146 | explicit ResourceStatisticsCallbackContext(TestController& controller) |
3147 | : testController(controller) |
3148 | { |
3149 | } |
3150 | |
3151 | TestController& testController; |
3152 | bool done { false }; |
3153 | bool result { false }; |
3154 | WKRetainPtr<WKStringRef> resourceLoadStatisticsRepresentation; |
3155 | }; |
3156 | |
3157 | static void resourceStatisticsStringResultCallback(WKStringRef resourceLoadStatisticsRepresentation, void* userData) |
3158 | { |
3159 | auto* context = static_cast<ResourceStatisticsCallbackContext*>(userData); |
3160 | context->resourceLoadStatisticsRepresentation = resourceLoadStatisticsRepresentation; |
3161 | context->done = true; |
3162 | context->testController.notifyDone(); |
3163 | } |
3164 | |
3165 | static void resourceStatisticsVoidResultCallback(void* userData) |
3166 | { |
3167 | auto* context = static_cast<ResourceStatisticsCallbackContext*>(userData); |
3168 | context->done = true; |
3169 | context->testController.notifyDone(); |
3170 | } |
3171 | |
3172 | static void resourceStatisticsBooleanResultCallback(bool result, void* userData) |
3173 | { |
3174 | auto* context = static_cast<ResourceStatisticsCallbackContext*>(userData); |
3175 | context->result = result; |
3176 | context->done = true; |
3177 | context->testController.notifyDone(); |
3178 | } |
3179 | |
3180 | void TestController::setStatisticsDebugMode(bool value) |
3181 | { |
3182 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3183 | ResourceStatisticsCallbackContext context(*this); |
3184 | WKWebsiteDataStoreSetResourceLoadStatisticsDebugModeWithCompletionHandler(dataStore, value, &context, resourceStatisticsVoidResultCallback); |
3185 | runUntil(context.done, noTimeout); |
3186 | m_currentInvocation->didSetStatisticsDebugMode(); |
3187 | } |
3188 | |
3189 | void TestController::setStatisticsPrevalentResourceForDebugMode(WKStringRef hostName) |
3190 | { |
3191 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3192 | ResourceStatisticsCallbackContext context(*this); |
3193 | WKWebsiteDataStoreSetResourceLoadStatisticsPrevalentResourceForDebugMode(dataStore, hostName, &context, resourceStatisticsVoidResultCallback); |
3194 | runUntil(context.done, noTimeout); |
3195 | m_currentInvocation->didSetPrevalentResourceForDebugMode(); |
3196 | } |
3197 | |
3198 | void TestController::setStatisticsLastSeen(WKStringRef host, double seconds) |
3199 | { |
3200 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3201 | ResourceStatisticsCallbackContext context(*this); |
3202 | WKWebsiteDataStoreSetStatisticsLastSeen(dataStore, host, seconds, &context, resourceStatisticsVoidResultCallback); |
3203 | runUntil(context.done, noTimeout); |
3204 | m_currentInvocation->didSetLastSeen(); |
3205 | } |
3206 | |
3207 | void TestController::setStatisticsPrevalentResource(WKStringRef host, bool value) |
3208 | { |
3209 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3210 | ResourceStatisticsCallbackContext context(*this); |
3211 | WKWebsiteDataStoreSetStatisticsPrevalentResource(dataStore, host, value, &context, resourceStatisticsVoidResultCallback); |
3212 | runUntil(context.done, noTimeout); |
3213 | m_currentInvocation->didSetPrevalentResource(); |
3214 | } |
3215 | |
3216 | void TestController::setStatisticsVeryPrevalentResource(WKStringRef host, bool value) |
3217 | { |
3218 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3219 | ResourceStatisticsCallbackContext context(*this); |
3220 | WKWebsiteDataStoreSetStatisticsVeryPrevalentResource(dataStore, host, value, &context, resourceStatisticsVoidResultCallback); |
3221 | runUntil(context.done, noTimeout); |
3222 | m_currentInvocation->didSetVeryPrevalentResource(); |
3223 | } |
3224 | |
3225 | String TestController::dumpResourceLoadStatistics() |
3226 | { |
3227 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3228 | ResourceStatisticsCallbackContext context(*this); |
3229 | WKWebsiteDataStoreDumpResourceLoadStatistics(dataStore, &context, resourceStatisticsStringResultCallback); |
3230 | runUntil(context.done, noTimeout); |
3231 | return toWTFString(context.resourceLoadStatisticsRepresentation.get()); |
3232 | } |
3233 | |
3234 | bool TestController::isStatisticsPrevalentResource(WKStringRef host) |
3235 | { |
3236 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3237 | ResourceStatisticsCallbackContext context(*this); |
3238 | WKWebsiteDataStoreIsStatisticsPrevalentResource(dataStore, host, &context, resourceStatisticsBooleanResultCallback); |
3239 | runUntil(context.done, noTimeout); |
3240 | return context.result; |
3241 | } |
3242 | |
3243 | bool TestController::isStatisticsVeryPrevalentResource(WKStringRef host) |
3244 | { |
3245 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3246 | ResourceStatisticsCallbackContext context(*this); |
3247 | WKWebsiteDataStoreIsStatisticsVeryPrevalentResource(dataStore, host, &context, resourceStatisticsBooleanResultCallback); |
3248 | runUntil(context.done, noTimeout); |
3249 | return context.result; |
3250 | } |
3251 | |
3252 | bool TestController::isStatisticsRegisteredAsSubresourceUnder(WKStringRef subresourceHost, WKStringRef topFrameHost) |
3253 | { |
3254 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3255 | ResourceStatisticsCallbackContext context(*this); |
3256 | WKWebsiteDataStoreIsStatisticsRegisteredAsSubresourceUnder(dataStore, subresourceHost, topFrameHost, &context, resourceStatisticsBooleanResultCallback); |
3257 | runUntil(context.done, noTimeout); |
3258 | return context.result; |
3259 | } |
3260 | |
3261 | bool TestController::isStatisticsRegisteredAsSubFrameUnder(WKStringRef subFrameHost, WKStringRef topFrameHost) |
3262 | { |
3263 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3264 | ResourceStatisticsCallbackContext context(*this); |
3265 | WKWebsiteDataStoreIsStatisticsRegisteredAsSubFrameUnder(dataStore, subFrameHost, topFrameHost, &context, resourceStatisticsBooleanResultCallback); |
3266 | runUntil(context.done, noTimeout); |
3267 | return context.result; |
3268 | } |
3269 | |
3270 | bool TestController::isStatisticsRegisteredAsRedirectingTo(WKStringRef hostRedirectedFrom, WKStringRef hostRedirectedTo) |
3271 | { |
3272 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3273 | ResourceStatisticsCallbackContext context(*this); |
3274 | WKWebsiteDataStoreIsStatisticsRegisteredAsRedirectingTo(dataStore, hostRedirectedFrom, hostRedirectedTo, &context, resourceStatisticsBooleanResultCallback); |
3275 | runUntil(context.done, noTimeout); |
3276 | return context.result; |
3277 | } |
3278 | |
3279 | void TestController::setStatisticsHasHadUserInteraction(WKStringRef host, bool value) |
3280 | { |
3281 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3282 | ResourceStatisticsCallbackContext context(*this); |
3283 | WKWebsiteDataStoreSetStatisticsHasHadUserInteraction(dataStore, host, value, &context, resourceStatisticsVoidResultCallback); |
3284 | runUntil(context.done, noTimeout); |
3285 | m_currentInvocation->didSetHasHadUserInteraction(); |
3286 | } |
3287 | |
3288 | bool TestController::isStatisticsHasHadUserInteraction(WKStringRef host) |
3289 | { |
3290 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3291 | ResourceStatisticsCallbackContext context(*this); |
3292 | WKWebsiteDataStoreIsStatisticsHasHadUserInteraction(dataStore, host, &context, resourceStatisticsBooleanResultCallback); |
3293 | runUntil(context.done, noTimeout); |
3294 | return context.result; |
3295 | } |
3296 | |
3297 | void TestController::setStatisticsGrandfathered(WKStringRef host, bool value) |
3298 | { |
3299 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3300 | WKWebsiteDataStoreSetStatisticsGrandfathered(dataStore, host, value); |
3301 | } |
3302 | |
3303 | bool TestController::isStatisticsGrandfathered(WKStringRef host) |
3304 | { |
3305 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3306 | ResourceStatisticsCallbackContext context(*this); |
3307 | WKWebsiteDataStoreIsStatisticsGrandfathered(dataStore, host, &context, resourceStatisticsBooleanResultCallback); |
3308 | runUntil(context.done, noTimeout); |
3309 | return context.result; |
3310 | } |
3311 | |
3312 | void TestController::setStatisticsSubframeUnderTopFrameOrigin(WKStringRef host, WKStringRef topFrameHost) |
3313 | { |
3314 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3315 | WKWebsiteDataStoreSetStatisticsSubframeUnderTopFrameOrigin(dataStore, host, topFrameHost); |
3316 | } |
3317 | |
3318 | void TestController::setStatisticsSubresourceUnderTopFrameOrigin(WKStringRef host, WKStringRef topFrameHost) |
3319 | { |
3320 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3321 | WKWebsiteDataStoreSetStatisticsSubresourceUnderTopFrameOrigin(dataStore, host, topFrameHost); |
3322 | } |
3323 | |
3324 | void TestController::setStatisticsSubresourceUniqueRedirectTo(WKStringRef host, WKStringRef hostRedirectedTo) |
3325 | { |
3326 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3327 | WKWebsiteDataStoreSetStatisticsSubresourceUniqueRedirectTo(dataStore, host, hostRedirectedTo); |
3328 | } |
3329 | |
3330 | void TestController::setStatisticsSubresourceUniqueRedirectFrom(WKStringRef host, WKStringRef hostRedirectedFrom) |
3331 | { |
3332 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3333 | WKWebsiteDataStoreSetStatisticsSubresourceUniqueRedirectFrom(dataStore, host, hostRedirectedFrom); |
3334 | } |
3335 | |
3336 | void TestController::setStatisticsTopFrameUniqueRedirectTo(WKStringRef host, WKStringRef hostRedirectedTo) |
3337 | { |
3338 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3339 | WKWebsiteDataStoreSetStatisticsTopFrameUniqueRedirectTo(dataStore, host, hostRedirectedTo); |
3340 | } |
3341 | |
3342 | void TestController::setStatisticsTopFrameUniqueRedirectFrom(WKStringRef host, WKStringRef hostRedirectedFrom) |
3343 | { |
3344 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3345 | WKWebsiteDataStoreSetStatisticsTopFrameUniqueRedirectFrom(dataStore, host, hostRedirectedFrom); |
3346 | } |
3347 | |
3348 | void TestController::setStatisticsCrossSiteLoadWithLinkDecoration(WKStringRef fromHost, WKStringRef toHost) |
3349 | { |
3350 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3351 | ResourceStatisticsCallbackContext context(*this); |
3352 | WKWebsiteDataStoreSetStatisticsCrossSiteLoadWithLinkDecoration(dataStore, fromHost, toHost, &context, resourceStatisticsVoidResultCallback); |
3353 | runUntil(context.done, noTimeout); |
3354 | } |
3355 | |
3356 | void TestController::setStatisticsTimeToLiveUserInteraction(double seconds) |
3357 | { |
3358 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3359 | ResourceStatisticsCallbackContext context(*this); |
3360 | WKWebsiteDataStoreSetStatisticsTimeToLiveUserInteraction(dataStore, seconds, &context, resourceStatisticsVoidResultCallback); |
3361 | runUntil(context.done, noTimeout); |
3362 | } |
3363 | |
3364 | void TestController::statisticsProcessStatisticsAndDataRecords() |
3365 | { |
3366 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3367 | ResourceStatisticsCallbackContext context(*this); |
3368 | WKWebsiteDataStoreStatisticsProcessStatisticsAndDataRecords(dataStore, &context, resourceStatisticsVoidResultCallback); |
3369 | runUntil(context.done, noTimeout); |
3370 | } |
3371 | |
3372 | void TestController::statisticsUpdateCookieBlocking() |
3373 | { |
3374 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3375 | ResourceStatisticsCallbackContext context(*this); |
3376 | WKWebsiteDataStoreStatisticsUpdateCookieBlocking(dataStore, &context, resourceStatisticsVoidResultCallback); |
3377 | runUntil(context.done, noTimeout); |
3378 | m_currentInvocation->didSetBlockCookiesForHost(); |
3379 | } |
3380 | |
3381 | void TestController::statisticsSubmitTelemetry() |
3382 | { |
3383 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3384 | WKWebsiteDataStoreStatisticsSubmitTelemetry(dataStore); |
3385 | } |
3386 | |
3387 | void TestController::setStatisticsNotifyPagesWhenDataRecordsWereScanned(bool value) |
3388 | { |
3389 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3390 | WKWebsiteDataStoreSetStatisticsNotifyPagesWhenDataRecordsWereScanned(dataStore, value); |
3391 | } |
3392 | |
3393 | void TestController::setStatisticsIsRunningTest(bool value) |
3394 | { |
3395 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3396 | ResourceStatisticsCallbackContext context(*this); |
3397 | WKWebsiteDataStoreSetStatisticsIsRunningTest(dataStore, value, &context, resourceStatisticsVoidResultCallback); |
3398 | runUntil(context.done, noTimeout); |
3399 | } |
3400 | |
3401 | void TestController::setStatisticsShouldClassifyResourcesBeforeDataRecordsRemoval(bool value) |
3402 | { |
3403 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3404 | WKWebsiteDataStoreSetStatisticsShouldClassifyResourcesBeforeDataRecordsRemoval(dataStore, value); |
3405 | } |
3406 | |
3407 | void TestController::setStatisticsNotifyPagesWhenTelemetryWasCaptured(bool value) |
3408 | { |
3409 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3410 | WKWebsiteDataStoreSetStatisticsNotifyPagesWhenTelemetryWasCaptured(dataStore, value); |
3411 | } |
3412 | |
3413 | void TestController::setStatisticsMinimumTimeBetweenDataRecordsRemoval(double seconds) |
3414 | { |
3415 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3416 | WKWebsiteDataStoreSetStatisticsMinimumTimeBetweenDataRecordsRemoval(dataStore, seconds); |
3417 | } |
3418 | |
3419 | void TestController::setStatisticsGrandfatheringTime(double seconds) |
3420 | { |
3421 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3422 | WKWebsiteDataStoreSetStatisticsGrandfatheringTime(dataStore, seconds); |
3423 | } |
3424 | |
3425 | void TestController::setStatisticsMaxStatisticsEntries(unsigned entries) |
3426 | { |
3427 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3428 | WKWebsiteDataStoreSetStatisticsMaxStatisticsEntries(dataStore, entries); |
3429 | } |
3430 | |
3431 | void TestController::setStatisticsPruneEntriesDownTo(unsigned entries) |
3432 | { |
3433 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3434 | WKWebsiteDataStoreSetStatisticsPruneEntriesDownTo(dataStore, entries); |
3435 | } |
3436 | |
3437 | void TestController::statisticsClearInMemoryAndPersistentStore() |
3438 | { |
3439 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3440 | ResourceStatisticsCallbackContext context(*this); |
3441 | WKWebsiteDataStoreStatisticsClearInMemoryAndPersistentStore(dataStore, &context, resourceStatisticsVoidResultCallback); |
3442 | runUntil(context.done, noTimeout); |
3443 | m_currentInvocation->didClearStatisticsThroughWebsiteDataRemoval(); |
3444 | } |
3445 | |
3446 | void TestController::statisticsClearInMemoryAndPersistentStoreModifiedSinceHours(unsigned hours) |
3447 | { |
3448 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3449 | ResourceStatisticsCallbackContext context(*this); |
3450 | WKWebsiteDataStoreStatisticsClearInMemoryAndPersistentStoreModifiedSinceHours(dataStore, hours, &context, resourceStatisticsVoidResultCallback); |
3451 | runUntil(context.done, noTimeout); |
3452 | m_currentInvocation->didClearStatisticsThroughWebsiteDataRemoval(); |
3453 | } |
3454 | |
3455 | void TestController::statisticsClearThroughWebsiteDataRemoval() |
3456 | { |
3457 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3458 | ResourceStatisticsCallbackContext context(*this); |
3459 | WKWebsiteDataStoreStatisticsClearThroughWebsiteDataRemoval(dataStore, &context, resourceStatisticsVoidResultCallback); |
3460 | runUntil(context.done, noTimeout); |
3461 | m_currentInvocation->didClearStatisticsThroughWebsiteDataRemoval(); |
3462 | } |
3463 | |
3464 | void TestController::statisticsDeleteCookiesForHost(WKStringRef host, bool includeHttpOnlyCookies) |
3465 | { |
3466 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3467 | ResourceStatisticsCallbackContext context(*this); |
3468 | WKWebsiteDataStoreStatisticsDeleteCookiesForTesting(dataStore, host, includeHttpOnlyCookies, &context, resourceStatisticsVoidResultCallback); |
3469 | runUntil(context.done, noTimeout); |
3470 | } |
3471 | |
3472 | bool TestController::isStatisticsHasLocalStorage(WKStringRef host) |
3473 | { |
3474 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3475 | ResourceStatisticsCallbackContext context(*this); |
3476 | WKWebsiteDataStoreStatisticsHasLocalStorage(dataStore, host, &context, resourceStatisticsBooleanResultCallback); |
3477 | runUntil(context.done, noTimeout); |
3478 | return context.result; |
3479 | } |
3480 | |
3481 | void TestController::setStatisticsCacheMaxAgeCap(double seconds) |
3482 | { |
3483 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3484 | ResourceStatisticsCallbackContext context(*this); |
3485 | WKWebsiteDataStoreSetStatisticsCacheMaxAgeCap(dataStore, seconds, &context, resourceStatisticsVoidResultCallback); |
3486 | runUntil(context.done, noTimeout); |
3487 | } |
3488 | |
3489 | void TestController::statisticsResetToConsistentState() |
3490 | { |
3491 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3492 | ResourceStatisticsCallbackContext context(*this); |
3493 | WKWebsiteDataStoreStatisticsResetToConsistentState(dataStore, &context, resourceStatisticsVoidResultCallback); |
3494 | runUntil(context.done, noTimeout); |
3495 | m_currentInvocation->didResetStatisticsToConsistentState(); |
3496 | } |
3497 | |
3498 | void TestController::addMockMediaDevice(WKStringRef persistentID, WKStringRef label, WKStringRef type) |
3499 | { |
3500 | WKAddMockMediaDevice(platformContext(), persistentID, label, type); |
3501 | } |
3502 | |
3503 | void TestController::clearMockMediaDevices() |
3504 | { |
3505 | WKClearMockMediaDevices(platformContext()); |
3506 | } |
3507 | |
3508 | void TestController::removeMockMediaDevice(WKStringRef persistentID) |
3509 | { |
3510 | WKRemoveMockMediaDevice(platformContext(), persistentID); |
3511 | } |
3512 | |
3513 | void TestController::resetMockMediaDevices() |
3514 | { |
3515 | WKResetMockMediaDevices(platformContext()); |
3516 | } |
3517 | |
3518 | #if !PLATFORM(COCOA) |
3519 | void TestController::platformAddTestOptions(TestOptions&) const |
3520 | { |
3521 | } |
3522 | |
3523 | void TestController::injectUserScript(WKStringRef) |
3524 | { |
3525 | } |
3526 | |
3527 | void TestController::addTestKeyToKeychain(const String&, const String&, const String&) |
3528 | { |
3529 | } |
3530 | |
3531 | void TestController::cleanUpKeychain(const String&) |
3532 | { |
3533 | } |
3534 | |
3535 | bool TestController::keyExistsInKeychain(const String&, const String&) |
3536 | { |
3537 | return false; |
3538 | } |
3539 | |
3540 | bool TestController::canDoServerTrustEvaluationInNetworkProcess() const |
3541 | { |
3542 | return false; |
3543 | } |
3544 | |
3545 | void TestController::(const String&, bool) |
3546 | { |
3547 | } |
3548 | |
3549 | void TestController::(const Vector<String>&) |
3550 | { |
3551 | } |
3552 | |
3553 | #endif |
3554 | |
3555 | void TestController::sendDisplayConfigurationChangedMessageForTesting() |
3556 | { |
3557 | WKSendDisplayConfigurationChangedMessageForTesting(platformContext()); |
3558 | } |
3559 | |
3560 | void TestController::setWebAuthenticationMockConfiguration(WKDictionaryRef configuration) |
3561 | { |
3562 | WKWebsiteDataStoreSetWebAuthenticationMockConfiguration(WKContextGetWebsiteDataStore(platformContext()), configuration); |
3563 | } |
3564 | |
3565 | struct AdClickAttributionStringResultCallbackContext { |
3566 | explicit AdClickAttributionStringResultCallbackContext(TestController& controller) |
3567 | : testController(controller) |
3568 | { |
3569 | } |
3570 | |
3571 | TestController& testController; |
3572 | bool done { false }; |
3573 | WKRetainPtr<WKStringRef> adClickAttributionRepresentation; |
3574 | }; |
3575 | |
3576 | static void adClickAttributionStringResultCallback(WKStringRef adClickAttributionRepresentation, void* userData) |
3577 | { |
3578 | auto* context = static_cast<AdClickAttributionStringResultCallbackContext*>(userData); |
3579 | context->adClickAttributionRepresentation = adClickAttributionRepresentation; |
3580 | context->done = true; |
3581 | context->testController.notifyDone(); |
3582 | } |
3583 | |
3584 | String TestController::dumpAdClickAttribution() |
3585 | { |
3586 | AdClickAttributionStringResultCallbackContext callbackContext(*this); |
3587 | WKPageDumpAdClickAttribution(m_mainWebView->page(), adClickAttributionStringResultCallback, &callbackContext); |
3588 | runUntil(callbackContext.done, noTimeout); |
3589 | return toWTFString(callbackContext.adClickAttributionRepresentation.get()); |
3590 | } |
3591 | |
3592 | struct AdClickAttributionVoidCallbackContext { |
3593 | explicit AdClickAttributionVoidCallbackContext(TestController& controller) |
3594 | : testController(controller) |
3595 | { |
3596 | } |
3597 | |
3598 | TestController& testController; |
3599 | bool done { false }; |
3600 | }; |
3601 | |
3602 | static void adClickAttributionVoidCallback(void* userData) |
3603 | { |
3604 | auto* context = static_cast<AdClickAttributionVoidCallbackContext*>(userData); |
3605 | context->done = true; |
3606 | context->testController.notifyDone(); |
3607 | } |
3608 | |
3609 | void TestController::clearAdClickAttribution() |
3610 | { |
3611 | AdClickAttributionVoidCallbackContext callbackContext(*this); |
3612 | WKPageClearAdClickAttribution(m_mainWebView->page(), adClickAttributionVoidCallback, &callbackContext); |
3613 | runUntil(callbackContext.done, noTimeout); |
3614 | } |
3615 | |
3616 | void TestController::clearAdClickAttributionsThroughWebsiteDataRemoval() |
3617 | { |
3618 | auto* dataStore = WKContextGetWebsiteDataStore(platformContext()); |
3619 | AdClickAttributionVoidCallbackContext callbackContext(*this); |
3620 | WKWebsiteDataStoreClearAdClickAttributionsThroughWebsiteDataRemoval(dataStore, &callbackContext, adClickAttributionVoidCallback); |
3621 | runUntil(callbackContext.done, noTimeout); |
3622 | } |
3623 | |
3624 | void TestController::setAdClickAttributionOverrideTimerForTesting(bool value) |
3625 | { |
3626 | AdClickAttributionVoidCallbackContext callbackContext(*this); |
3627 | WKPageSetAdClickAttributionOverrideTimerForTesting(m_mainWebView->page(), value, adClickAttributionVoidCallback, &callbackContext); |
3628 | runUntil(callbackContext.done, noTimeout); |
3629 | } |
3630 | |
3631 | void TestController::setAdClickAttributionConversionURLForTesting(WKURLRef url) |
3632 | { |
3633 | AdClickAttributionVoidCallbackContext callbackContext(*this); |
3634 | WKPageSetAdClickAttributionConversionURLForTesting(m_mainWebView->page(), url, adClickAttributionVoidCallback, &callbackContext); |
3635 | runUntil(callbackContext.done, noTimeout); |
3636 | } |
3637 | |
3638 | void TestController::markAdClickAttributionsAsExpiredForTesting() |
3639 | { |
3640 | AdClickAttributionVoidCallbackContext callbackContext(*this); |
3641 | WKPageMarkAdClickAttributionsAsExpiredForTesting(m_mainWebView->page(), adClickAttributionVoidCallback, &callbackContext); |
3642 | runUntil(callbackContext.done, noTimeout); |
3643 | } |
3644 | |
3645 | } // namespace WTR |
3646 | |