2 * Copyright (C) 2010, 2014 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "TestController.h"
29 #include "EventSenderProxy.h"
31 #include "PlatformWebView.h"
32 #include "StringFunctions.h"
33 #include "TestInvocation.h"
34 #include <WebKit/WKAuthenticationChallenge.h>
35 #include <WebKit/WKAuthenticationDecisionListener.h>
36 #include <WebKit/WKContextConfigurationRef.h>
37 #include <WebKit/WKContextPrivate.h>
38 #include <WebKit/WKCookieManager.h>
39 #include <WebKit/WKCredential.h>
40 #include <WebKit/WKIconDatabase.h>
41 #include <WebKit/WKNotification.h>
42 #include <WebKit/WKNotificationManager.h>
43 #include <WebKit/WKNotificationPermissionRequest.h>
44 #include <WebKit/WKNumber.h>
45 #include <WebKit/WKPageGroup.h>
46 #include <WebKit/WKPagePrivate.h>
47 #include <WebKit/WKPreferencesRefPrivate.h>
48 #include <WebKit/WKProtectionSpace.h>
49 #include <WebKit/WKRetainPtr.h>
55 #include <wtf/text/CString.h>
58 #include <WebKit/WKPagePrivateMac.h>
62 #include <WebKit/WKTextChecker.h>
67 const unsigned TestController::viewWidth = 800;
68 const unsigned TestController::viewHeight = 600;
70 const unsigned TestController::w3cSVGViewWidth = 480;
71 const unsigned TestController::w3cSVGViewHeight = 360;
73 #if defined(__has_feature)
74 #if __has_feature(address_sanitizer)
75 const double TestController::shortTimeout = 10.0;
77 const double TestController::shortTimeout = 5.0;
80 const double TestController::shortTimeout = 5.0;
83 const double TestController::noTimeout = -1;
85 static WKURLRef blankURL()
87 static WKURLRef staticBlankURL = WKURLCreateWithUTF8CString("about:blank");
88 return staticBlankURL;
91 static WKDataRef copyWebCryptoMasterKey(WKContextRef, const void*)
93 // Any 128 bit key would do, all we need for testing is to implement the callback.
94 return WKDataCreate((const uint8_t*)"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", 16);
97 static TestController* controller;
99 TestController& TestController::shared()
105 TestController::TestController(int argc, const char* argv[])
107 , m_printSeparators(false)
108 , m_usingServerMode(false)
109 , m_gcBetweenTests(false)
110 , m_shouldDumpPixelsForAllTests(false)
112 , m_doneResetting(false)
113 , m_useWaitToDumpWatchdogTimer(true)
114 , m_forceNoTimeout(false)
115 , m_didPrintWebProcessCrashedMessage(false)
116 , m_shouldExitWhenWebProcessCrashes(true)
117 , m_beforeUnloadReturnValue(true)
118 , m_isGeolocationPermissionSet(false)
119 , m_isGeolocationPermissionAllowed(false)
120 , m_policyDelegateEnabled(false)
121 , m_policyDelegatePermissive(false)
122 , m_handlesAuthenticationChallenges(false)
123 , m_shouldBlockAllPlugins(false)
124 , m_forceComplexText(false)
125 , m_shouldUseAcceleratedDrawing(false)
126 , m_shouldUseRemoteLayerTree(false)
127 , m_shouldLogHistoryClientCallbacks(false)
129 initialize(argc, argv);
135 TestController::~TestController()
137 WKIconDatabaseClose(WKContextGetIconDatabase(m_context.get()));
142 static WKRect getWindowFrame(WKPageRef page, const void* clientInfo)
144 PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
145 return view->windowFrame();
148 static void setWindowFrame(WKPageRef page, WKRect frame, const void* clientInfo)
150 PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
151 view->setWindowFrame(frame);
154 static bool runBeforeUnloadConfirmPanel(WKPageRef page, WKStringRef message, WKFrameRef frame, const void*)
156 printf("CONFIRM NAVIGATION: %s\n", toSTD(message).c_str());
157 return TestController::shared().beforeUnloadReturnValue();
160 void TestController::runModal(WKPageRef page, const void* clientInfo)
162 PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
163 view->setWindowIsKey(false);
165 view->setWindowIsKey(true);
168 static void closeOtherPage(WKPageRef page, const void* clientInfo)
171 PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
175 static void focus(WKPageRef page, const void* clientInfo)
177 PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
179 view->setWindowIsKey(true);
182 static void unfocus(WKPageRef page, const void* clientInfo)
184 PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
185 view->setWindowIsKey(false);
188 static void decidePolicyForGeolocationPermissionRequest(WKPageRef, WKFrameRef, WKSecurityOriginRef, WKGeolocationPermissionRequestRef permissionRequest, const void* clientInfo)
190 TestController::shared().handleGeolocationPermissionRequest(permissionRequest);
193 static void decidePolicyForUserMediaPermissionRequest(WKPageRef, WKFrameRef, WKSecurityOriginRef, WKUserMediaPermissionRequestRef permissionRequest, const void* clientInfo)
195 TestController::shared().handleUserMediaPermissionRequest(permissionRequest);
198 WKPageRef TestController::createOtherPage(WKPageRef oldPage, WKURLRequestRef, WKDictionaryRef, WKEventModifiers, WKEventMouseButton, const void* clientInfo)
200 PlatformWebView* parentView = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
202 PlatformWebView* view = new PlatformWebView(WKPageGetContext(oldPage), WKPageGetPageGroup(oldPage), oldPage, parentView->options());
203 WKPageRef newPage = view->page();
205 view->resizeTo(800, 600);
207 WKPageUIClientV5 otherPageUIClient = {
209 0, // createNewPage_deprecatedForUseWithV0
215 0, // runJavaScriptAlert
216 0, // runJavaScriptConfirm
217 0, // runJavaScriptPrompt
219 0, // mouseDidMoveOverElement_deprecatedForUseWithV0
220 0, // missingPluginButtonClicked
221 0, // didNotHandleKeyEvent
222 0, // didNotHandleWheelEvent
223 0, // toolbarsAreVisible
224 0, // setToolbarsAreVisible
225 0, // menuBarIsVisible
226 0, // setMenuBarIsVisible
227 0, // statusBarIsVisible
228 0, // setStatusBarIsVisible
233 runBeforeUnloadConfirmPanel,
236 0, // exceededDatabaseQuota
238 decidePolicyForGeolocationPermissionRequest,
245 0, // didCompleteRubberBandForMainFrame
246 0, // saveDataToFileInDownloadsFolder
247 0, // shouldInterruptJavaScript
249 0, // mouseDidMoveOverElement
250 0, // decidePolicyForNotificationPermissionRequest
251 0, // unavailablePluginButtonClicked_deprecatedForUseWithV1
252 0, // showColorPicker
253 0, // hideColorPicker
254 0, // unavailablePluginButtonClicked
255 0, // pinnedStateDidChange
256 0, // didBeginTrackingPotentialLongMousePress
257 0, // didRecognizeLongMousePress
258 0, // didCancelTrackingPotentialLongMousePress
259 0, // isPlayingAudioDidChange
260 decidePolicyForUserMediaPermissionRequest,
262 WKPageSetPageUIClient(newPage, &otherPageUIClient.base);
264 WKPageLoaderClientV5 pageLoaderClient = {
265 { 5, &TestController::shared() },
266 0, // didStartProvisionalLoadForFrame
267 0, // didReceiveServerRedirectForProvisionalLoadForFrame
268 0, // didFailProvisionalLoadWithErrorForFrame
269 0, // didCommitLoadForFrame,
270 0, // didFinishDocumentLoadForFrame
271 0, // didFinishLoadForFrame,
272 0, // didFailLoadWithErrorForFrame
273 0, // didSameDocumentNavigationForFrame
274 0, // didReceiveTitleForFrame
275 0, // didFirstLayoutForFrame
276 0, // didFirstVisuallyNonEmptyLayoutForFrame
277 0, // didRemoveFrameFromHierarchy
278 0, // didFailToInitializePlugin
279 0, // didDisplayInsecureContentForFrame
280 canAuthenticateAgainstProtectionSpaceInFrame,
281 didReceiveAuthenticationChallengeInFrame,
282 0, // didStartProgress
283 0, // didChangeProgress
284 0, // didFinishProgress
285 0, // didBecomeUnresponsive
286 0, // didBecomeResponsive
288 0, // didChangeBackForwardList
289 0, // shouldGoToBackForwardListItem
290 0, // didRunInsecureContentForFrame
291 0, // didDetectXSSForFrame
292 0, // didNewFirstVisuallyNonEmptyLayout_unavailable
293 0, // willGoToBackForwardListItem
294 0, // interactionOccurredWhileProcessUnresponsive
295 0, // pluginDidFail_deprecatedForUseWithV1
296 0, // didReceiveIntentForFrame
297 0, // registerIntentServiceForFrame
299 0, // pluginLoadPolicy_deprecatedForUseWithV2
301 pluginLoadPolicy, // pluginLoadPolicy
302 0, // webGLLoadPolicy
303 0, // resolveWebGLLoadPolicy
304 0, // shouldKeepCurrentBackForwardListItemInList
306 WKPageSetPageLoaderClient(view->page(), &pageLoaderClient.base);
308 WKPagePolicyClientV1 pagePolicyClient = {
309 { 1, &TestController::shared() },
310 0, // decidePolicyForNavigationAction_deprecatedForUseWithV0
311 0, // decidePolicyForNewWindowAction
312 0, // decidePolicyForResponse_deprecatedForUseWithV0
313 0, // unableToImplementPolicy
314 decidePolicyForNavigationAction,
315 decidePolicyForResponse,
317 WKPageSetPagePolicyClient(view->page(), &pagePolicyClient.base);
319 view->didInitializeClients();
325 const char* TestController::libraryPathForTesting()
327 // FIXME: This may not be sufficient to prevent interactions/crashes
328 // when running more than one copy of DumpRenderTree.
329 // See https://bugs.webkit.org/show_bug.cgi?id=10906
330 char* dumpRenderTreeTemp = getenv("DUMPRENDERTREE_TEMP");
331 if (dumpRenderTreeTemp)
332 return dumpRenderTreeTemp;
333 return platformLibraryPathForTesting();
337 void TestController::initialize(int argc, const char* argv[])
339 platformInitialize();
342 OptionsHandler optionsHandler(options);
345 optionsHandler.printHelp();
348 if (!optionsHandler.parse(argc, argv))
351 m_useWaitToDumpWatchdogTimer = options.useWaitToDumpWatchdogTimer;
352 m_forceNoTimeout = options.forceNoTimeout;
353 m_verbose = options.verbose;
354 m_gcBetweenTests = options.gcBetweenTests;
355 m_shouldDumpPixelsForAllTests = options.shouldDumpPixelsForAllTests;
356 m_forceComplexText = options.forceComplexText;
357 m_shouldUseAcceleratedDrawing = options.shouldUseAcceleratedDrawing;
358 m_shouldUseRemoteLayerTree = options.shouldUseRemoteLayerTree;
359 m_paths = options.paths;
361 if (options.printSupportedFeatures) {
362 // FIXME: On Windows, DumpRenderTree uses this to expose whether it supports 3d
363 // transforms and accelerated compositing. When we support those features, we
364 // should match DRT's behavior.
368 m_usingServerMode = (m_paths.size() == 1 && m_paths[0] == "-");
369 if (m_usingServerMode)
370 m_printSeparators = true;
372 m_printSeparators = m_paths.size() > 1;
374 initializeInjectedBundlePath();
375 initializeTestPluginDirectory();
377 WKRetainPtr<WKStringRef> pageGroupIdentifier(AdoptWK, WKStringCreateWithUTF8CString("WebKitTestRunnerPageGroup"));
378 m_pageGroup.adopt(WKPageGroupCreateWithIdentifier(pageGroupIdentifier.get()));
380 auto configuration = adoptWK(WKContextConfigurationCreate());
381 WKContextConfigurationSetInjectedBundlePath(configuration.get(), injectedBundlePath());
383 if (const char* dumpRenderTreeTemp = libraryPathForTesting()) {
384 String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp);
386 const char separator = '/';
388 WKContextConfigurationSetIndexedDBDatabaseDirectory(configuration.get(), toWK(temporaryFolder + separator + "Databases" + separator + "IndexedDB").get());
389 WKContextConfigurationSetLocalStorageDirectory(configuration.get(), toWK(temporaryFolder + separator + "LocalStorage").get());
390 WKContextConfigurationSetWebSQLDatabaseDirectory(configuration.get(), toWK(temporaryFolder + separator + "Databases" + separator + "WebSQL").get());
393 m_context = adoptWK(WKContextCreateWithConfiguration(configuration.get()));
394 m_geolocationProvider = std::make_unique<GeolocationProviderMock>(m_context.get());
396 #if PLATFORM(IOS) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED > 1080) || PLATFORM(GTK)
397 WKContextSetUsesNetworkProcess(m_context.get(), true);
398 WKContextSetProcessModel(m_context.get(), kWKProcessModelMultipleSecondaryProcesses);
401 if (const char* dumpRenderTreeTemp = libraryPathForTesting()) {
402 String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp);
404 const char separator = '/';
406 // FIXME: These should be migrated to WKContextConfigurationRef.
407 WKContextSetApplicationCacheDirectory(m_context.get(), toWK(temporaryFolder + separator + "ApplicationCache").get());
408 WKContextSetDiskCacheDirectory(m_context.get(), toWK(temporaryFolder + separator + "Cache").get());
409 WKContextSetCookieStorageDirectory(m_context.get(), toWK(temporaryFolder + separator + "Cookies").get());
410 // Disable icon database to avoid fetching <http://127.0.0.1:8000/favicon.ico> and making tests flaky.
411 // Invividual tests can enable it using testRunner.setIconDatabaseEnabled, although it's not currently supported in WebKitTestRunner.
412 WKContextSetIconDatabasePath(m_context.get(), toWK(emptyString()).get());
415 WKContextUseTestingNetworkSession(m_context.get());
416 WKContextSetCacheModel(m_context.get(), kWKCacheModelDocumentBrowser);
418 platformInitializeContext();
420 WKContextClientV1 contextClient = {
422 nullptr, // plugInAutoStartOriginHashesChanged
423 nullptr, // networkProcessDidCrash,
424 nullptr, // plugInInformationBecameAvailable,
425 copyWebCryptoMasterKey
427 WKContextSetClient(m_context.get(), &contextClient.base);
429 WKContextInjectedBundleClientV1 injectedBundleClient = {
431 didReceiveMessageFromInjectedBundle,
432 didReceiveSynchronousMessageFromInjectedBundle,
433 0 // getInjectedBundleInitializationUserData
435 WKContextSetInjectedBundleClient(m_context.get(), &injectedBundleClient.base);
437 WKContextHistoryClientV0 historyClient = {
439 didNavigateWithNavigationData,
440 didPerformClientRedirect,
441 didPerformServerRedirect,
442 didUpdateHistoryTitle,
443 0, // populateVisitedLinks
445 WKContextSetHistoryClient(m_context.get(), &historyClient.base);
447 WKNotificationManagerRef notificationManager = WKContextGetNotificationManager(m_context.get());
448 WKNotificationProviderV0 notificationKit = m_webNotificationProvider.provider();
449 WKNotificationManagerSetProvider(notificationManager, ¬ificationKit.base);
451 if (testPluginDirectory())
452 WKContextSetAdditionalPluginsDirectory(m_context.get(), testPluginDirectory());
454 if (m_forceComplexText)
455 WKContextSetAlwaysUsesComplexTextCodePath(m_context.get(), true);
457 // Some preferences (notably mock scroll bars setting) currently cannot be re-applied to an existing view, so we need to set them now.
458 resetPreferencesToConsistentValues();
460 WKRetainPtr<WKMutableDictionaryRef> viewOptions;
461 if (m_shouldUseRemoteLayerTree) {
462 viewOptions = adoptWK(WKMutableDictionaryCreate());
463 WKRetainPtr<WKStringRef> useRemoteLayerTreeKey = adoptWK(WKStringCreateWithUTF8CString("RemoteLayerTree"));
464 WKRetainPtr<WKBooleanRef> useRemoteLayerTreeValue = adoptWK(WKBooleanCreate(m_shouldUseRemoteLayerTree));
465 WKDictionarySetItem(viewOptions.get(), useRemoteLayerTreeKey.get(), useRemoteLayerTreeValue.get());
468 createWebViewWithOptions(viewOptions.get());
471 void TestController::createWebViewWithOptions(WKDictionaryRef options)
473 m_mainWebView = std::make_unique<PlatformWebView>(m_context.get(), m_pageGroup.get(), nullptr, options);
474 WKPageUIClientV5 pageUIClient = {
475 { 5, m_mainWebView.get() },
476 0, // createNewPage_deprecatedForUseWithV0
482 0, // runJavaScriptAlert
483 0, // runJavaScriptConfirm
484 0, // runJavaScriptPrompt
486 0, // mouseDidMoveOverElement_deprecatedForUseWithV0
487 0, // missingPluginButtonClicked
488 0, // didNotHandleKeyEvent
489 0, // didNotHandleWheelEvent
490 0, // toolbarsAreVisible
491 0, // setToolbarsAreVisible
492 0, // menuBarIsVisible
493 0, // setMenuBarIsVisible
494 0, // statusBarIsVisible
495 0, // setStatusBarIsVisible
500 runBeforeUnloadConfirmPanel,
503 0, // exceededDatabaseQuota,
505 decidePolicyForGeolocationPermissionRequest,
512 0, // didCompleteRubberBandForMainFrame
513 0, // saveDataToFileInDownloadsFolder
514 0, // shouldInterruptJavaScript
516 0, // mouseDidMoveOverElement
517 decidePolicyForNotificationPermissionRequest, // decidePolicyForNotificationPermissionRequest
518 0, // unavailablePluginButtonClicked_deprecatedForUseWithV1
519 0, // showColorPicker
520 0, // hideColorPicker
521 unavailablePluginButtonClicked,
522 0, // pinnedStateDidChange
523 0, // didBeginTrackingPotentialLongMousePress
524 0, // didRecognizeLongMousePress
525 0, // didCancelTrackingPotentialLongMousePress
526 0, // isPlayingAudioDidChange
527 decidePolicyForUserMediaPermissionRequest,
529 WKPageSetPageUIClient(m_mainWebView->page(), &pageUIClient.base);
531 WKPageLoaderClientV5 pageLoaderClient = {
533 0, // didStartProvisionalLoadForFrame
534 0, // didReceiveServerRedirectForProvisionalLoadForFrame
535 0, // didFailProvisionalLoadWithErrorForFrame
536 didCommitLoadForFrame,
537 0, // didFinishDocumentLoadForFrame
538 didFinishLoadForFrame,
539 0, // didFailLoadWithErrorForFrame
540 0, // didSameDocumentNavigationForFrame
541 0, // didReceiveTitleForFrame
542 0, // didFirstLayoutForFrame
543 0, // didFirstVisuallyNonEmptyLayoutForFrame
544 0, // didRemoveFrameFromHierarchy
545 0, // didFailToInitializePlugin
546 0, // didDisplayInsecureContentForFrame
547 canAuthenticateAgainstProtectionSpaceInFrame,
548 didReceiveAuthenticationChallengeInFrame,
549 0, // didStartProgress
550 0, // didChangeProgress
551 0, // didFinishProgress
552 0, // didBecomeUnresponsive
553 0, // didBecomeResponsive
555 0, // didChangeBackForwardList
556 0, // shouldGoToBackForwardListItem
557 0, // didRunInsecureContentForFrame
558 0, // didDetectXSSForFrame
559 0, // didNewFirstVisuallyNonEmptyLayout_unavailable
560 0, // willGoToBackForwardListItem
561 0, // interactionOccurredWhileProcessUnresponsive
562 0, // pluginDidFail_deprecatedForUseWithV1
563 0, // didReceiveIntentForFrame
564 0, // registerIntentServiceForFrame
566 0, // pluginLoadPolicy_deprecatedForUseWithV2
568 pluginLoadPolicy, // pluginLoadPolicy
569 0, // webGLLoadPolicy
570 0, // resolveWebGLLoadPolicy
571 0, // shouldKeepCurrentBackForwardListItemInList
573 WKPageSetPageLoaderClient(m_mainWebView->page(), &pageLoaderClient.base);
575 WKPagePolicyClientV1 pagePolicyClient = {
577 0, // decidePolicyForNavigationAction_deprecatedForUseWithV0
578 0, // decidePolicyForNewWindowAction
579 0, // decidePolicyForResponse_deprecatedForUseWithV0
580 0, // unableToImplementPolicy
581 decidePolicyForNavigationAction,
582 decidePolicyForResponse,
584 WKPageSetPagePolicyClient(m_mainWebView->page(), &pagePolicyClient.base);
586 m_mainWebView->didInitializeClients();
588 // Generally, the tests should default to running at 1x. updateWindowScaleForTest() will adjust the scale to
589 // something else for specific tests that need to run at a different window scale.
590 m_mainWebView->changeWindowScaleIfNeeded(1);
593 void TestController::ensureViewSupportsOptions(WKDictionaryRef options)
595 if (m_mainWebView && !m_mainWebView->viewSupportsOptions(options)) {
596 WKPageSetPageUIClient(m_mainWebView->page(), 0);
597 WKPageSetPageLoaderClient(m_mainWebView->page(), 0);
598 WKPageSetPagePolicyClient(m_mainWebView->page(), 0);
599 WKPageClose(m_mainWebView->page());
601 m_mainWebView = nullptr;
603 createWebViewWithOptions(options);
604 resetStateToConsistentValues();
608 void TestController::resetPreferencesToConsistentValues()
611 WKPreferencesRef preferences = WKPageGroupGetPreferences(m_pageGroup.get());
612 WKPreferencesResetTestRunnerOverrides(preferences);
613 WKPreferencesSetOfflineWebApplicationCacheEnabled(preferences, true);
614 WKPreferencesSetFontSmoothingLevel(preferences, kWKFontSmoothingLevelNoSubpixelAntiAliasing);
615 WKPreferencesSetXSSAuditorEnabled(preferences, false);
616 WKPreferencesSetWebAudioEnabled(preferences, true);
617 WKPreferencesSetMediaStreamEnabled(preferences, true);
618 WKPreferencesSetDeveloperExtrasEnabled(preferences, true);
619 WKPreferencesSetJavaScriptExperimentsEnabled(preferences, true);
620 WKPreferencesSetJavaScriptCanOpenWindowsAutomatically(preferences, true);
621 WKPreferencesSetJavaScriptCanAccessClipboard(preferences, true);
622 WKPreferencesSetDOMPasteAllowed(preferences, true);
623 WKPreferencesSetUniversalAccessFromFileURLsAllowed(preferences, true);
624 WKPreferencesSetFileAccessFromFileURLsAllowed(preferences, true);
625 #if ENABLE(FULLSCREEN_API)
626 WKPreferencesSetFullScreenEnabled(preferences, true);
628 WKPreferencesSetPageCacheEnabled(preferences, false);
629 WKPreferencesSetAsynchronousPluginInitializationEnabled(preferences, false);
630 WKPreferencesSetAsynchronousPluginInitializationEnabledForAllPlugins(preferences, false);
631 WKPreferencesSetArtificialPluginInitializationDelayEnabled(preferences, false);
632 WKPreferencesSetTabToLinksEnabled(preferences, false);
633 WKPreferencesSetInteractiveFormValidationEnabled(preferences, true);
634 WKPreferencesSetMockScrollbarsEnabled(preferences, true);
636 static WKStringRef defaultTextEncoding = WKStringCreateWithUTF8CString("ISO-8859-1");
637 WKPreferencesSetDefaultTextEncodingName(preferences, defaultTextEncoding);
639 static WKStringRef standardFontFamily = WKStringCreateWithUTF8CString("Times");
640 static WKStringRef cursiveFontFamily = WKStringCreateWithUTF8CString("Apple Chancery");
641 static WKStringRef fantasyFontFamily = WKStringCreateWithUTF8CString("Papyrus");
642 static WKStringRef fixedFontFamily = WKStringCreateWithUTF8CString("Courier");
643 static WKStringRef pictographFontFamily = WKStringCreateWithUTF8CString("Apple Color Emoji");
644 static WKStringRef sansSerifFontFamily = WKStringCreateWithUTF8CString("Helvetica");
645 static WKStringRef serifFontFamily = WKStringCreateWithUTF8CString("Times");
647 WKPreferencesSetStandardFontFamily(preferences, standardFontFamily);
648 WKPreferencesSetCursiveFontFamily(preferences, cursiveFontFamily);
649 WKPreferencesSetFantasyFontFamily(preferences, fantasyFontFamily);
650 WKPreferencesSetFixedFontFamily(preferences, fixedFontFamily);
651 WKPreferencesSetPictographFontFamily(preferences, pictographFontFamily);
652 WKPreferencesSetSansSerifFontFamily(preferences, sansSerifFontFamily);
653 WKPreferencesSetSerifFontFamily(preferences, serifFontFamily);
654 WKPreferencesSetAsynchronousSpellCheckingEnabled(preferences, false);
655 #if ENABLE(WEB_AUDIO)
656 WKPreferencesSetMediaSourceEnabled(preferences, true);
659 WKPreferencesSetAcceleratedDrawingEnabled(preferences, m_shouldUseAcceleratedDrawing);
661 WKCookieManagerDeleteAllCookies(WKContextGetCookieManager(m_context.get()));
663 platformResetPreferencesToConsistentValues();
666 bool TestController::resetStateToConsistentValues()
670 m_beforeUnloadReturnValue = true;
672 WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("Reset"));
673 WKRetainPtr<WKMutableDictionaryRef> resetMessageBody = adoptWK(WKMutableDictionaryCreate());
675 WKRetainPtr<WKStringRef> shouldGCKey = adoptWK(WKStringCreateWithUTF8CString("ShouldGC"));
676 WKRetainPtr<WKBooleanRef> shouldGCValue = adoptWK(WKBooleanCreate(m_gcBetweenTests));
677 WKDictionarySetItem(resetMessageBody.get(), shouldGCKey.get(), shouldGCValue.get());
679 WKContextPostMessageToInjectedBundle(TestController::shared().context(), messageName.get(), resetMessageBody.get());
681 WKContextSetShouldUseFontSmoothing(TestController::shared().context(), false);
683 WKContextSetCacheModel(TestController::shared().context(), kWKCacheModelDocumentBrowser);
685 // FIXME: This function should also ensure that there is only one page open.
687 // Reset the EventSender for each test.
688 m_eventSenderProxy = std::make_unique<EventSenderProxy>(this);
690 // FIXME: Is this needed? Nothing in TestController changes preferences during tests, and if there is
691 // some other code doing this, it should probably be responsible for cleanup too.
692 resetPreferencesToConsistentValues();
695 WKTextCheckerContinuousSpellCheckingEnabledStateChanged(true);
698 // in the case that a test using the chrome input field failed, be sure to clean up for the next test
699 m_mainWebView->removeChromeInputField();
700 m_mainWebView->focus();
702 // Re-set to the default backing scale factor by setting the custom scale factor to 0.
703 WKPageSetCustomBackingScaleFactor(m_mainWebView->page(), 0);
706 // EFL use a real window while other ports such as Qt don't.
707 // In EFL, we need to resize the window to the original size after calls to window.resizeTo.
708 WKRect rect = m_mainWebView->windowFrame();
709 m_mainWebView->setWindowFrame(WKRectMake(rect.origin.x, rect.origin.y, TestController::viewWidth, TestController::viewHeight));
712 // Reset notification permissions
713 m_webNotificationProvider.reset();
715 // Reset Geolocation permissions.
716 m_geolocationPermissionRequests.clear();
717 m_isGeolocationPermissionSet = false;
718 m_isGeolocationPermissionAllowed = false;
720 // Reset UserMedia permissions.
721 m_userMediaPermissionRequests.clear();
722 m_isUserMediaPermissionSet = false;
723 m_isUserMediaPermissionAllowed = false;
725 // Reset Custom Policy Delegate.
726 setCustomPolicyDelegate(false, false);
728 m_workQueueManager.clearWorkQueue();
730 m_handlesAuthenticationChallenges = false;
731 m_authenticationUsername = String();
732 m_authenticationPassword = String();
734 m_shouldBlockAllPlugins = false;
736 m_shouldLogHistoryClientCallbacks = false;
738 // Reset main page back to about:blank
739 m_doneResetting = false;
741 WKPageLoadURL(m_mainWebView->page(), blankURL());
742 runUntil(m_doneResetting, shortTimeout);
743 return m_doneResetting;
746 void TestController::terminateWebContentProcess()
748 WKPageTerminate(m_mainWebView->page());
751 void TestController::reattachPageToWebProcess()
753 // Loading a web page is the only way to reattach an existing page to a process.
754 m_doneResetting = false;
755 WKPageLoadURL(m_mainWebView->page(), blankURL());
756 runUntil(m_doneResetting, shortTimeout);
759 void TestController::updateWebViewSizeForTest(const TestInvocation& test)
761 bool isSVGW3CTest = strstr(test.pathOrURL(), "svg/W3C-SVG-1.1") || strstr(test.pathOrURL(), "svg\\W3C-SVG-1.1");
763 unsigned width = viewWidth;
764 unsigned height = viewHeight;
766 width = w3cSVGViewWidth;
767 height = w3cSVGViewHeight;
770 mainWebView()->resizeTo(width, height);
773 void TestController::updateWindowScaleForTest(const TestInvocation& test)
775 WTF::String localPathOrUrl = String(test.pathOrURL());
776 bool needsHighDPIWindow = localPathOrUrl.findIgnoringCase("/hidpi-") != notFound;
777 mainWebView()->changeWindowScaleIfNeeded(needsHighDPIWindow ? 2 : 1);
780 // FIXME: move into relevant platformConfigureViewForTest()?
781 static bool shouldUseFixedLayout(const char* pathOrURL)
783 #if ENABLE(CSS_DEVICE_ADAPTATION)
784 if (strstr(pathOrURL, "device-adapt/") || strstr(pathOrURL, "device-adapt\\"))
788 #if USE(TILED_BACKING_STORE) && PLATFORM(EFL)
789 if (strstr(pathOrURL, "sticky/") || strstr(pathOrURL, "sticky\\"))
794 UNUSED_PARAM(pathOrURL);
797 void TestController::updateLayoutTypeForTest(const TestInvocation& test)
799 auto viewOptions = adoptWK(WKMutableDictionaryCreate());
800 auto useFixedLayoutKey = adoptWK(WKStringCreateWithUTF8CString("UseFixedLayout"));
801 auto useFixedLayoutValue = adoptWK(WKBooleanCreate(shouldUseFixedLayout(test.pathOrURL())));
802 WKDictionarySetItem(viewOptions.get(), useFixedLayoutKey.get(), useFixedLayoutValue.get());
804 ensureViewSupportsOptions(viewOptions.get());
808 void TestController::platformConfigureViewForTest(const TestInvocation&)
812 void TestController::platformResetPreferencesToConsistentValues()
817 void TestController::configureViewForTest(const TestInvocation& test)
819 updateWebViewSizeForTest(test);
820 updateWindowScaleForTest(test);
821 updateLayoutTypeForTest(test);
823 platformConfigureViewForTest(test);
827 TestCommand() : shouldDumpPixels(false), timeout(0) { }
829 std::string pathOrURL;
830 bool shouldDumpPixels;
831 std::string expectedPixelHash;
835 class CommandTokenizer {
837 explicit CommandTokenizer(const std::string& input)
839 , m_posNextSeparator(0)
844 bool hasNext() const;
849 static const char kSeparator = '\'';
850 const std::string& m_input;
852 size_t m_posNextSeparator;
855 void CommandTokenizer::pump()
857 if (m_posNextSeparator == std::string::npos || m_posNextSeparator == m_input.size()) {
858 m_next = std::string();
861 size_t start = m_posNextSeparator ? m_posNextSeparator + 1 : 0;
862 m_posNextSeparator = m_input.find(kSeparator, start);
863 size_t size = m_posNextSeparator == std::string::npos ? std::string::npos : m_posNextSeparator - start;
864 m_next = std::string(m_input, start, size);
867 std::string CommandTokenizer::next()
871 std::string oldNext = m_next;
876 bool CommandTokenizer::hasNext() const
878 return !m_next.empty();
881 NO_RETURN static void die(const std::string& inputLine)
883 fprintf(stderr, "Unexpected input line: %s\n", inputLine.c_str());
887 TestCommand parseInputLine(const std::string& inputLine)
890 CommandTokenizer tokenizer(inputLine);
891 if (!tokenizer.hasNext())
894 std::string arg = tokenizer.next();
895 result.pathOrURL = arg;
896 while (tokenizer.hasNext()) {
897 arg = tokenizer.next();
898 if (arg == std::string("--timeout")) {
899 std::string timeoutToken = tokenizer.next();
900 result.timeout = atoi(timeoutToken.c_str());
901 } else if (arg == std::string("-p") || arg == std::string("--pixel-test")) {
902 result.shouldDumpPixels = true;
903 if (tokenizer.hasNext())
904 result.expectedPixelHash = tokenizer.next();
911 bool TestController::runTest(const char* inputLine)
913 TestCommand command = parseInputLine(std::string(inputLine));
915 m_state = RunningTest;
917 m_currentInvocation = std::make_unique<TestInvocation>(command.pathOrURL);
918 if (command.shouldDumpPixels || m_shouldDumpPixelsForAllTests)
919 m_currentInvocation->setIsPixelTest(command.expectedPixelHash);
920 if (command.timeout > 0)
921 m_currentInvocation->setCustomTimeout(command.timeout);
923 platformWillRunTest(*m_currentInvocation);
925 m_currentInvocation->invoke();
926 m_currentInvocation = nullptr;
931 void TestController::runTestingServerLoop()
933 char filenameBuffer[2048];
934 while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
935 char* newLineCharacter = strchr(filenameBuffer, '\n');
936 if (newLineCharacter)
937 *newLineCharacter = '\0';
939 if (strlen(filenameBuffer) == 0)
942 if (!runTest(filenameBuffer))
947 void TestController::run()
949 if (!resetStateToConsistentValues()) {
950 TestInvocation::dumpWebProcessUnresponsiveness("<unknown> - TestController::run - Failed to reset state to consistent values\n");
954 if (m_usingServerMode)
955 runTestingServerLoop();
957 for (size_t i = 0; i < m_paths.size(); ++i) {
958 if (!runTest(m_paths[i].c_str()))
964 void TestController::runUntil(bool& done, double timeout)
966 if (m_forceNoTimeout)
969 platformRunUntil(done, timeout);
972 // WKContextInjectedBundleClient
974 void TestController::didReceiveMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
976 static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveMessageFromInjectedBundle(messageName, messageBody);
979 void TestController::didReceiveSynchronousMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, WKTypeRef* returnData, const void* clientInfo)
981 *returnData = static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody).leakRef();
984 void TestController::didReceiveKeyDownMessageFromInjectedBundle(WKDictionaryRef messageBodyDictionary, bool synchronous)
986 WKRetainPtr<WKStringRef> keyKey = adoptWK(WKStringCreateWithUTF8CString("Key"));
987 WKStringRef key = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, keyKey.get()));
989 WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
990 WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
992 WKRetainPtr<WKStringRef> locationKey = adoptWK(WKStringCreateWithUTF8CString("Location"));
993 unsigned location = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, locationKey.get()))));
996 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
998 m_eventSenderProxy->keyDown(key, modifiers, location);
1001 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
1004 void TestController::didReceiveMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
1006 if (WKStringIsEqualToUTF8CString(messageName, "EventSender")) {
1007 ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
1008 WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
1010 WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
1011 WKStringRef subMessageName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, subMessageKey.get()));
1013 if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown") || WKStringIsEqualToUTF8CString(subMessageName, "MouseUp")) {
1014 WKRetainPtr<WKStringRef> buttonKey = adoptWK(WKStringCreateWithUTF8CString("Button"));
1015 unsigned button = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, buttonKey.get()))));
1017 WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
1018 WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
1020 // Forward to WebProcess
1021 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
1022 if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown"))
1023 m_eventSenderProxy->mouseDown(button, modifiers);
1025 m_eventSenderProxy->mouseUp(button, modifiers);
1030 if (WKStringIsEqualToUTF8CString(subMessageName, "KeyDown")) {
1031 didReceiveKeyDownMessageFromInjectedBundle(messageBodyDictionary, false);
1036 if (WKStringIsEqualToUTF8CString(subMessageName, "MouseScrollByWithWheelAndMomentumPhases")) {
1037 WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1038 double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1040 WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1041 double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1043 WKRetainPtr<WKStringRef> phaseKey = adoptWK(WKStringCreateWithUTF8CString("Phase"));
1044 int phase = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, phaseKey.get()))));
1045 WKRetainPtr<WKStringRef> momentumKey = adoptWK(WKStringCreateWithUTF8CString("Momentum"));
1046 int momentum = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, momentumKey.get()))));
1048 // Forward to WebProcess
1049 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
1050 m_eventSenderProxy->mouseScrollByWithWheelAndMomentumPhases(x, y, phase, momentum);
1055 ASSERT_NOT_REACHED();
1058 if (!m_currentInvocation)
1061 m_currentInvocation->didReceiveMessageFromInjectedBundle(messageName, messageBody);
1064 WKRetainPtr<WKTypeRef> TestController::didReceiveSynchronousMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
1066 if (WKStringIsEqualToUTF8CString(messageName, "EventSender")) {
1067 ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
1068 WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
1070 WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
1071 WKStringRef subMessageName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, subMessageKey.get()));
1073 if (WKStringIsEqualToUTF8CString(subMessageName, "KeyDown")) {
1074 didReceiveKeyDownMessageFromInjectedBundle(messageBodyDictionary, true);
1079 if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown") || WKStringIsEqualToUTF8CString(subMessageName, "MouseUp")) {
1080 WKRetainPtr<WKStringRef> buttonKey = adoptWK(WKStringCreateWithUTF8CString("Button"));
1081 unsigned button = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, buttonKey.get()))));
1083 WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
1084 WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
1086 // Forward to WebProcess
1087 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
1088 if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown"))
1089 m_eventSenderProxy->mouseDown(button, modifiers);
1091 m_eventSenderProxy->mouseUp(button, modifiers);
1092 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
1096 if (WKStringIsEqualToUTF8CString(subMessageName, "MouseMoveTo")) {
1097 WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1098 double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1100 WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1101 double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1103 // Forward to WebProcess
1104 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
1105 m_eventSenderProxy->mouseMoveTo(x, y);
1106 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
1110 if (WKStringIsEqualToUTF8CString(subMessageName, "MouseScrollBy")) {
1111 WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1112 double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1114 WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1115 double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1117 // Forward to WebProcess
1118 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
1119 m_eventSenderProxy->mouseScrollBy(x, y);
1120 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
1124 if (WKStringIsEqualToUTF8CString(subMessageName, "MouseScrollByWithWheelAndMomentumPhases")) {
1125 WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1126 double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1128 WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1129 double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1131 WKRetainPtr<WKStringRef> phaseKey = adoptWK(WKStringCreateWithUTF8CString("Phase"));
1132 int phase = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, phaseKey.get()))));
1133 WKRetainPtr<WKStringRef> momentumKey = adoptWK(WKStringCreateWithUTF8CString("Momentum"));
1134 int momentum = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, momentumKey.get()))));
1136 // Forward to WebProcess
1137 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
1138 m_eventSenderProxy->mouseScrollByWithWheelAndMomentumPhases(x, y, phase, momentum);
1139 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
1143 if (WKStringIsEqualToUTF8CString(subMessageName, "ContinuousMouseScrollBy")) {
1144 WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1145 double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
1147 WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1148 double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
1150 WKRetainPtr<WKStringRef> pagedKey = adoptWK(WKStringCreateWithUTF8CString("Paged"));
1151 bool paged = static_cast<bool>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, pagedKey.get()))));
1153 // Forward to WebProcess
1154 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
1155 m_eventSenderProxy->continuousMouseScrollBy(x, y, paged);
1156 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
1160 if (WKStringIsEqualToUTF8CString(subMessageName, "LeapForward")) {
1161 WKRetainPtr<WKStringRef> timeKey = adoptWK(WKStringCreateWithUTF8CString("TimeInMilliseconds"));
1162 unsigned time = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, timeKey.get()))));
1164 m_eventSenderProxy->leapForward(time);
1168 #if ENABLE(TOUCH_EVENTS)
1169 if (WKStringIsEqualToUTF8CString(subMessageName, "AddTouchPoint")) {
1170 WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1171 int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
1173 WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1174 int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
1176 m_eventSenderProxy->addTouchPoint(x, y);
1180 if (WKStringIsEqualToUTF8CString(subMessageName, "UpdateTouchPoint")) {
1181 WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
1182 int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
1184 WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
1185 int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
1187 WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
1188 int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
1190 m_eventSenderProxy->updateTouchPoint(index, x, y);
1194 if (WKStringIsEqualToUTF8CString(subMessageName, "SetTouchModifier")) {
1195 WKRetainPtr<WKStringRef> modifierKey = adoptWK(WKStringCreateWithUTF8CString("Modifier"));
1196 WKEventModifiers modifier = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifierKey.get()))));
1198 WKRetainPtr<WKStringRef> enableKey = adoptWK(WKStringCreateWithUTF8CString("Enable"));
1199 bool enable = static_cast<bool>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, enableKey.get()))));
1201 m_eventSenderProxy->setTouchModifier(modifier, enable);
1205 if (WKStringIsEqualToUTF8CString(subMessageName, "SetTouchPointRadius")) {
1206 WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("RadiusX"));
1207 int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
1209 WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("RadiusY"));
1210 int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
1212 m_eventSenderProxy->setTouchPointRadius(x, y);
1216 if (WKStringIsEqualToUTF8CString(subMessageName, "TouchStart")) {
1217 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
1218 m_eventSenderProxy->touchStart();
1219 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
1223 if (WKStringIsEqualToUTF8CString(subMessageName, "TouchMove")) {
1224 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
1225 m_eventSenderProxy->touchMove();
1226 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
1230 if (WKStringIsEqualToUTF8CString(subMessageName, "TouchEnd")) {
1231 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
1232 m_eventSenderProxy->touchEnd();
1233 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
1237 if (WKStringIsEqualToUTF8CString(subMessageName, "TouchCancel")) {
1238 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
1239 m_eventSenderProxy->touchCancel();
1240 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
1244 if (WKStringIsEqualToUTF8CString(subMessageName, "ClearTouchPoints")) {
1245 m_eventSenderProxy->clearTouchPoints();
1249 if (WKStringIsEqualToUTF8CString(subMessageName, "ReleaseTouchPoint")) {
1250 WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
1251 int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
1252 m_eventSenderProxy->releaseTouchPoint(index);
1256 if (WKStringIsEqualToUTF8CString(subMessageName, "CancelTouchPoint")) {
1257 WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
1258 int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
1259 m_eventSenderProxy->cancelTouchPoint(index);
1263 ASSERT_NOT_REACHED();
1265 return m_currentInvocation->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody);
1268 // WKPageLoaderClient
1270 void TestController::didCommitLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef, const void* clientInfo)
1272 static_cast<TestController*>(const_cast<void*>(clientInfo))->didCommitLoadForFrame(page, frame);
1275 void TestController::didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef, const void* clientInfo)
1277 static_cast<TestController*>(const_cast<void*>(clientInfo))->didFinishLoadForFrame(page, frame);
1280 bool TestController::canAuthenticateAgainstProtectionSpaceInFrame(WKPageRef, WKFrameRef, WKProtectionSpaceRef protectionSpace, const void*)
1282 WKProtectionSpaceAuthenticationScheme authenticationScheme = WKProtectionSpaceGetAuthenticationScheme(protectionSpace);
1284 if (authenticationScheme == kWKProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested) {
1285 std::string host = toSTD(adoptWK(WKProtectionSpaceCopyHost(protectionSpace)).get());
1286 return host == "localhost" || host == "127.0.0.1";
1289 return authenticationScheme <= kWKProtectionSpaceAuthenticationSchemeHTTPDigest;
1292 void TestController::didReceiveAuthenticationChallengeInFrame(WKPageRef page, WKFrameRef frame, WKAuthenticationChallengeRef authenticationChallenge, const void *clientInfo)
1294 static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveAuthenticationChallengeInFrame(page, frame, authenticationChallenge);
1297 void TestController::processDidCrash(WKPageRef page, const void* clientInfo)
1299 static_cast<TestController*>(const_cast<void*>(clientInfo))->processDidCrash();
1302 WKPluginLoadPolicy TestController::pluginLoadPolicy(WKPageRef page, WKPluginLoadPolicy currentPluginLoadPolicy, WKDictionaryRef pluginInformation, WKStringRef* unavailabilityDescription, const void* clientInfo)
1304 return static_cast<TestController*>(const_cast<void*>(clientInfo))->pluginLoadPolicy(page, currentPluginLoadPolicy, pluginInformation, unavailabilityDescription);
1307 WKPluginLoadPolicy TestController::pluginLoadPolicy(WKPageRef, WKPluginLoadPolicy currentPluginLoadPolicy, WKDictionaryRef pluginInformation, WKStringRef* unavailabilityDescription)
1309 if (m_shouldBlockAllPlugins)
1310 return kWKPluginLoadPolicyBlocked;
1311 return currentPluginLoadPolicy;
1314 void TestController::didCommitLoadForFrame(WKPageRef page, WKFrameRef frame)
1316 if (!WKFrameIsMainFrame(frame))
1319 mainWebView()->focus();
1322 void TestController::didFinishLoadForFrame(WKPageRef page, WKFrameRef frame)
1324 if (m_state != Resetting)
1327 if (!WKFrameIsMainFrame(frame))
1330 WKRetainPtr<WKURLRef> wkURL(AdoptWK, WKFrameCopyURL(frame));
1331 if (!WKURLIsEqual(wkURL.get(), blankURL()))
1334 m_doneResetting = true;
1335 shared().notifyDone();
1338 void TestController::didReceiveAuthenticationChallengeInFrame(WKPageRef page, WKFrameRef frame, WKAuthenticationChallengeRef authenticationChallenge)
1340 WKProtectionSpaceRef protectionSpace = WKAuthenticationChallengeGetProtectionSpace(authenticationChallenge);
1341 WKAuthenticationDecisionListenerRef decisionListener = WKAuthenticationChallengeGetDecisionListener(authenticationChallenge);
1343 if (WKProtectionSpaceGetAuthenticationScheme(protectionSpace) == kWKProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested) {
1344 // Any non-empty credential signals to accept the server trust. Since the cross-platform API
1345 // doesn't expose a way to create a credential from server trust, we use a password credential.
1347 WKRetainPtr<WKCredentialRef> credential = adoptWK(WKCredentialCreate(toWK("accept server trust").get(), toWK("").get(), kWKCredentialPersistenceNone));
1348 WKAuthenticationDecisionListenerUseCredential(decisionListener, credential.get());
1352 std::string host = toSTD(adoptWK(WKProtectionSpaceCopyHost(protectionSpace)).get());
1353 int port = WKProtectionSpaceGetPort(protectionSpace);
1354 String message = String::format("%s:%d - didReceiveAuthenticationChallenge - ", host.c_str(), port);
1355 if (!m_handlesAuthenticationChallenges)
1356 message.append("Simulating cancelled authentication sheet\n");
1358 message.append(String::format("Responding with %s:%s\n", m_authenticationUsername.utf8().data(), m_authenticationPassword.utf8().data()));
1359 m_currentInvocation->outputText(message);
1361 if (!m_handlesAuthenticationChallenges) {
1362 WKAuthenticationDecisionListenerUseCredential(decisionListener, 0);
1365 WKRetainPtr<WKStringRef> username(AdoptWK, WKStringCreateWithUTF8CString(m_authenticationUsername.utf8().data()));
1366 WKRetainPtr<WKStringRef> password(AdoptWK, WKStringCreateWithUTF8CString(m_authenticationPassword.utf8().data()));
1367 WKRetainPtr<WKCredentialRef> credential(AdoptWK, WKCredentialCreate(username.get(), password.get(), kWKCredentialPersistenceForSession));
1368 WKAuthenticationDecisionListenerUseCredential(decisionListener, credential.get());
1371 void TestController::processDidCrash()
1373 // This function can be called multiple times when crash logs are being saved on Windows, so
1374 // ensure we only print the crashed message once.
1375 if (!m_didPrintWebProcessCrashedMessage) {
1377 pid_t pid = WKPageGetProcessIdentifier(m_mainWebView->page());
1378 // FIXME: Find a way to not hardcode the process name.
1380 const char* processName = "com.apple.WebKit.WebContent";
1381 #elif PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED > 1080
1382 const char* processName = "com.apple.WebKit.WebContent.Development";
1384 const char* processName = "WebProcess";
1386 fprintf(stderr, "#CRASHED - %s (pid %ld)\n", processName, static_cast<long>(pid));
1388 fputs("#CRASHED - WebProcess\n", stderr);
1391 m_didPrintWebProcessCrashedMessage = true;
1394 if (m_shouldExitWhenWebProcessCrashes)
1398 void TestController::simulateWebNotificationClick(uint64_t notificationID)
1400 m_webNotificationProvider.simulateWebNotificationClick(notificationID);
1403 void TestController::setGeolocationPermission(bool enabled)
1405 m_isGeolocationPermissionSet = true;
1406 m_isGeolocationPermissionAllowed = enabled;
1407 decidePolicyForGeolocationPermissionRequestIfPossible();
1410 void TestController::setMockGeolocationPosition(double latitude, double longitude, double accuracy, bool providesAltitude, double altitude, bool providesAltitudeAccuracy, double altitudeAccuracy, bool providesHeading, double heading, bool providesSpeed, double speed)
1412 m_geolocationProvider->setPosition(latitude, longitude, accuracy, providesAltitude, altitude, providesAltitudeAccuracy, altitudeAccuracy, providesHeading, heading, providesSpeed, speed);
1415 void TestController::setMockGeolocationPositionUnavailableError(WKStringRef errorMessage)
1417 m_geolocationProvider->setPositionUnavailableError(errorMessage);
1420 void TestController::handleGeolocationPermissionRequest(WKGeolocationPermissionRequestRef geolocationPermissionRequest)
1422 m_geolocationPermissionRequests.append(geolocationPermissionRequest);
1423 decidePolicyForGeolocationPermissionRequestIfPossible();
1426 void TestController::setUserMediaPermission(bool enabled)
1428 m_isUserMediaPermissionSet = true;
1429 m_isUserMediaPermissionAllowed = enabled;
1430 decidePolicyForUserMediaPermissionRequestIfPossible();
1433 void TestController::handleUserMediaPermissionRequest(WKUserMediaPermissionRequestRef userMediaPermissionRequest)
1435 m_userMediaPermissionRequests.append(userMediaPermissionRequest);
1436 decidePolicyForUserMediaPermissionRequestIfPossible();
1439 void TestController::decidePolicyForUserMediaPermissionRequestIfPossible()
1441 if (!m_isUserMediaPermissionSet)
1444 for (auto& request : m_userMediaPermissionRequests) {
1445 if (m_isUserMediaPermissionAllowed)
1446 WKUserMediaPermissionRequestAllow(request.get());
1448 WKUserMediaPermissionRequestDeny(request.get());
1450 m_userMediaPermissionRequests.clear();
1453 void TestController::setCustomPolicyDelegate(bool enabled, bool permissive)
1455 m_policyDelegateEnabled = enabled;
1456 m_policyDelegatePermissive = permissive;
1459 void TestController::decidePolicyForGeolocationPermissionRequestIfPossible()
1461 if (!m_isGeolocationPermissionSet)
1464 for (size_t i = 0; i < m_geolocationPermissionRequests.size(); ++i) {
1465 WKGeolocationPermissionRequestRef permissionRequest = m_geolocationPermissionRequests[i].get();
1466 if (m_isGeolocationPermissionAllowed)
1467 WKGeolocationPermissionRequestAllow(permissionRequest);
1469 WKGeolocationPermissionRequestDeny(permissionRequest);
1471 m_geolocationPermissionRequests.clear();
1474 void TestController::decidePolicyForNotificationPermissionRequest(WKPageRef page, WKSecurityOriginRef origin, WKNotificationPermissionRequestRef request, const void*)
1476 TestController::shared().decidePolicyForNotificationPermissionRequest(page, origin, request);
1479 void TestController::decidePolicyForNotificationPermissionRequest(WKPageRef, WKSecurityOriginRef, WKNotificationPermissionRequestRef request)
1481 WKNotificationPermissionRequestAllow(request);
1484 void TestController::unavailablePluginButtonClicked(WKPageRef, WKPluginUnavailabilityReason, WKDictionaryRef, const void*)
1486 printf("MISSING PLUGIN BUTTON PRESSED\n");
1489 void TestController::decidePolicyForNavigationAction(WKPageRef, WKFrameRef, WKFrameNavigationType, WKEventModifiers, WKEventMouseButton, WKFrameRef, WKURLRequestRef, WKFramePolicyListenerRef listener, WKTypeRef, const void* clientInfo)
1491 static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForNavigationAction(listener);
1494 void TestController::decidePolicyForNavigationAction(WKFramePolicyListenerRef listener)
1496 if (m_policyDelegateEnabled && !m_policyDelegatePermissive) {
1497 WKFramePolicyListenerIgnore(listener);
1501 WKFramePolicyListenerUse(listener);
1504 void TestController::decidePolicyForResponse(WKPageRef, WKFrameRef frame, WKURLResponseRef response, WKURLRequestRef, bool canShowMIMEType, WKFramePolicyListenerRef listener, WKTypeRef, const void* clientInfo)
1506 static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForResponse(frame, response, listener);
1509 void TestController::decidePolicyForResponse(WKFrameRef frame, WKURLResponseRef response, WKFramePolicyListenerRef listener)
1511 // Even though Response was already checked by WKBundlePagePolicyClient, the check did not include plugins
1512 // so we have to re-check again.
1513 WKRetainPtr<WKStringRef> wkMIMEType(AdoptWK, WKURLResponseCopyMIMEType(response));
1514 if (WKFrameCanShowMIMEType(frame, wkMIMEType.get())) {
1515 WKFramePolicyListenerUse(listener);
1519 WKFramePolicyListenerIgnore(listener);
1522 void TestController::didNavigateWithNavigationData(WKContextRef, WKPageRef, WKNavigationDataRef navigationData, WKFrameRef frame, const void* clientInfo)
1524 static_cast<TestController*>(const_cast<void*>(clientInfo))->didNavigateWithNavigationData(navigationData, frame);
1527 void TestController::didNavigateWithNavigationData(WKNavigationDataRef navigationData, WKFrameRef)
1529 if (m_state != RunningTest)
1532 if (!m_shouldLogHistoryClientCallbacks)
1536 WKRetainPtr<WKURLRef> urlWK = adoptWK(WKNavigationDataCopyURL(navigationData));
1537 WKRetainPtr<WKStringRef> urlStringWK = adoptWK(WKURLCopyString(urlWK.get()));
1539 WKRetainPtr<WKStringRef> titleWK = adoptWK(WKNavigationDataCopyTitle(navigationData));
1541 WKRetainPtr<WKURLRequestRef> requestWK = adoptWK(WKNavigationDataCopyOriginalRequest(navigationData));
1542 WKRetainPtr<WKStringRef> methodWK = adoptWK(WKURLRequestCopyHTTPMethod(requestWK.get()));
1544 // FIXME: Determine whether the navigation was successful / a client redirect rather than hard-coding the message here.
1545 m_currentInvocation->outputText(String::format("WebView navigated to url \"%s\" with title \"%s\" with HTTP equivalent method \"%s\". The navigation was successful and was not a client redirect.\n",
1546 toSTD(urlStringWK).c_str(), toSTD(titleWK).c_str(), toSTD(methodWK).c_str()));
1549 void TestController::didPerformClientRedirect(WKContextRef, WKPageRef, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void* clientInfo)
1551 static_cast<TestController*>(const_cast<void*>(clientInfo))->didPerformClientRedirect(sourceURL, destinationURL, frame);
1554 void TestController::didPerformClientRedirect(WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef)
1556 if (m_state != RunningTest)
1559 if (!m_shouldLogHistoryClientCallbacks)
1562 WKRetainPtr<WKStringRef> sourceStringWK = adoptWK(WKURLCopyString(sourceURL));
1563 WKRetainPtr<WKStringRef> destinationStringWK = adoptWK(WKURLCopyString(destinationURL));
1565 m_currentInvocation->outputText(String::format("WebView performed a client redirect from \"%s\" to \"%s\".\n", toSTD(sourceStringWK).c_str(), toSTD(destinationStringWK).c_str()));
1568 void TestController::didPerformServerRedirect(WKContextRef, WKPageRef, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void* clientInfo)
1570 static_cast<TestController*>(const_cast<void*>(clientInfo))->didPerformServerRedirect(sourceURL, destinationURL, frame);
1573 void TestController::didPerformServerRedirect(WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef)
1575 if (m_state != RunningTest)
1578 if (!m_shouldLogHistoryClientCallbacks)
1581 WKRetainPtr<WKStringRef> sourceStringWK = adoptWK(WKURLCopyString(sourceURL));
1582 WKRetainPtr<WKStringRef> destinationStringWK = adoptWK(WKURLCopyString(destinationURL));
1584 m_currentInvocation->outputText(String::format("WebView performed a server redirect from \"%s\" to \"%s\".\n", toSTD(sourceStringWK).c_str(), toSTD(destinationStringWK).c_str()));
1587 void TestController::didUpdateHistoryTitle(WKContextRef, WKPageRef, WKStringRef title, WKURLRef URL, WKFrameRef frame, const void* clientInfo)
1589 static_cast<TestController*>(const_cast<void*>(clientInfo))->didUpdateHistoryTitle(title, URL, frame);
1592 void TestController::didUpdateHistoryTitle(WKStringRef title, WKURLRef URL, WKFrameRef)
1594 if (m_state != RunningTest)
1597 if (!m_shouldLogHistoryClientCallbacks)
1600 WKRetainPtr<WKStringRef> urlStringWK(AdoptWK, WKURLCopyString(URL));
1601 m_currentInvocation->outputText(String::format("WebView updated the title for history URL \"%s\" to \"%s\".\n", toSTD(urlStringWK).c_str(), toSTD(title).c_str()));