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
99namespace WTR {
100
101#if OS(WINDOWS)
102static constexpr auto pathSeparator = '\\';
103#else
104static constexpr auto pathSeparator = '/';
105#endif
106
107const unsigned TestController::viewWidth = 800;
108const unsigned TestController::viewHeight = 600;
109
110const unsigned TestController::w3cSVGViewWidth = 480;
111const unsigned TestController::w3cSVGViewHeight = 360;
112
113const WTF::Seconds TestController::defaultShortTimeout = 5_s;
114const WTF::Seconds TestController::noTimeout = -1_s;
115
116static WKURLRef blankURL()
117{
118 static WKURLRef staticBlankURL = WKURLCreateWithUTF8CString("about:blank");
119 return staticBlankURL;
120}
121
122static 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
128static 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
134AsyncTask* AsyncTask::m_currentTask;
135
136bool 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
145AsyncTask* AsyncTask::currentTask()
146{
147 return m_currentTask;
148}
149
150static TestController* controller;
151
152TestController& TestController::singleton()
153{
154 ASSERT(controller);
155 return *controller;
156}
157
158TestController::TestController(int argc, const char* argv[])
159{
160 initialize(argc, argv);
161 controller = this;
162 run();
163 controller = nullptr;
164}
165
166TestController::~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
175static WKRect getWindowFrame(WKPageRef page, const void* clientInfo)
176{
177 PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
178 return view->windowFrame();
179}
180
181static 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
187static 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
193static 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
229void 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
237static 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
244static 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
251static void unfocus(WKPageRef page, const void* clientInfo)
252{
253 PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
254 view->setWindowIsKey(false);
255}
256
257static void decidePolicyForGeolocationPermissionRequest(WKPageRef, WKFrameRef, WKSecurityOriginRef, WKGeolocationPermissionRequestRef permissionRequest, const void* clientInfo)
258{
259 TestController::singleton().handleGeolocationPermissionRequest(permissionRequest);
260}
261
262static 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
267static void runJavaScriptAlert(WKPageRef page, WKStringRef alertText, WKFrameRef frame, WKSecurityOriginRef securityOrigin, WKPageRunJavaScriptAlertResultListenerRef listener, const void *clientInfo)
268{
269 TestController::singleton().handleJavaScriptAlert(listener);
270}
271
272static void checkUserMediaPermissionForOrigin(WKPageRef, WKFrameRef frame, WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin, WKUserMediaPermissionCheckRef checkRequest, const void*)
273{
274 TestController::singleton().handleCheckOfUserMediaPermissionForOrigin(frame, userMediaDocumentOrigin, topLevelDocumentOrigin, checkRequest);
275}
276
277static void requestPointerLock(WKPageRef page, const void*)
278{
279 WKPageDidAllowPointerLock(page);
280}
281
282static void printFrame(WKPageRef page, WKFrameRef frame, const void*)
283{
284 WKPageBeginPrinting(page, frame, WKPrintInfo { 1, 21, 29.7f });
285 WKPageEndPrinting(page);
286}
287
288static bool shouldAllowDeviceOrientationAndMotionAccess(WKPageRef, WKSecurityOriginRef origin, const void*)
289{
290 return TestController::singleton().handleDeviceOrientationAndMotionAccessRequest(origin);
291}
292
293WKPageRef 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
299WKPageRef 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
418const 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
429void 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
488WKRetainPtr<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
520WKRetainPtr<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, &notificationKit.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
594void 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
741void 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
764void 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
893bool 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
1054void 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
1066void 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
1078void 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
1090void 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
1113void 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
1140void TestController::willDestroyWebView()
1141{
1142 // Before we kill the web view, look for abandoned documents before that web process goes away.
1143 checkForWorldLeaks();
1144}
1145
1146void TestController::terminateWebContentProcess()
1147{
1148 WKPageTerminate(m_mainWebView->page());
1149}
1150
1151void 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
1160const 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
1176const 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
1192void TestController::setAllowsAnySSLCertificate(bool allows)
1193{
1194 WKContextSetAllowsAnySSLCertificateForWebSocketTesting(platformContext(), allows);
1195}
1196
1197static 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
1215static 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
1253static bool parseBooleanTestHeaderValue(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
1264static std::string parseStringTestHeaderValueAsRelativePath(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
1271static void updateTestOptionsFromTestHeader(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
1406TestOptions 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
1420void 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
1432void TestController::updateWindowScaleForTest(PlatformWebView* view, const TestInvocation& test)
1433{
1434 view->changeWindowScaleIfNeeded(test.options().deviceScaleFactor);
1435}
1436
1437void 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)
1447struct 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
1459static 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
1468static 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)
1483void 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
1520void 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)
1545void TestController::configureContentExtensionForTest(const TestInvocation&)
1546{
1547}
1548
1549void TestController::resetContentExtensions()
1550{
1551}
1552#endif // ENABLE(CONTENT_EXTENSIONS)
1553#endif // !PLATFORM(COCOA)
1554
1555class CommandTokenizer {
1556public:
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
1567private:
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
1575void 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
1587std::string CommandTokenizer::next()
1588{
1589 ASSERT(hasNext());
1590
1591 std::string oldNext = m_next;
1592 pump();
1593 return oldNext;
1594}
1595
1596bool CommandTokenizer::hasNext() const
1597{
1598 return !m_next.empty();
1599}
1600
1601NO_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
1607static 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
1635bool 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
1665bool 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
1673bool 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
1691void 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
1710void 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
1724void 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
1734void 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
1739void 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
1744WKTypeRef TestController::getInjectedBundleInitializationUserData(WKContextRef, const void* clientInfo)
1745{
1746 return static_cast<TestController*>(const_cast<void*>(clientInfo))->getInjectedBundleInitializationUserData().leakRef();
1747}
1748
1749// WKPageInjectedBundleClient
1750
1751void 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
1756void 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
1761void TestController::networkProcessDidCrash(WKContextRef context, const void *clientInfo)
1762{
1763 static_cast<TestController*>(const_cast<void*>(clientInfo))->networkProcessDidCrash();
1764}
1765
1766void 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
1780void 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
1816void 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
1895WKRetainPtr<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
2085WKRetainPtr<WKTypeRef> TestController::getInjectedBundleInitializationUserData()
2086{
2087 return nullptr;
2088}
2089
2090// WKContextClient
2091
2092void 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
2101void TestController::didCommitNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef, const void* clientInfo)
2102{
2103 static_cast<TestController*>(const_cast<void*>(clientInfo))->didCommitNavigation(page, navigation);
2104}
2105
2106void TestController::didFinishNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef, const void* clientInfo)
2107{
2108 static_cast<TestController*>(const_cast<void*>(clientInfo))->didFinishNavigation(page, navigation);
2109}
2110
2111void 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
2116bool TestController::canAuthenticateAgainstProtectionSpace(WKPageRef page, WKProtectionSpaceRef protectionSpace, const void* clientInfo)
2117{
2118 return static_cast<TestController*>(const_cast<void*>(clientInfo))->canAuthenticateAgainstProtectionSpace(page, protectionSpace);
2119}
2120
2121void TestController::didReceiveAuthenticationChallenge(WKPageRef page, WKAuthenticationChallengeRef authenticationChallenge, const void *clientInfo)
2122{
2123 static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveAuthenticationChallenge(page, /*frame,*/ authenticationChallenge);
2124}
2125
2126void TestController::processDidCrash(WKPageRef page, const void* clientInfo)
2127{
2128 static_cast<TestController*>(const_cast<void*>(clientInfo))->processDidCrash();
2129}
2130
2131void TestController::didBeginNavigationGesture(WKPageRef page, const void *clientInfo)
2132{
2133 static_cast<TestController*>(const_cast<void*>(clientInfo))->didBeginNavigationGesture(page);
2134}
2135
2136void TestController::willEndNavigationGesture(WKPageRef page, WKBackForwardListItemRef backForwardListItem, const void *clientInfo)
2137{
2138 static_cast<TestController*>(const_cast<void*>(clientInfo))->willEndNavigationGesture(page, backForwardListItem);
2139}
2140
2141void TestController::didEndNavigationGesture(WKPageRef page, WKBackForwardListItemRef backForwardListItem, const void *clientInfo)
2142{
2143 static_cast<TestController*>(const_cast<void*>(clientInfo))->didEndNavigationGesture(page, backForwardListItem);
2144}
2145
2146void TestController::didRemoveNavigationGestureSnapshot(WKPageRef page, const void *clientInfo)
2147{
2148 static_cast<TestController*>(const_cast<void*>(clientInfo))->didRemoveNavigationGestureSnapshot(page);
2149}
2150
2151WKPluginLoadPolicy 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
2156WKPluginLoadPolicy 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
2178void 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
2193void 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
2231void TestController::didCommitNavigation(WKPageRef page, WKNavigationRef navigation)
2232{
2233 mainWebView()->focus();
2234}
2235
2236void TestController::didReceiveServerRedirectForProvisionalNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef userData)
2237{
2238 m_didReceiveServerRedirectForProvisionalNavigation = true;
2239 return;
2240}
2241
2242static 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
2268bool 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
2282void 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
2295void 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
2340void TestController::downloadDidStart(WKContextRef context, WKDownloadRef download, const void* clientInfo)
2341{
2342 static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidStart(context, download);
2343}
2344
2345WKStringRef 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
2350void TestController::downloadDidFinish(WKContextRef context, WKDownloadRef download, const void* clientInfo)
2351{
2352 static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidFinish(context, download);
2353}
2354
2355void 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
2360void TestController::downloadDidCancel(WKContextRef context, WKDownloadRef download, const void* clientInfo)
2361{
2362 static_cast<TestController*>(const_cast<void*>(clientInfo))->downloadDidCancel(context, download);
2363}
2364
2365void 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
2370void TestController::downloadDidStart(WKContextRef context, WKDownloadRef download)
2371{
2372 if (m_shouldLogDownloadCallbacks)
2373 m_currentInvocation->outputText("Download started.\n");
2374}
2375
2376WKStringRef 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
2400void TestController::downloadDidFinish(WKContextRef, WKDownloadRef)
2401{
2402 if (m_shouldLogDownloadCallbacks)
2403 m_currentInvocation->outputText("Download completed.\n");
2404 m_currentInvocation->notifyDownloadDone();
2405}
2406
2407void 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
2419void 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
2442void TestController::downloadDidCancel(WKContextRef, WKDownloadRef)
2443{
2444 if (m_shouldLogDownloadCallbacks)
2445 m_currentInvocation->outputText("Download cancelled.\n");
2446 m_currentInvocation->notifyDownloadDone();
2447}
2448
2449void 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
2464void TestController::didBeginNavigationGesture(WKPageRef)
2465{
2466 m_currentInvocation->didBeginSwipe();
2467}
2468
2469void TestController::willEndNavigationGesture(WKPageRef, WKBackForwardListItemRef)
2470{
2471 m_currentInvocation->willEndSwipe();
2472}
2473
2474void TestController::didEndNavigationGesture(WKPageRef, WKBackForwardListItemRef)
2475{
2476 m_currentInvocation->didEndSwipe();
2477}
2478
2479void TestController::didRemoveNavigationGestureSnapshot(WKPageRef)
2480{
2481 m_currentInvocation->didRemoveSwipeSnapshot();
2482}
2483
2484void TestController::simulateWebNotificationClick(uint64_t notificationID)
2485{
2486 m_webNotificationProvider.simulateWebNotificationClick(mainWebView()->page(), notificationID);
2487}
2488
2489void TestController::setGeolocationPermission(bool enabled)
2490{
2491 m_isGeolocationPermissionSet = true;
2492 m_isGeolocationPermissionAllowed = enabled;
2493 decidePolicyForGeolocationPermissionRequestIfPossible();
2494}
2495
2496void 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
2501void TestController::setMockGeolocationPositionUnavailableError(WKStringRef errorMessage)
2502{
2503 m_geolocationProvider->setPositionUnavailableError(errorMessage);
2504}
2505
2506void TestController::handleGeolocationPermissionRequest(WKGeolocationPermissionRequestRef geolocationPermissionRequest)
2507{
2508 m_geolocationPermissionRequests.append(geolocationPermissionRequest);
2509 decidePolicyForGeolocationPermissionRequestIfPossible();
2510}
2511
2512bool TestController::isGeolocationProviderActive() const
2513{
2514 return m_geolocationProvider->isActive();
2515}
2516
2517static 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
2534static 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
2545static 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
2555void TestController::setUserMediaPermission(bool enabled)
2556{
2557 m_isUserMediaPermissionSet = true;
2558 m_isUserMediaPermissionAllowed = enabled;
2559 decidePolicyForUserMediaPermissionRequestIfPossible();
2560}
2561
2562void TestController::resetUserMediaPermission()
2563{
2564 m_isUserMediaPermissionSet = false;
2565}
2566
2567void TestController::setShouldDismissJavaScriptAlertsAsynchronously(bool value)
2568{
2569 m_shouldDismissJavaScriptAlertsAsynchronously = value;
2570}
2571
2572void 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
2586class OriginSettings : public RefCounted<OriginSettings> {
2587public:
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
2604private:
2605 HashMap<uint64_t, String> m_ephemeralSalts;
2606 String m_persistentSalt;
2607 unsigned m_requestCount { 0 };
2608 bool m_persistentPermission { false };
2609};
2610
2611String 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
2638void 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
2645void 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
2654bool 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
2660void 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
2667OriginSettings& 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
2678unsigned TestController::userMediaPermissionRequestCountForOrigin(WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString)
2679{
2680 auto originHash = userMediaOriginHash(userMediaDocumentOriginString, topLevelDocumentOriginString);
2681 return settingsForOrigin(originHash).requestCount();
2682}
2683
2684void TestController::resetUserMediaPermissionRequestCountForOrigin(WKStringRef userMediaDocumentOriginString, WKStringRef topLevelDocumentOriginString)
2685{
2686 auto originHash = userMediaOriginHash(userMediaDocumentOriginString, topLevelDocumentOriginString);
2687 settingsForOrigin(originHash).resetRequestCount();
2688}
2689
2690void 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
2732void TestController::setCustomPolicyDelegate(bool enabled, bool permissive)
2733{
2734 m_policyDelegateEnabled = enabled;
2735 m_policyDelegatePermissive = permissive;
2736}
2737
2738void 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
2753void TestController::decidePolicyForNotificationPermissionRequest(WKPageRef page, WKSecurityOriginRef origin, WKNotificationPermissionRequestRef request, const void*)
2754{
2755 TestController::singleton().decidePolicyForNotificationPermissionRequest(page, origin, request);
2756}
2757
2758void TestController::decidePolicyForNotificationPermissionRequest(WKPageRef, WKSecurityOriginRef, WKNotificationPermissionRequestRef request)
2759{
2760 WKNotificationPermissionRequestAllow(request);
2761}
2762
2763void TestController::unavailablePluginButtonClicked(WKPageRef, WKPluginUnavailabilityReason, WKDictionaryRef, const void*)
2764{
2765 printf("MISSING PLUGIN BUTTON PRESSED\n");
2766}
2767
2768void 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
2773void 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
2793void 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
2798void 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
2824void 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
2829void 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
2851void 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
2856void 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
2870void 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
2875void 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
2889void 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
2894void 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
2906void TestController::setNavigationGesturesEnabled(bool value)
2907{
2908 m_mainWebView->setNavigationGesturesEnabled(value);
2909}
2910
2911void TestController::setIgnoresViewportScaleLimits(bool ignoresViewportScaleLimits)
2912{
2913 WKPageSetIgnoresViewportScaleLimits(m_mainWebView->page(), ignoresViewportScaleLimits);
2914}
2915
2916void TestController::terminateNetworkProcess()
2917{
2918 WKContextTerminateNetworkProcess(platformContext());
2919}
2920
2921void TestController::terminateServiceWorkerProcess()
2922{
2923 WKContextTerminateServiceWorkerProcess(platformContext());
2924}
2925
2926#if !PLATFORM(COCOA)
2927void TestController::platformWillRunTest(const TestInvocation&)
2928{
2929}
2930
2931void TestController::platformCreateWebView(WKPageConfigurationRef configuration, const TestOptions& options)
2932{
2933 m_mainWebView = std::make_unique<PlatformWebView>(configuration, options);
2934}
2935
2936PlatformWebView* TestController::platformCreateOtherPage(PlatformWebView* parentView, WKPageConfigurationRef configuration, const TestOptions& options)
2937{
2938 return new PlatformWebView(configuration, options);
2939}
2940
2941WKContextRef 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
2955void TestController::platformResetStateToConsistentValues(const TestOptions&)
2956{
2957}
2958
2959unsigned TestController::imageCountInGeneralPasteboard() const
2960{
2961 return 0;
2962}
2963
2964void TestController::removeAllSessionCredentials()
2965{
2966}
2967
2968void TestController::getAllStorageAccessEntries()
2969{
2970}
2971
2972#endif
2973
2974struct ClearServiceWorkerRegistrationsCallbackContext {
2975 explicit ClearServiceWorkerRegistrationsCallbackContext(TestController& controller)
2976 : testController(controller)
2977 {
2978 }
2979
2980 TestController& testController;
2981 bool done { false };
2982};
2983
2984static void clearServiceWorkerRegistrationsCallback(void* userData)
2985{
2986 auto* context = static_cast<ClearServiceWorkerRegistrationsCallbackContext*>(userData);
2987 context->done = true;
2988 context->testController.notifyDone();
2989}
2990
2991void 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
3000struct ClearDOMCacheCallbackContext {
3001 explicit ClearDOMCacheCallbackContext(TestController& controller)
3002 : testController(controller)
3003 {
3004 }
3005
3006 TestController& testController;
3007 bool done { false };
3008};
3009
3010static void clearDOMCacheCallback(void* userData)
3011{
3012 auto* context = static_cast<ClearDOMCacheCallbackContext*>(userData);
3013 context->done = true;
3014 context->testController.notifyDone();
3015}
3016
3017void 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
3027void 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
3036void TestController::setIDBPerOriginQuota(uint64_t quota)
3037{
3038 WKContextSetIDBPerOriginQuota(platformContext(), quota);
3039}
3040
3041struct RemoveAllIndexedDatabasesCallbackContext {
3042 explicit RemoveAllIndexedDatabasesCallbackContext(TestController& controller)
3043 : testController(controller)
3044 {
3045 }
3046
3047 TestController& testController;
3048 bool done { false };
3049};
3050
3051static void RemoveAllIndexedDatabasesCallback(void* userData)
3052{
3053 auto* context = static_cast<RemoveAllIndexedDatabasesCallbackContext*>(userData);
3054 context->done = true;
3055 context->testController.notifyDone();
3056}
3057
3058void TestController::ClearIndexedDatabases()
3059{
3060 auto websiteDataStore = WKContextGetWebsiteDataStore(platformContext());
3061 RemoveAllIndexedDatabasesCallbackContext context(*this);
3062 WKWebsiteDataStoreRemoveAllIndexedDatabases(websiteDataStore, &context, RemoveAllIndexedDatabasesCallback);
3063 runUntil(context.done, noTimeout);
3064}
3065
3066struct 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
3080static 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
3094bool 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
3103struct 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
3115static 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
3123uint64_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)
3133void TestController::setAllowStorageQuotaIncrease(bool)
3134{
3135 // FIXME: To implement.
3136}
3137
3138bool TestController::isDoingMediaCapture() const
3139{
3140 return false;
3141}
3142
3143#endif
3144
3145struct 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
3157static 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
3165static void resourceStatisticsVoidResultCallback(void* userData)
3166{
3167 auto* context = static_cast<ResourceStatisticsCallbackContext*>(userData);
3168 context->done = true;
3169 context->testController.notifyDone();
3170}
3171
3172static 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
3180void 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
3189void 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
3198void 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
3207void 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
3216void 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
3225String 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
3234bool 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
3243bool 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
3252bool 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
3261bool 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
3270bool 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
3279void 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
3288bool 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
3297void TestController::setStatisticsGrandfathered(WKStringRef host, bool value)
3298{
3299 auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3300 WKWebsiteDataStoreSetStatisticsGrandfathered(dataStore, host, value);
3301}
3302
3303bool 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
3312void TestController::setStatisticsSubframeUnderTopFrameOrigin(WKStringRef host, WKStringRef topFrameHost)
3313{
3314 auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3315 WKWebsiteDataStoreSetStatisticsSubframeUnderTopFrameOrigin(dataStore, host, topFrameHost);
3316}
3317
3318void TestController::setStatisticsSubresourceUnderTopFrameOrigin(WKStringRef host, WKStringRef topFrameHost)
3319{
3320 auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3321 WKWebsiteDataStoreSetStatisticsSubresourceUnderTopFrameOrigin(dataStore, host, topFrameHost);
3322}
3323
3324void TestController::setStatisticsSubresourceUniqueRedirectTo(WKStringRef host, WKStringRef hostRedirectedTo)
3325{
3326 auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3327 WKWebsiteDataStoreSetStatisticsSubresourceUniqueRedirectTo(dataStore, host, hostRedirectedTo);
3328}
3329
3330void TestController::setStatisticsSubresourceUniqueRedirectFrom(WKStringRef host, WKStringRef hostRedirectedFrom)
3331{
3332 auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3333 WKWebsiteDataStoreSetStatisticsSubresourceUniqueRedirectFrom(dataStore, host, hostRedirectedFrom);
3334}
3335
3336void TestController::setStatisticsTopFrameUniqueRedirectTo(WKStringRef host, WKStringRef hostRedirectedTo)
3337{
3338 auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3339 WKWebsiteDataStoreSetStatisticsTopFrameUniqueRedirectTo(dataStore, host, hostRedirectedTo);
3340}
3341
3342void TestController::setStatisticsTopFrameUniqueRedirectFrom(WKStringRef host, WKStringRef hostRedirectedFrom)
3343{
3344 auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3345 WKWebsiteDataStoreSetStatisticsTopFrameUniqueRedirectFrom(dataStore, host, hostRedirectedFrom);
3346}
3347
3348void 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
3356void 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
3364void TestController::statisticsProcessStatisticsAndDataRecords()
3365{
3366 auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3367 ResourceStatisticsCallbackContext context(*this);
3368 WKWebsiteDataStoreStatisticsProcessStatisticsAndDataRecords(dataStore, &context, resourceStatisticsVoidResultCallback);
3369 runUntil(context.done, noTimeout);
3370}
3371
3372void 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
3381void TestController::statisticsSubmitTelemetry()
3382{
3383 auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3384 WKWebsiteDataStoreStatisticsSubmitTelemetry(dataStore);
3385}
3386
3387void TestController::setStatisticsNotifyPagesWhenDataRecordsWereScanned(bool value)
3388{
3389 auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3390 WKWebsiteDataStoreSetStatisticsNotifyPagesWhenDataRecordsWereScanned(dataStore, value);
3391}
3392
3393void 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
3401void TestController::setStatisticsShouldClassifyResourcesBeforeDataRecordsRemoval(bool value)
3402{
3403 auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3404 WKWebsiteDataStoreSetStatisticsShouldClassifyResourcesBeforeDataRecordsRemoval(dataStore, value);
3405}
3406
3407void TestController::setStatisticsNotifyPagesWhenTelemetryWasCaptured(bool value)
3408{
3409 auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3410 WKWebsiteDataStoreSetStatisticsNotifyPagesWhenTelemetryWasCaptured(dataStore, value);
3411}
3412
3413void TestController::setStatisticsMinimumTimeBetweenDataRecordsRemoval(double seconds)
3414{
3415 auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3416 WKWebsiteDataStoreSetStatisticsMinimumTimeBetweenDataRecordsRemoval(dataStore, seconds);
3417}
3418
3419void TestController::setStatisticsGrandfatheringTime(double seconds)
3420{
3421 auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3422 WKWebsiteDataStoreSetStatisticsGrandfatheringTime(dataStore, seconds);
3423}
3424
3425void TestController::setStatisticsMaxStatisticsEntries(unsigned entries)
3426{
3427 auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3428 WKWebsiteDataStoreSetStatisticsMaxStatisticsEntries(dataStore, entries);
3429}
3430
3431void TestController::setStatisticsPruneEntriesDownTo(unsigned entries)
3432{
3433 auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3434 WKWebsiteDataStoreSetStatisticsPruneEntriesDownTo(dataStore, entries);
3435}
3436
3437void 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
3446void 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
3455void 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
3464void 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
3472bool 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
3481void 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
3489void 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
3498void TestController::addMockMediaDevice(WKStringRef persistentID, WKStringRef label, WKStringRef type)
3499{
3500 WKAddMockMediaDevice(platformContext(), persistentID, label, type);
3501}
3502
3503void TestController::clearMockMediaDevices()
3504{
3505 WKClearMockMediaDevices(platformContext());
3506}
3507
3508void TestController::removeMockMediaDevice(WKStringRef persistentID)
3509{
3510 WKRemoveMockMediaDevice(platformContext(), persistentID);
3511}
3512
3513void TestController::resetMockMediaDevices()
3514{
3515 WKResetMockMediaDevices(platformContext());
3516}
3517
3518#if !PLATFORM(COCOA)
3519void TestController::platformAddTestOptions(TestOptions&) const
3520{
3521}
3522
3523void TestController::injectUserScript(WKStringRef)
3524{
3525}
3526
3527void TestController::addTestKeyToKeychain(const String&, const String&, const String&)
3528{
3529}
3530
3531void TestController::cleanUpKeychain(const String&)
3532{
3533}
3534
3535bool TestController::keyExistsInKeychain(const String&, const String&)
3536{
3537 return false;
3538}
3539
3540bool TestController::canDoServerTrustEvaluationInNetworkProcess() const
3541{
3542 return false;
3543}
3544
3545void TestController::installCustomMenuAction(const String&, bool)
3546{
3547}
3548
3549void TestController::setAllowedMenuActions(const Vector<String>&)
3550{
3551}
3552
3553#endif
3554
3555void TestController::sendDisplayConfigurationChangedMessageForTesting()
3556{
3557 WKSendDisplayConfigurationChangedMessageForTesting(platformContext());
3558}
3559
3560void TestController::setWebAuthenticationMockConfiguration(WKDictionaryRef configuration)
3561{
3562 WKWebsiteDataStoreSetWebAuthenticationMockConfiguration(WKContextGetWebsiteDataStore(platformContext()), configuration);
3563}
3564
3565struct 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
3576static 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
3584String 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
3592struct AdClickAttributionVoidCallbackContext {
3593 explicit AdClickAttributionVoidCallbackContext(TestController& controller)
3594 : testController(controller)
3595 {
3596 }
3597
3598 TestController& testController;
3599 bool done { false };
3600};
3601
3602static void adClickAttributionVoidCallback(void* userData)
3603{
3604 auto* context = static_cast<AdClickAttributionVoidCallbackContext*>(userData);
3605 context->done = true;
3606 context->testController.notifyDone();
3607}
3608
3609void TestController::clearAdClickAttribution()
3610{
3611 AdClickAttributionVoidCallbackContext callbackContext(*this);
3612 WKPageClearAdClickAttribution(m_mainWebView->page(), adClickAttributionVoidCallback, &callbackContext);
3613 runUntil(callbackContext.done, noTimeout);
3614}
3615
3616void TestController::clearAdClickAttributionsThroughWebsiteDataRemoval()
3617{
3618 auto* dataStore = WKContextGetWebsiteDataStore(platformContext());
3619 AdClickAttributionVoidCallbackContext callbackContext(*this);
3620 WKWebsiteDataStoreClearAdClickAttributionsThroughWebsiteDataRemoval(dataStore, &callbackContext, adClickAttributionVoidCallback);
3621 runUntil(callbackContext.done, noTimeout);
3622}
3623
3624void TestController::setAdClickAttributionOverrideTimerForTesting(bool value)
3625{
3626 AdClickAttributionVoidCallbackContext callbackContext(*this);
3627 WKPageSetAdClickAttributionOverrideTimerForTesting(m_mainWebView->page(), value, adClickAttributionVoidCallback, &callbackContext);
3628 runUntil(callbackContext.done, noTimeout);
3629}
3630
3631void TestController::setAdClickAttributionConversionURLForTesting(WKURLRef url)
3632{
3633 AdClickAttributionVoidCallbackContext callbackContext(*this);
3634 WKPageSetAdClickAttributionConversionURLForTesting(m_mainWebView->page(), url, adClickAttributionVoidCallback, &callbackContext);
3635 runUntil(callbackContext.done, noTimeout);
3636}
3637
3638void 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