summaryrefslogtreecommitdiff
path: root/Tools/WebKitTestRunner/TestController.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/WebKitTestRunner/TestController.cpp')
-rw-r--r--Tools/WebKitTestRunner/TestController.cpp1653
1 files changed, 1653 insertions, 0 deletions
diff --git a/Tools/WebKitTestRunner/TestController.cpp b/Tools/WebKitTestRunner/TestController.cpp
new file mode 100644
index 000000000..8da43e650
--- /dev/null
+++ b/Tools/WebKitTestRunner/TestController.cpp
@@ -0,0 +1,1653 @@
+/*
+ * Copyright (C) 2010, 2014-2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "TestController.h"
+
+#include "EventSenderProxy.h"
+#include "Options.h"
+#include "PlatformWebView.h"
+#include "StringFunctions.h"
+#include "TestInvocation.h"
+#include <WebKit/WKAuthenticationChallenge.h>
+#include <WebKit/WKAuthenticationDecisionListener.h>
+#include <WebKit/WKContextConfigurationRef.h>
+#include <WebKit/WKContextPrivate.h>
+#include <WebKit/WKCookieManager.h>
+#include <WebKit/WKCredential.h>
+#include <WebKit/WKIconDatabase.h>
+#include <WebKit/WKNavigationResponseRef.h>
+#include <WebKit/WKNotification.h>
+#include <WebKit/WKNotificationManager.h>
+#include <WebKit/WKNotificationPermissionRequest.h>
+#include <WebKit/WKNumber.h>
+#include <WebKit/WKPageGroup.h>
+#include <WebKit/WKPageInjectedBundleClient.h>
+#include <WebKit/WKPagePrivate.h>
+#include <WebKit/WKPluginInformation.h>
+#include <WebKit/WKPreferencesRefPrivate.h>
+#include <WebKit/WKProtectionSpace.h>
+#include <WebKit/WKRetainPtr.h>
+#include <algorithm>
+#include <cstdio>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string>
+#include <wtf/text/CString.h>
+
+#if PLATFORM(COCOA)
+#include <WebKit/WKContextPrivateMac.h>
+#include <WebKit/WKPagePrivateMac.h>
+#endif
+
+#if !PLATFORM(COCOA)
+#include <WebKit/WKTextChecker.h>
+#endif
+
+namespace WTR {
+
+const unsigned TestController::viewWidth = 800;
+const unsigned TestController::viewHeight = 600;
+
+const unsigned TestController::w3cSVGViewWidth = 480;
+const unsigned TestController::w3cSVGViewHeight = 360;
+
+#if ASAN_ENABLED
+const double TestController::shortTimeout = 10.0;
+#else
+const double TestController::shortTimeout = 5.0;
+#endif
+
+const double TestController::noTimeout = -1;
+
+static WKURLRef blankURL()
+{
+ static WKURLRef staticBlankURL = WKURLCreateWithUTF8CString("about:blank");
+ return staticBlankURL;
+}
+
+static WKDataRef copyWebCryptoMasterKey(WKPageRef, const void*)
+{
+ // Any 128 bit key would do, all we need for testing is to implement the callback.
+ return WKDataCreate((const uint8_t*)"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", 16);
+}
+
+static TestController* controller;
+
+TestController& TestController::singleton()
+{
+ ASSERT(controller);
+ return *controller;
+}
+
+TestController::TestController(int argc, const char* argv[])
+ : m_verbose(false)
+ , m_printSeparators(false)
+ , m_usingServerMode(false)
+ , m_gcBetweenTests(false)
+ , m_shouldDumpPixelsForAllTests(false)
+ , m_state(Initial)
+ , m_doneResetting(false)
+ , m_useWaitToDumpWatchdogTimer(true)
+ , m_forceNoTimeout(false)
+ , m_didPrintWebProcessCrashedMessage(false)
+ , m_shouldExitWhenWebProcessCrashes(true)
+ , m_beforeUnloadReturnValue(true)
+ , m_isGeolocationPermissionSet(false)
+ , m_isGeolocationPermissionAllowed(false)
+ , m_policyDelegateEnabled(false)
+ , m_policyDelegatePermissive(false)
+ , m_handlesAuthenticationChallenges(false)
+ , m_shouldBlockAllPlugins(false)
+ , m_forceComplexText(false)
+ , m_shouldUseAcceleratedDrawing(false)
+ , m_shouldUseRemoteLayerTree(false)
+ , m_shouldLogHistoryClientCallbacks(false)
+ , m_shouldShowWebView(false)
+{
+ initialize(argc, argv);
+ controller = this;
+ run();
+ controller = 0;
+}
+
+TestController::~TestController()
+{
+ WKIconDatabaseClose(WKContextGetIconDatabase(m_context.get()));
+
+ platformDestroy();
+}
+
+static WKRect getWindowFrame(WKPageRef page, const void* clientInfo)
+{
+ PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
+ return view->windowFrame();
+}
+
+static void setWindowFrame(WKPageRef page, WKRect frame, const void* clientInfo)
+{
+ PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
+ view->setWindowFrame(frame);
+}
+
+static bool runBeforeUnloadConfirmPanel(WKPageRef page, WKStringRef message, WKFrameRef frame, const void*)
+{
+ printf("CONFIRM NAVIGATION: %s\n", toSTD(message).c_str());
+ return TestController::singleton().beforeUnloadReturnValue();
+}
+
+void TestController::runModal(WKPageRef page, const void* clientInfo)
+{
+ PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
+ view->setWindowIsKey(false);
+ runModal(view);
+ view->setWindowIsKey(true);
+}
+
+static void closeOtherPage(WKPageRef page, const void* clientInfo)
+{
+ WKPageClose(page);
+ PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
+ delete view;
+}
+
+static void focus(WKPageRef page, const void* clientInfo)
+{
+ PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
+ view->focus();
+ view->setWindowIsKey(true);
+}
+
+static void unfocus(WKPageRef page, const void* clientInfo)
+{
+ PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
+ view->setWindowIsKey(false);
+}
+
+static void decidePolicyForGeolocationPermissionRequest(WKPageRef, WKFrameRef, WKSecurityOriginRef, WKGeolocationPermissionRequestRef permissionRequest, const void* clientInfo)
+{
+ TestController::singleton().handleGeolocationPermissionRequest(permissionRequest);
+}
+
+static void decidePolicyForUserMediaPermissionRequest(WKPageRef, WKFrameRef, WKSecurityOriginRef, WKUserMediaPermissionRequestRef permissionRequest, const void* clientInfo)
+{
+ TestController::singleton().handleUserMediaPermissionRequest(permissionRequest);
+}
+
+WKPageRef TestController::createOtherPage(WKPageRef oldPage, WKURLRequestRef, WKDictionaryRef, WKEventModifiers, WKEventMouseButton, const void* clientInfo)
+{
+ PlatformWebView* parentView = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
+
+ PlatformWebView* view = new PlatformWebView(WKPageGetContext(oldPage), WKPageGetPageGroup(oldPage), oldPage, parentView->options());
+ WKPageRef newPage = view->page();
+
+ view->resizeTo(800, 600);
+
+ WKPageUIClientV5 otherPageUIClient = {
+ { 5, view },
+ 0, // createNewPage_deprecatedForUseWithV0
+ 0, // showPage
+ closeOtherPage,
+ 0, // takeFocus
+ focus,
+ unfocus,
+ 0, // runJavaScriptAlert_deprecatedForUseWithV0
+ 0, // runJavaScriptAlert_deprecatedForUseWithV0
+ 0, // runJavaScriptAlert_deprecatedForUseWithV0
+ 0, // setStatusText
+ 0, // mouseDidMoveOverElement_deprecatedForUseWithV0
+ 0, // missingPluginButtonClicked
+ 0, // didNotHandleKeyEvent
+ 0, // didNotHandleWheelEvent
+ 0, // toolbarsAreVisible
+ 0, // setToolbarsAreVisible
+ 0, // menuBarIsVisible
+ 0, // setMenuBarIsVisible
+ 0, // statusBarIsVisible
+ 0, // setStatusBarIsVisible
+ 0, // isResizable
+ 0, // setIsResizable
+ getWindowFrame,
+ setWindowFrame,
+ runBeforeUnloadConfirmPanel,
+ 0, // didDraw
+ 0, // pageDidScroll
+ 0, // exceededDatabaseQuota
+ 0, // runOpenPanel
+ decidePolicyForGeolocationPermissionRequest,
+ 0, // headerHeight
+ 0, // footerHeight
+ 0, // drawHeader
+ 0, // drawFooter
+ 0, // printFrame
+ runModal,
+ 0, // didCompleteRubberBandForMainFrame
+ 0, // saveDataToFileInDownloadsFolder
+ 0, // shouldInterruptJavaScript
+ createOtherPage,
+ 0, // mouseDidMoveOverElement
+ 0, // decidePolicyForNotificationPermissionRequest
+ 0, // unavailablePluginButtonClicked_deprecatedForUseWithV1
+ 0, // showColorPicker
+ 0, // hideColorPicker
+ 0, // unavailablePluginButtonClicked
+ 0, // pinnedStateDidChange
+ 0, // didBeginTrackingPotentialLongMousePress
+ 0, // didRecognizeLongMousePress
+ 0, // didCancelTrackingPotentialLongMousePress
+ 0, // isPlayingAudioDidChange
+ decidePolicyForUserMediaPermissionRequest,
+ 0, // didClickAutofillButton
+ 0, // runJavaScriptAlert
+ 0, // runJavaScriptConfirm
+ 0, // runJavaScriptPrompt
+ 0, // mediaSessionMetadataDidChange
+ };
+ WKPageSetPageUIClient(newPage, &otherPageUIClient.base);
+
+ WKPageNavigationClientV0 pageNavigationClient = {
+ { 0, &TestController::singleton() },
+ decidePolicyForNavigationAction,
+ decidePolicyForNavigationResponse,
+ decidePolicyForPluginLoad,
+ 0, // didStartProvisionalNavigation
+ 0, // didReceiveServerRedirectForProvisionalNavigation
+ 0, // didFailProvisionalNavigation
+ 0, // didCommitNavigation
+ 0, // didFinishNavigation
+ 0, // didFailNavigation
+ 0, // didFailProvisionalLoadInSubframe
+ 0, // didFinishDocumentLoad
+ 0, // didSameDocumentNavigation
+ 0, // renderingProgressDidChange
+ canAuthenticateAgainstProtectionSpace,
+ didReceiveAuthenticationChallenge,
+ processDidCrash,
+ copyWebCryptoMasterKey,
+ };
+ WKPageSetPageNavigationClient(newPage, &pageNavigationClient.base);
+
+ view->didInitializeClients();
+
+ TestController::singleton().updateWindowScaleForTest(view, *TestController::singleton().m_currentInvocation);
+
+ WKRetain(newPage);
+ return newPage;
+}
+
+const char* TestController::libraryPathForTesting()
+{
+ // FIXME: This may not be sufficient to prevent interactions/crashes
+ // when running more than one copy of DumpRenderTree.
+ // See https://bugs.webkit.org/show_bug.cgi?id=10906
+ char* dumpRenderTreeTemp = getenv("DUMPRENDERTREE_TEMP");
+ if (dumpRenderTreeTemp)
+ return dumpRenderTreeTemp;
+ return platformLibraryPathForTesting();
+}
+
+
+void TestController::initialize(int argc, const char* argv[])
+{
+ platformInitialize();
+
+ Options options;
+ OptionsHandler optionsHandler(options);
+
+ if (argc < 2) {
+ optionsHandler.printHelp();
+ exit(1);
+ }
+ if (!optionsHandler.parse(argc, argv))
+ exit(1);
+
+ m_useWaitToDumpWatchdogTimer = options.useWaitToDumpWatchdogTimer;
+ m_forceNoTimeout = options.forceNoTimeout;
+ m_verbose = options.verbose;
+ m_gcBetweenTests = options.gcBetweenTests;
+ m_shouldDumpPixelsForAllTests = options.shouldDumpPixelsForAllTests;
+ m_forceComplexText = options.forceComplexText;
+ m_shouldUseAcceleratedDrawing = options.shouldUseAcceleratedDrawing;
+ m_shouldUseRemoteLayerTree = options.shouldUseRemoteLayerTree;
+ m_paths = options.paths;
+ m_allowedHosts = options.allowedHosts;
+ m_shouldShowWebView = options.shouldShowWebView;
+
+ if (options.printSupportedFeatures) {
+ // FIXME: On Windows, DumpRenderTree uses this to expose whether it supports 3d
+ // transforms and accelerated compositing. When we support those features, we
+ // should match DRT's behavior.
+ exit(0);
+ }
+
+ m_usingServerMode = (m_paths.size() == 1 && m_paths[0] == "-");
+ if (m_usingServerMode)
+ m_printSeparators = true;
+ else
+ m_printSeparators = m_paths.size() > 1;
+
+ initializeInjectedBundlePath();
+ initializeTestPluginDirectory();
+
+ WKRetainPtr<WKStringRef> pageGroupIdentifier(AdoptWK, WKStringCreateWithUTF8CString("WebKitTestRunnerPageGroup"));
+ m_pageGroup.adopt(WKPageGroupCreateWithIdentifier(pageGroupIdentifier.get()));
+
+ auto configuration = adoptWK(WKContextConfigurationCreate());
+ WKContextConfigurationSetInjectedBundlePath(configuration.get(), injectedBundlePath());
+
+ if (const char* dumpRenderTreeTemp = libraryPathForTesting()) {
+ String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp);
+
+ const char separator = '/';
+
+ WKContextConfigurationSetApplicationCacheDirectory(configuration.get(), toWK(temporaryFolder + separator + "ApplicationCache").get());
+ WKContextConfigurationSetDiskCacheDirectory(configuration.get(), toWK(temporaryFolder + separator + "Cache").get());
+ WKContextConfigurationSetIndexedDBDatabaseDirectory(configuration.get(), toWK(temporaryFolder + separator + "Databases" + separator + "IndexedDB").get());
+ WKContextConfigurationSetLocalStorageDirectory(configuration.get(), toWK(temporaryFolder + separator + "LocalStorage").get());
+ WKContextConfigurationSetWebSQLDatabaseDirectory(configuration.get(), toWK(temporaryFolder + separator + "Databases" + separator + "WebSQL").get());
+ WKContextConfigurationSetMediaKeysStorageDirectory(configuration.get(), toWK(temporaryFolder + separator + "MediaKeys").get());
+ }
+
+ m_context = adoptWK(WKContextCreateWithConfiguration(configuration.get()));
+ m_geolocationProvider = std::make_unique<GeolocationProviderMock>(m_context.get());
+
+#if PLATFORM(EFL)
+ WKContextSetUsesNetworkProcess(m_context.get(), false);
+ WKContextSetProcessModel(m_context.get(), kWKProcessModelSharedSecondaryProcess);
+#endif
+
+ if (const char* dumpRenderTreeTemp = libraryPathForTesting()) {
+ String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp);
+
+ // FIXME: This should be migrated to WKContextConfigurationRef.
+ // Disable icon database to avoid fetching <http://127.0.0.1:8000/favicon.ico> and making tests flaky.
+ // Invividual tests can enable it using testRunner.setIconDatabaseEnabled, although it's not currently supported in WebKitTestRunner.
+ WKContextSetIconDatabasePath(m_context.get(), toWK(emptyString()).get());
+ }
+
+ WKContextUseTestingNetworkSession(m_context.get());
+ WKContextSetCacheModel(m_context.get(), kWKCacheModelDocumentBrowser);
+
+ platformInitializeContext();
+
+ WKContextInjectedBundleClientV1 injectedBundleClient = {
+ { 1, this },
+ didReceiveMessageFromInjectedBundle,
+ didReceiveSynchronousMessageFromInjectedBundle,
+ 0 // getInjectedBundleInitializationUserData
+ };
+ WKContextSetInjectedBundleClient(m_context.get(), &injectedBundleClient.base);
+
+ WKContextClientV1 contextClient = {
+ { 1, this },
+ 0, // plugInAutoStartOriginHashesChanged
+ networkProcessDidCrash,
+ 0, // plugInInformationBecameAvailable
+ 0, // copyWebCryptoMasterKey
+ };
+ WKContextSetClient(m_context.get(), &contextClient.base);
+
+ WKContextHistoryClientV0 historyClient = {
+ { 0, this },
+ didNavigateWithNavigationData,
+ didPerformClientRedirect,
+ didPerformServerRedirect,
+ didUpdateHistoryTitle,
+ 0, // populateVisitedLinks
+ };
+ WKContextSetHistoryClient(m_context.get(), &historyClient.base);
+
+ WKNotificationManagerRef notificationManager = WKContextGetNotificationManager(m_context.get());
+ WKNotificationProviderV0 notificationKit = m_webNotificationProvider.provider();
+ WKNotificationManagerSetProvider(notificationManager, &notificationKit.base);
+
+ if (testPluginDirectory())
+ WKContextSetAdditionalPluginsDirectory(m_context.get(), testPluginDirectory());
+
+ if (m_forceComplexText)
+ WKContextSetAlwaysUsesComplexTextCodePath(m_context.get(), true);
+
+ // Some preferences (notably mock scroll bars setting) currently cannot be re-applied to an existing view, so we need to set them now.
+ resetPreferencesToConsistentValues();
+
+ ViewOptions viewOptions;
+ viewOptions.useRemoteLayerTree = m_shouldUseRemoteLayerTree;
+ viewOptions.shouldShowWebView = m_shouldShowWebView;
+
+ createWebViewWithOptions(viewOptions);
+}
+
+void TestController::createWebViewWithOptions(const ViewOptions& options)
+{
+ m_mainWebView = std::make_unique<PlatformWebView>(m_context.get(), m_pageGroup.get(), nullptr, options);
+ WKPageUIClientV5 pageUIClient = {
+ { 5, m_mainWebView.get() },
+ 0, // createNewPage_deprecatedForUseWithV0
+ 0, // showPage
+ 0, // close
+ 0, // takeFocus
+ focus,
+ unfocus,
+ 0, // runJavaScriptAlert_deprecatedForUseWithV0
+ 0, // runJavaScriptAlert_deprecatedForUseWithV0
+ 0, // runJavaScriptAlert_deprecatedForUseWithV0
+ 0, // setStatusText
+ 0, // mouseDidMoveOverElement_deprecatedForUseWithV0
+ 0, // missingPluginButtonClicked
+ 0, // didNotHandleKeyEvent
+ 0, // didNotHandleWheelEvent
+ 0, // toolbarsAreVisible
+ 0, // setToolbarsAreVisible
+ 0, // menuBarIsVisible
+ 0, // setMenuBarIsVisible
+ 0, // statusBarIsVisible
+ 0, // setStatusBarIsVisible
+ 0, // isResizable
+ 0, // setIsResizable
+ getWindowFrame,
+ setWindowFrame,
+ runBeforeUnloadConfirmPanel,
+ 0, // didDraw
+ 0, // pageDidScroll
+ 0, // exceededDatabaseQuota,
+ 0, // runOpenPanel
+ decidePolicyForGeolocationPermissionRequest,
+ 0, // headerHeight
+ 0, // footerHeight
+ 0, // drawHeader
+ 0, // drawFooter
+ 0, // printFrame
+ runModal,
+ 0, // didCompleteRubberBandForMainFrame
+ 0, // saveDataToFileInDownloadsFolder
+ 0, // shouldInterruptJavaScript
+ createOtherPage,
+ 0, // mouseDidMoveOverElement
+ decidePolicyForNotificationPermissionRequest, // decidePolicyForNotificationPermissionRequest
+ 0, // unavailablePluginButtonClicked_deprecatedForUseWithV1
+ 0, // showColorPicker
+ 0, // hideColorPicker
+ unavailablePluginButtonClicked,
+ 0, // pinnedStateDidChange
+ 0, // didBeginTrackingPotentialLongMousePress
+ 0, // didRecognizeLongMousePress
+ 0, // didCancelTrackingPotentialLongMousePress
+ 0, // isPlayingAudioDidChange
+ decidePolicyForUserMediaPermissionRequest,
+ 0, // didClickAutofillButton
+ 0, // runJavaScriptAlert
+ 0, // runJavaScriptConfirm
+ 0, // runJavaScriptPrompt
+ 0, // mediaSessionMetadataDidChange
+ };
+ WKPageSetPageUIClient(m_mainWebView->page(), &pageUIClient.base);
+
+ WKPageNavigationClientV0 pageNavigationClient = {
+ { 0, this },
+ decidePolicyForNavigationAction,
+ decidePolicyForNavigationResponse,
+ decidePolicyForPluginLoad,
+ 0, // didStartProvisionalNavigation
+ 0, // didReceiveServerRedirectForProvisionalNavigation
+ 0, // didFailProvisionalNavigation
+ didCommitNavigation,
+ didFinishNavigation,
+ 0, // didFailNavigation
+ 0, // didFailProvisionalLoadInSubframe
+ 0, // didFinishDocumentLoad
+ 0, // didSameDocumentNavigation
+ 0, // renderingProgressDidChange
+ canAuthenticateAgainstProtectionSpace,
+ didReceiveAuthenticationChallenge,
+ processDidCrash,
+ copyWebCryptoMasterKey,
+ };
+ WKPageSetPageNavigationClient(m_mainWebView->page(), &pageNavigationClient.base);
+
+
+ // this should just be done on the page?
+ WKPageInjectedBundleClientV0 injectedBundleClient = {
+ { 0, this },
+ didReceivePageMessageFromInjectedBundle,
+ didReceiveSynchronousPageMessageFromInjectedBundle
+ };
+ WKPageSetPageInjectedBundleClient(m_mainWebView->page(), &injectedBundleClient.base);
+
+ m_mainWebView->didInitializeClients();
+
+ // Generally, the tests should default to running at 1x. updateWindowScaleForTest() will adjust the scale to
+ // something else for specific tests that need to run at a different window scale.
+ m_mainWebView->changeWindowScaleIfNeeded(1);
+}
+
+void TestController::ensureViewSupportsOptions(const ViewOptions& options)
+{
+ if (m_mainWebView && !m_mainWebView->viewSupportsOptions(options)) {
+ WKPageSetPageUIClient(m_mainWebView->page(), 0);
+ WKPageSetPageNavigationClient(m_mainWebView->page(), 0);
+ WKPageClose(m_mainWebView->page());
+
+ m_mainWebView = nullptr;
+
+ createWebViewWithOptions(options);
+ resetStateToConsistentValues();
+ }
+}
+
+void TestController::resetPreferencesToConsistentValues()
+{
+ // Reset preferences
+ WKPreferencesRef preferences = WKPageGroupGetPreferences(m_pageGroup.get());
+ WKPreferencesResetTestRunnerOverrides(preferences);
+ WKPreferencesSetPageVisibilityBasedProcessSuppressionEnabled(preferences, false);
+ WKPreferencesSetOfflineWebApplicationCacheEnabled(preferences, true);
+ WKPreferencesSetFontSmoothingLevel(preferences, kWKFontSmoothingLevelNoSubpixelAntiAliasing);
+ WKPreferencesSetAntialiasedFontDilationEnabled(preferences, false);
+ WKPreferencesSetXSSAuditorEnabled(preferences, false);
+ WKPreferencesSetWebAudioEnabled(preferences, true);
+ WKPreferencesSetMediaStreamEnabled(preferences, true);
+ WKPreferencesSetDeveloperExtrasEnabled(preferences, true);
+ WKPreferencesSetJavaScriptRuntimeFlags(preferences, kWKJavaScriptRuntimeFlagsAllEnabled);
+ WKPreferencesSetJavaScriptCanOpenWindowsAutomatically(preferences, true);
+ WKPreferencesSetJavaScriptCanAccessClipboard(preferences, true);
+ WKPreferencesSetDOMPasteAllowed(preferences, true);
+ WKPreferencesSetUniversalAccessFromFileURLsAllowed(preferences, true);
+ WKPreferencesSetFileAccessFromFileURLsAllowed(preferences, true);
+#if ENABLE(FULLSCREEN_API)
+ WKPreferencesSetFullScreenEnabled(preferences, true);
+#endif
+ WKPreferencesSetPageCacheEnabled(preferences, false);
+ WKPreferencesSetAsynchronousPluginInitializationEnabled(preferences, false);
+ WKPreferencesSetAsynchronousPluginInitializationEnabledForAllPlugins(preferences, false);
+ WKPreferencesSetArtificialPluginInitializationDelayEnabled(preferences, false);
+ WKPreferencesSetTabToLinksEnabled(preferences, false);
+ WKPreferencesSetInteractiveFormValidationEnabled(preferences, true);
+ WKPreferencesSetMockScrollbarsEnabled(preferences, true);
+
+ static WKStringRef defaultTextEncoding = WKStringCreateWithUTF8CString("ISO-8859-1");
+ WKPreferencesSetDefaultTextEncodingName(preferences, defaultTextEncoding);
+
+ static WKStringRef standardFontFamily = WKStringCreateWithUTF8CString("Times");
+ static WKStringRef cursiveFontFamily = WKStringCreateWithUTF8CString("Apple Chancery");
+ static WKStringRef fantasyFontFamily = WKStringCreateWithUTF8CString("Papyrus");
+ static WKStringRef fixedFontFamily = WKStringCreateWithUTF8CString("Courier");
+ static WKStringRef pictographFontFamily = WKStringCreateWithUTF8CString("Apple Color Emoji");
+ static WKStringRef sansSerifFontFamily = WKStringCreateWithUTF8CString("Helvetica");
+ static WKStringRef serifFontFamily = WKStringCreateWithUTF8CString("Times");
+
+ WKPreferencesSetStandardFontFamily(preferences, standardFontFamily);
+ WKPreferencesSetCursiveFontFamily(preferences, cursiveFontFamily);
+ WKPreferencesSetFantasyFontFamily(preferences, fantasyFontFamily);
+ WKPreferencesSetFixedFontFamily(preferences, fixedFontFamily);
+ WKPreferencesSetPictographFontFamily(preferences, pictographFontFamily);
+ WKPreferencesSetSansSerifFontFamily(preferences, sansSerifFontFamily);
+ WKPreferencesSetSerifFontFamily(preferences, serifFontFamily);
+ WKPreferencesSetAsynchronousSpellCheckingEnabled(preferences, false);
+#if ENABLE(WEB_AUDIO)
+ WKPreferencesSetMediaSourceEnabled(preferences, true);
+#endif
+
+ WKPreferencesSetHiddenPageDOMTimerThrottlingEnabled(preferences, false);
+ WKPreferencesSetHiddenPageCSSAnimationSuspensionEnabled(preferences, false);
+
+ WKPreferencesSetAcceleratedDrawingEnabled(preferences, m_shouldUseAcceleratedDrawing);
+
+ WKCookieManagerDeleteAllCookies(WKContextGetCookieManager(m_context.get()));
+
+ platformResetPreferencesToConsistentValues();
+}
+
+bool TestController::resetStateToConsistentValues()
+{
+ m_state = Resetting;
+
+ m_beforeUnloadReturnValue = true;
+
+ WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("Reset"));
+ WKRetainPtr<WKMutableDictionaryRef> resetMessageBody = adoptWK(WKMutableDictionaryCreate());
+
+ WKRetainPtr<WKStringRef> shouldGCKey = adoptWK(WKStringCreateWithUTF8CString("ShouldGC"));
+ WKRetainPtr<WKBooleanRef> shouldGCValue = adoptWK(WKBooleanCreate(m_gcBetweenTests));
+ WKDictionarySetItem(resetMessageBody.get(), shouldGCKey.get(), shouldGCValue.get());
+
+ WKRetainPtr<WKStringRef> allowedHostsKey = adoptWK(WKStringCreateWithUTF8CString("AllowedHosts"));
+ WKRetainPtr<WKMutableArrayRef> allowedHostsValue = adoptWK(WKMutableArrayCreate());
+ for (auto& host : m_allowedHosts) {
+ WKRetainPtr<WKStringRef> wkHost = adoptWK(WKStringCreateWithUTF8CString(host.c_str()));
+ WKArrayAppendItem(allowedHostsValue.get(), wkHost.get());
+ }
+ WKDictionarySetItem(resetMessageBody.get(), allowedHostsKey.get(), allowedHostsValue.get());
+
+ WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), resetMessageBody.get());
+
+ WKContextSetShouldUseFontSmoothing(TestController::singleton().context(), false);
+
+ WKContextSetCacheModel(TestController::singleton().context(), kWKCacheModelDocumentBrowser);
+
+ // FIXME: This function should also ensure that there is only one page open.
+
+ // Reset the EventSender for each test.
+ m_eventSenderProxy = std::make_unique<EventSenderProxy>(this);
+
+ // FIXME: Is this needed? Nothing in TestController changes preferences during tests, and if there is
+ // some other code doing this, it should probably be responsible for cleanup too.
+ resetPreferencesToConsistentValues();
+
+#if !PLATFORM(COCOA)
+ WKTextCheckerContinuousSpellCheckingEnabledStateChanged(true);
+#endif
+
+ // in the case that a test using the chrome input field failed, be sure to clean up for the next test
+ m_mainWebView->removeChromeInputField();
+ m_mainWebView->focus();
+
+ // Re-set to the default backing scale factor by setting the custom scale factor to 0.
+ WKPageSetCustomBackingScaleFactor(m_mainWebView->page(), 0);
+
+ WKPageClearWheelEventTestTrigger(m_mainWebView->page());
+
+#if PLATFORM(EFL)
+ // EFL use a real window while other ports such as Qt don't.
+ // In EFL, we need to resize the window to the original size after calls to window.resizeTo.
+ WKRect rect = m_mainWebView->windowFrame();
+ m_mainWebView->setWindowFrame(WKRectMake(rect.origin.x, rect.origin.y, TestController::viewWidth, TestController::viewHeight));
+#endif
+
+ // Reset notification permissions
+ m_webNotificationProvider.reset();
+
+ // Reset Geolocation permissions.
+ m_geolocationPermissionRequests.clear();
+ m_isGeolocationPermissionSet = false;
+ m_isGeolocationPermissionAllowed = false;
+
+ // Reset UserMedia permissions.
+ m_userMediaPermissionRequests.clear();
+ m_isUserMediaPermissionSet = false;
+ m_isUserMediaPermissionAllowed = false;
+
+ // Reset Custom Policy Delegate.
+ setCustomPolicyDelegate(false, false);
+
+ m_workQueueManager.clearWorkQueue();
+
+ m_handlesAuthenticationChallenges = false;
+ m_authenticationUsername = String();
+ m_authenticationPassword = String();
+
+ m_shouldBlockAllPlugins = false;
+
+ m_shouldLogHistoryClientCallbacks = false;
+
+ WKPageGroupRemoveAllUserContentFilters(WKPageGetPageGroup(m_mainWebView->page()));
+
+ setHidden(false);
+
+ // Reset main page back to about:blank
+ m_doneResetting = false;
+
+ WKPageLoadURL(m_mainWebView->page(), blankURL());
+ runUntil(m_doneResetting, shortTimeout);
+ return m_doneResetting;
+}
+
+void TestController::terminateWebContentProcess()
+{
+ WKPageTerminate(m_mainWebView->page());
+}
+
+void TestController::reattachPageToWebProcess()
+{
+ // Loading a web page is the only way to reattach an existing page to a process.
+ m_doneResetting = false;
+ WKPageLoadURL(m_mainWebView->page(), blankURL());
+ runUntil(m_doneResetting, shortTimeout);
+}
+
+const char* TestController::webProcessName()
+{
+ // FIXME: Find a way to not hardcode the process name.
+#if PLATFORM(COCOA)
+ return "com.apple.WebKit.WebContent.Development";
+#else
+ return "WebProcess";
+#endif
+}
+
+const char* TestController::networkProcessName()
+{
+ // FIXME: Find a way to not hardcode the process name.
+#if PLATFORM(COCOA)
+ return "com.apple.WebKit.Networking.Development";
+#else
+ return "NetworkProcess";
+#endif
+}
+
+void TestController::updateWebViewSizeForTest(const TestInvocation& test)
+{
+ bool isSVGW3CTest = test.urlContains("svg/W3C-SVG-1.1") || test.urlContains("svg\\W3C-SVG-1.1");
+
+ unsigned width = viewWidth;
+ unsigned height = viewHeight;
+ if (isSVGW3CTest) {
+ width = w3cSVGViewWidth;
+ height = w3cSVGViewHeight;
+ }
+
+ mainWebView()->resizeTo(width, height);
+}
+
+void TestController::updateWindowScaleForTest(PlatformWebView* view, const TestInvocation& test)
+{
+ bool needsHighDPIWindow = test.urlContains("/hidpi-");
+ view->changeWindowScaleIfNeeded(needsHighDPIWindow ? 2 : 1);
+}
+
+// FIXME: move into relevant platformConfigureViewForTest()?
+static bool shouldUseFixedLayout(const TestInvocation& test)
+{
+#if ENABLE(CSS_DEVICE_ADAPTATION)
+ if (test.urlContains("device-adapt/") || test.urlContains("device-adapt\\"))
+ return true;
+#endif
+
+#if USE(COORDINATED_GRAPHICS) && PLATFORM(EFL)
+ if (test.urlContains("sticky/") || test.urlContains("sticky\\"))
+ return true;
+#endif
+ return false;
+
+ UNUSED_PARAM(test);
+}
+
+void TestController::updateLayoutTypeForTest(const TestInvocation& test)
+{
+ ViewOptions viewOptions;
+
+ viewOptions.useFixedLayout = shouldUseFixedLayout(test);
+
+ ensureViewSupportsOptions(viewOptions);
+}
+
+#if !PLATFORM(COCOA) && !PLATFORM(GTK)
+void TestController::platformConfigureViewForTest(const TestInvocation&)
+{
+}
+
+void TestController::platformResetPreferencesToConsistentValues()
+{
+}
+#endif
+
+void TestController::configureViewForTest(const TestInvocation& test)
+{
+ updateWebViewSizeForTest(test);
+ updateWindowScaleForTest(mainWebView(), test);
+ updateLayoutTypeForTest(test);
+
+ platformConfigureViewForTest(test);
+}
+
+struct TestCommand {
+ TestCommand() : shouldDumpPixels(false), timeout(0) { }
+
+ std::string pathOrURL;
+ bool shouldDumpPixels;
+ std::string expectedPixelHash;
+ int timeout;
+};
+
+class CommandTokenizer {
+public:
+ explicit CommandTokenizer(const std::string& input)
+ : m_input(input)
+ , m_posNextSeparator(0)
+ {
+ pump();
+ }
+
+ bool hasNext() const;
+ std::string next();
+
+private:
+ void pump();
+ static const char kSeparator = '\'';
+ const std::string& m_input;
+ std::string m_next;
+ size_t m_posNextSeparator;
+};
+
+void CommandTokenizer::pump()
+{
+ if (m_posNextSeparator == std::string::npos || m_posNextSeparator == m_input.size()) {
+ m_next = std::string();
+ return;
+ }
+ size_t start = m_posNextSeparator ? m_posNextSeparator + 1 : 0;
+ m_posNextSeparator = m_input.find(kSeparator, start);
+ size_t size = m_posNextSeparator == std::string::npos ? std::string::npos : m_posNextSeparator - start;
+ m_next = std::string(m_input, start, size);
+}
+
+std::string CommandTokenizer::next()
+{
+ ASSERT(hasNext());
+
+ std::string oldNext = m_next;
+ pump();
+ return oldNext;
+}
+
+bool CommandTokenizer::hasNext() const
+{
+ return !m_next.empty();
+}
+
+NO_RETURN static void die(const std::string& inputLine)
+{
+ fprintf(stderr, "Unexpected input line: %s\n", inputLine.c_str());
+ exit(1);
+}
+
+TestCommand parseInputLine(const std::string& inputLine)
+{
+ TestCommand result;
+ CommandTokenizer tokenizer(inputLine);
+ if (!tokenizer.hasNext())
+ die(inputLine);
+
+ std::string arg = tokenizer.next();
+ result.pathOrURL = arg;
+ while (tokenizer.hasNext()) {
+ arg = tokenizer.next();
+ if (arg == std::string("--timeout")) {
+ std::string timeoutToken = tokenizer.next();
+ result.timeout = atoi(timeoutToken.c_str());
+ } else if (arg == std::string("-p") || arg == std::string("--pixel-test")) {
+ result.shouldDumpPixels = true;
+ if (tokenizer.hasNext())
+ result.expectedPixelHash = tokenizer.next();
+ } else
+ die(inputLine);
+ }
+ return result;
+}
+
+bool TestController::runTest(const char* inputLine)
+{
+ TestCommand command = parseInputLine(std::string(inputLine));
+
+ m_state = RunningTest;
+
+ m_currentInvocation = std::make_unique<TestInvocation>(command.pathOrURL);
+ if (command.shouldDumpPixels || m_shouldDumpPixelsForAllTests)
+ m_currentInvocation->setIsPixelTest(command.expectedPixelHash);
+ if (command.timeout > 0)
+ m_currentInvocation->setCustomTimeout(command.timeout);
+
+ platformWillRunTest(*m_currentInvocation);
+
+ m_currentInvocation->invoke();
+ m_currentInvocation = nullptr;
+
+ return true;
+}
+
+void TestController::runTestingServerLoop()
+{
+ char filenameBuffer[2048];
+ while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
+ char* newLineCharacter = strchr(filenameBuffer, '\n');
+ if (newLineCharacter)
+ *newLineCharacter = '\0';
+
+ if (strlen(filenameBuffer) == 0)
+ continue;
+
+ if (!runTest(filenameBuffer))
+ break;
+ }
+}
+
+void TestController::run()
+{
+ if (!resetStateToConsistentValues()) {
+ TestInvocation::dumpWebProcessUnresponsiveness("<unknown> - TestController::run - Failed to reset state to consistent values\n");
+ return;
+ }
+
+ if (m_usingServerMode)
+ runTestingServerLoop();
+ else {
+ for (size_t i = 0; i < m_paths.size(); ++i) {
+ if (!runTest(m_paths[i].c_str()))
+ break;
+ }
+ }
+}
+
+void TestController::runUntil(bool& done, double timeout)
+{
+ if (m_forceNoTimeout)
+ timeout = noTimeout;
+
+ platformRunUntil(done, timeout);
+}
+
+// WKContextInjectedBundleClient
+
+void TestController::didReceiveMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
+{
+ static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveMessageFromInjectedBundle(messageName, messageBody);
+}
+
+void TestController::didReceiveSynchronousMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, WKTypeRef* returnData, const void* clientInfo)
+{
+ *returnData = static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody).leakRef();
+}
+
+// WKPageInjectedBundleClient
+
+void TestController::didReceivePageMessageFromInjectedBundle(WKPageRef page, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
+{
+ static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveMessageFromInjectedBundle(messageName, messageBody);
+}
+
+void TestController::didReceiveSynchronousPageMessageFromInjectedBundle(WKPageRef page, WKStringRef messageName, WKTypeRef messageBody, WKTypeRef* returnData, const void* clientInfo)
+{
+ *returnData = static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody).leakRef();
+}
+
+void TestController::networkProcessDidCrash(WKContextRef context, const void *clientInfo)
+{
+ static_cast<TestController*>(const_cast<void*>(clientInfo))->networkProcessDidCrash();
+}
+
+void TestController::didReceiveKeyDownMessageFromInjectedBundle(WKDictionaryRef messageBodyDictionary, bool synchronous)
+{
+ WKRetainPtr<WKStringRef> keyKey = adoptWK(WKStringCreateWithUTF8CString("Key"));
+ WKStringRef key = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, keyKey.get()));
+
+ WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
+ WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
+
+ WKRetainPtr<WKStringRef> locationKey = adoptWK(WKStringCreateWithUTF8CString("Location"));
+ unsigned location = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, locationKey.get()))));
+
+ if (synchronous)
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
+
+ m_eventSenderProxy->keyDown(key, modifiers, location);
+
+ if (synchronous)
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+}
+
+void TestController::didReceiveMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
+{
+ if (WKStringIsEqualToUTF8CString(messageName, "EventSender")) {
+ ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
+ WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
+
+ WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
+ WKStringRef subMessageName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, subMessageKey.get()));
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown") || WKStringIsEqualToUTF8CString(subMessageName, "MouseUp")) {
+ WKRetainPtr<WKStringRef> buttonKey = adoptWK(WKStringCreateWithUTF8CString("Button"));
+ unsigned button = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, buttonKey.get()))));
+
+ WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
+ WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
+
+ // Forward to WebProcess
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+ if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown"))
+ m_eventSenderProxy->mouseDown(button, modifiers);
+ else
+ m_eventSenderProxy->mouseUp(button, modifiers);
+
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "KeyDown")) {
+ didReceiveKeyDownMessageFromInjectedBundle(messageBodyDictionary, false);
+
+ return;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "MouseScrollByWithWheelAndMomentumPhases")) {
+ WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
+ double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
+
+ WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
+ double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
+
+ WKRetainPtr<WKStringRef> phaseKey = adoptWK(WKStringCreateWithUTF8CString("Phase"));
+ int phase = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, phaseKey.get()))));
+ WKRetainPtr<WKStringRef> momentumKey = adoptWK(WKStringCreateWithUTF8CString("Momentum"));
+ int momentum = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, momentumKey.get()))));
+
+ // Forward to WebProcess
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+ m_eventSenderProxy->mouseScrollByWithWheelAndMomentumPhases(x, y, phase, momentum);
+
+ return;
+ }
+
+ ASSERT_NOT_REACHED();
+ }
+
+ if (!m_currentInvocation)
+ return;
+
+ m_currentInvocation->didReceiveMessageFromInjectedBundle(messageName, messageBody);
+}
+
+WKRetainPtr<WKTypeRef> TestController::didReceiveSynchronousMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
+{
+ if (WKStringIsEqualToUTF8CString(messageName, "EventSender")) {
+ ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
+ WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
+
+ WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
+ WKStringRef subMessageName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, subMessageKey.get()));
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "KeyDown")) {
+ didReceiveKeyDownMessageFromInjectedBundle(messageBodyDictionary, true);
+
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown") || WKStringIsEqualToUTF8CString(subMessageName, "MouseUp")) {
+ WKRetainPtr<WKStringRef> buttonKey = adoptWK(WKStringCreateWithUTF8CString("Button"));
+ unsigned button = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, buttonKey.get()))));
+
+ WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
+ WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
+
+ // Forward to WebProcess
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
+ if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown"))
+ m_eventSenderProxy->mouseDown(button, modifiers);
+ else
+ m_eventSenderProxy->mouseUp(button, modifiers);
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "MouseMoveTo")) {
+ WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
+ double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
+
+ WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
+ double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
+
+ // Forward to WebProcess
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
+ m_eventSenderProxy->mouseMoveTo(x, y);
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+ return 0;
+ }
+
+#if PLATFORM(MAC)
+ if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceDown")) {
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
+ m_eventSenderProxy->mouseForceDown();
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceUp")) {
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
+ m_eventSenderProxy->mouseForceUp();
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "MouseForceChanged")) {
+ WKRetainPtr<WKStringRef> forceKey = adoptWK(WKStringCreateWithUTF8CString("Force"));
+ double force = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, forceKey.get())));
+
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
+ m_eventSenderProxy->mouseForceChanged(force);
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+ return 0;
+ }
+#endif // PLATFORM(MAC)
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "MouseScrollBy")) {
+ WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
+ double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
+
+ WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
+ double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
+
+ // Forward to WebProcess
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
+ m_eventSenderProxy->mouseScrollBy(x, y);
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "MouseScrollByWithWheelAndMomentumPhases")) {
+ WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
+ double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
+
+ WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
+ double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
+
+ WKRetainPtr<WKStringRef> phaseKey = adoptWK(WKStringCreateWithUTF8CString("Phase"));
+ int phase = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, phaseKey.get()))));
+ WKRetainPtr<WKStringRef> momentumKey = adoptWK(WKStringCreateWithUTF8CString("Momentum"));
+ int momentum = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, momentumKey.get()))));
+
+ // Forward to WebProcess
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
+ m_eventSenderProxy->mouseScrollByWithWheelAndMomentumPhases(x, y, phase, momentum);
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "ContinuousMouseScrollBy")) {
+ WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
+ double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
+
+ WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
+ double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
+
+ WKRetainPtr<WKStringRef> pagedKey = adoptWK(WKStringCreateWithUTF8CString("Paged"));
+ bool paged = static_cast<bool>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, pagedKey.get()))));
+
+ // Forward to WebProcess
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
+ m_eventSenderProxy->continuousMouseScrollBy(x, y, paged);
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "LeapForward")) {
+ WKRetainPtr<WKStringRef> timeKey = adoptWK(WKStringCreateWithUTF8CString("TimeInMilliseconds"));
+ unsigned time = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, timeKey.get()))));
+
+ m_eventSenderProxy->leapForward(time);
+ return 0;
+ }
+
+#if ENABLE(TOUCH_EVENTS)
+ if (WKStringIsEqualToUTF8CString(subMessageName, "AddTouchPoint")) {
+ WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
+ int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
+
+ WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
+ int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
+
+ m_eventSenderProxy->addTouchPoint(x, y);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "UpdateTouchPoint")) {
+ WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
+ int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
+
+ WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
+ int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
+
+ WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
+ int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
+
+ m_eventSenderProxy->updateTouchPoint(index, x, y);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "SetTouchModifier")) {
+ WKRetainPtr<WKStringRef> modifierKey = adoptWK(WKStringCreateWithUTF8CString("Modifier"));
+ WKEventModifiers modifier = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifierKey.get()))));
+
+ WKRetainPtr<WKStringRef> enableKey = adoptWK(WKStringCreateWithUTF8CString("Enable"));
+ bool enable = static_cast<bool>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, enableKey.get()))));
+
+ m_eventSenderProxy->setTouchModifier(modifier, enable);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "SetTouchPointRadius")) {
+ WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("RadiusX"));
+ int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
+
+ WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("RadiusY"));
+ int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
+
+ m_eventSenderProxy->setTouchPointRadius(x, y);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "TouchStart")) {
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
+ m_eventSenderProxy->touchStart();
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "TouchMove")) {
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
+ m_eventSenderProxy->touchMove();
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "TouchEnd")) {
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
+ m_eventSenderProxy->touchEnd();
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "TouchCancel")) {
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
+ m_eventSenderProxy->touchCancel();
+ WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "ClearTouchPoints")) {
+ m_eventSenderProxy->clearTouchPoints();
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "ReleaseTouchPoint")) {
+ WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
+ int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
+ m_eventSenderProxy->releaseTouchPoint(index);
+ return 0;
+ }
+
+ if (WKStringIsEqualToUTF8CString(subMessageName, "CancelTouchPoint")) {
+ WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
+ int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
+ m_eventSenderProxy->cancelTouchPoint(index);
+ return 0;
+ }
+#endif
+ ASSERT_NOT_REACHED();
+ }
+ return m_currentInvocation->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody);
+}
+
+// WKContextClient
+
+void TestController::networkProcessDidCrash()
+{
+#if PLATFORM(COCOA)
+ pid_t pid = WKContextGetNetworkProcessIdentifier(m_context.get());
+ fprintf(stderr, "#CRASHED - %s (pid %ld)\n", networkProcessName(), static_cast<long>(pid));
+#else
+ fprintf(stderr, "#CRASHED - %s\n", networkProcessName());
+#endif
+ exit(1);
+}
+
+// WKPageNavigationClient
+
+void TestController::didCommitNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef, const void* clientInfo)
+{
+ static_cast<TestController*>(const_cast<void*>(clientInfo))->didCommitNavigation(page, navigation);
+}
+
+void TestController::didFinishNavigation(WKPageRef page, WKNavigationRef navigation, WKTypeRef, const void* clientInfo)
+{
+ static_cast<TestController*>(const_cast<void*>(clientInfo))->didFinishNavigation(page, navigation);
+}
+
+bool TestController::canAuthenticateAgainstProtectionSpace(WKPageRef, WKProtectionSpaceRef protectionSpace, const void*)
+{
+ WKProtectionSpaceAuthenticationScheme authenticationScheme = WKProtectionSpaceGetAuthenticationScheme(protectionSpace);
+
+ if (authenticationScheme == kWKProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested) {
+ std::string host = toSTD(adoptWK(WKProtectionSpaceCopyHost(protectionSpace)).get());
+ return host == "localhost" || host == "127.0.0.1";
+ }
+
+ return authenticationScheme <= kWKProtectionSpaceAuthenticationSchemeHTTPDigest;
+}
+
+void TestController::didReceiveAuthenticationChallenge(WKPageRef page, WKAuthenticationChallengeRef authenticationChallenge, const void *clientInfo)
+{
+ static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveAuthenticationChallenge(page, /*frame,*/ authenticationChallenge);
+}
+
+void TestController::processDidCrash(WKPageRef page, const void* clientInfo)
+{
+ static_cast<TestController*>(const_cast<void*>(clientInfo))->processDidCrash();
+}
+
+WKPluginLoadPolicy TestController::decidePolicyForPluginLoad(WKPageRef page, WKPluginLoadPolicy currentPluginLoadPolicy, WKDictionaryRef pluginInformation, WKStringRef* unavailabilityDescription, const void* clientInfo)
+{
+ return static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForPluginLoad(page, currentPluginLoadPolicy, pluginInformation, unavailabilityDescription);
+}
+
+WKPluginLoadPolicy TestController::decidePolicyForPluginLoad(WKPageRef, WKPluginLoadPolicy currentPluginLoadPolicy, WKDictionaryRef pluginInformation, WKStringRef* unavailabilityDescription)
+{
+ if (m_shouldBlockAllPlugins)
+ return kWKPluginLoadPolicyBlocked;
+
+#if PLATFORM(MAC)
+ WKStringRef bundleIdentifier = (WKStringRef)WKDictionaryGetItemForKey(pluginInformation, WKPluginInformationBundleIdentifierKey());
+ if (!bundleIdentifier)
+ return currentPluginLoadPolicy;
+
+ if (WKStringIsEqualToUTF8CString(bundleIdentifier, "com.apple.QuickTime Plugin.plugin"))
+ return currentPluginLoadPolicy;
+
+ if (WKStringIsEqualToUTF8CString(bundleIdentifier, "com.apple.testnetscapeplugin"))
+ return currentPluginLoadPolicy;
+
+ RELEASE_ASSERT_NOT_REACHED(); // Please don't use any other plug-ins in tests, as they will not be installed on all machines.
+#else
+ return currentPluginLoadPolicy;
+#endif
+}
+
+void TestController::didCommitNavigation(WKPageRef page, WKNavigationRef navigation)
+{
+ mainWebView()->focus();
+}
+
+void TestController::didFinishNavigation(WKPageRef page, WKNavigationRef navigation)
+{
+ if (m_state != Resetting)
+ return;
+
+ WKRetainPtr<WKURLRef> wkURL(AdoptWK, WKFrameCopyURL(WKPageGetMainFrame(page)));
+ if (!WKURLIsEqual(wkURL.get(), blankURL()))
+ return;
+
+ m_doneResetting = true;
+ singleton().notifyDone();
+}
+
+void TestController::didReceiveAuthenticationChallenge(WKPageRef page, WKAuthenticationChallengeRef authenticationChallenge)
+{
+ WKProtectionSpaceRef protectionSpace = WKAuthenticationChallengeGetProtectionSpace(authenticationChallenge);
+ WKAuthenticationDecisionListenerRef decisionListener = WKAuthenticationChallengeGetDecisionListener(authenticationChallenge);
+
+ if (WKProtectionSpaceGetAuthenticationScheme(protectionSpace) == kWKProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested) {
+ // Any non-empty credential signals to accept the server trust. Since the cross-platform API
+ // doesn't expose a way to create a credential from server trust, we use a password credential.
+
+ WKRetainPtr<WKCredentialRef> credential = adoptWK(WKCredentialCreate(toWK("accept server trust").get(), toWK("").get(), kWKCredentialPersistenceNone));
+ WKAuthenticationDecisionListenerUseCredential(decisionListener, credential.get());
+ return;
+ }
+
+ std::string host = toSTD(adoptWK(WKProtectionSpaceCopyHost(protectionSpace)).get());
+ int port = WKProtectionSpaceGetPort(protectionSpace);
+ String message = String::format("%s:%d - didReceiveAuthenticationChallenge - ", host.c_str(), port);
+ if (!m_handlesAuthenticationChallenges)
+ message.append("Simulating cancelled authentication sheet\n");
+ else
+ message.append(String::format("Responding with %s:%s\n", m_authenticationUsername.utf8().data(), m_authenticationPassword.utf8().data()));
+ m_currentInvocation->outputText(message);
+
+ if (!m_handlesAuthenticationChallenges) {
+ WKAuthenticationDecisionListenerUseCredential(decisionListener, 0);
+ return;
+ }
+ WKRetainPtr<WKStringRef> username(AdoptWK, WKStringCreateWithUTF8CString(m_authenticationUsername.utf8().data()));
+ WKRetainPtr<WKStringRef> password(AdoptWK, WKStringCreateWithUTF8CString(m_authenticationPassword.utf8().data()));
+ WKRetainPtr<WKCredentialRef> credential(AdoptWK, WKCredentialCreate(username.get(), password.get(), kWKCredentialPersistenceForSession));
+ WKAuthenticationDecisionListenerUseCredential(decisionListener, credential.get());
+}
+
+void TestController::processDidCrash()
+{
+ // This function can be called multiple times when crash logs are being saved on Windows, so
+ // ensure we only print the crashed message once.
+ if (!m_didPrintWebProcessCrashedMessage) {
+#if PLATFORM(COCOA)
+ pid_t pid = WKPageGetProcessIdentifier(m_mainWebView->page());
+ fprintf(stderr, "#CRASHED - %s (pid %ld)\n", webProcessName(), static_cast<long>(pid));
+#else
+ fprintf(stderr, "#CRASHED - %s\n", webProcessName());
+#endif
+ fflush(stderr);
+ m_didPrintWebProcessCrashedMessage = true;
+ }
+
+ if (m_shouldExitWhenWebProcessCrashes)
+ exit(1);
+}
+
+void TestController::simulateWebNotificationClick(uint64_t notificationID)
+{
+ m_webNotificationProvider.simulateWebNotificationClick(notificationID);
+}
+
+void TestController::setGeolocationPermission(bool enabled)
+{
+ m_isGeolocationPermissionSet = true;
+ m_isGeolocationPermissionAllowed = enabled;
+ decidePolicyForGeolocationPermissionRequestIfPossible();
+}
+
+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)
+{
+ m_geolocationProvider->setPosition(latitude, longitude, accuracy, providesAltitude, altitude, providesAltitudeAccuracy, altitudeAccuracy, providesHeading, heading, providesSpeed, speed);
+}
+
+void TestController::setMockGeolocationPositionUnavailableError(WKStringRef errorMessage)
+{
+ m_geolocationProvider->setPositionUnavailableError(errorMessage);
+}
+
+void TestController::handleGeolocationPermissionRequest(WKGeolocationPermissionRequestRef geolocationPermissionRequest)
+{
+ m_geolocationPermissionRequests.append(geolocationPermissionRequest);
+ decidePolicyForGeolocationPermissionRequestIfPossible();
+}
+
+bool TestController::isGeolocationProviderActive() const
+{
+ return m_geolocationProvider->isActive();
+}
+
+void TestController::setUserMediaPermission(bool enabled)
+{
+ m_isUserMediaPermissionSet = true;
+ m_isUserMediaPermissionAllowed = enabled;
+ decidePolicyForUserMediaPermissionRequestIfPossible();
+}
+
+void TestController::handleUserMediaPermissionRequest(WKUserMediaPermissionRequestRef userMediaPermissionRequest)
+{
+ m_userMediaPermissionRequests.append(userMediaPermissionRequest);
+ decidePolicyForUserMediaPermissionRequestIfPossible();
+}
+
+void TestController::decidePolicyForUserMediaPermissionRequestIfPossible()
+{
+ if (!m_isUserMediaPermissionSet)
+ return;
+
+ for (auto& request : m_userMediaPermissionRequests) {
+ if (m_isUserMediaPermissionAllowed)
+ WKUserMediaPermissionRequestAllow(request.get());
+ else
+ WKUserMediaPermissionRequestDeny(request.get());
+ }
+ m_userMediaPermissionRequests.clear();
+}
+
+void TestController::setCustomPolicyDelegate(bool enabled, bool permissive)
+{
+ m_policyDelegateEnabled = enabled;
+ m_policyDelegatePermissive = permissive;
+}
+
+void TestController::decidePolicyForGeolocationPermissionRequestIfPossible()
+{
+ if (!m_isGeolocationPermissionSet)
+ return;
+
+ for (size_t i = 0; i < m_geolocationPermissionRequests.size(); ++i) {
+ WKGeolocationPermissionRequestRef permissionRequest = m_geolocationPermissionRequests[i].get();
+ if (m_isGeolocationPermissionAllowed)
+ WKGeolocationPermissionRequestAllow(permissionRequest);
+ else
+ WKGeolocationPermissionRequestDeny(permissionRequest);
+ }
+ m_geolocationPermissionRequests.clear();
+}
+
+void TestController::decidePolicyForNotificationPermissionRequest(WKPageRef page, WKSecurityOriginRef origin, WKNotificationPermissionRequestRef request, const void*)
+{
+ TestController::singleton().decidePolicyForNotificationPermissionRequest(page, origin, request);
+}
+
+void TestController::decidePolicyForNotificationPermissionRequest(WKPageRef, WKSecurityOriginRef, WKNotificationPermissionRequestRef request)
+{
+ WKNotificationPermissionRequestAllow(request);
+}
+
+void TestController::unavailablePluginButtonClicked(WKPageRef, WKPluginUnavailabilityReason, WKDictionaryRef, const void*)
+{
+ printf("MISSING PLUGIN BUTTON PRESSED\n");
+}
+
+void TestController::decidePolicyForNavigationAction(WKPageRef, WKNavigationActionRef navigationAction, WKFramePolicyListenerRef listener, WKTypeRef, const void* clientInfo)
+{
+ static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForNavigationAction(listener);
+}
+
+void TestController::decidePolicyForNavigationAction(WKFramePolicyListenerRef listener)
+{
+ if (m_policyDelegateEnabled && !m_policyDelegatePermissive) {
+ WKFramePolicyListenerIgnore(listener);
+ return;
+ }
+
+ WKFramePolicyListenerUse(listener);
+}
+
+void TestController::decidePolicyForNavigationResponse(WKPageRef, WKNavigationResponseRef navigationResponse, WKFramePolicyListenerRef listener, WKTypeRef, const void* clientInfo)
+{
+ static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForNavigationResponse(navigationResponse, listener);
+}
+
+void TestController::decidePolicyForNavigationResponse(WKNavigationResponseRef navigationResponse, WKFramePolicyListenerRef listener)
+{
+ // Even though Response was already checked by WKBundlePagePolicyClient, the check did not include plugins
+ // so we have to re-check again.
+ if (WKNavigationResponseCanShowMIMEType(navigationResponse)) {
+ WKFramePolicyListenerUse(listener);
+ return;
+ }
+
+ WKFramePolicyListenerIgnore(listener);
+}
+
+void TestController::didNavigateWithNavigationData(WKContextRef, WKPageRef, WKNavigationDataRef navigationData, WKFrameRef frame, const void* clientInfo)
+{
+ static_cast<TestController*>(const_cast<void*>(clientInfo))->didNavigateWithNavigationData(navigationData, frame);
+}
+
+void TestController::didNavigateWithNavigationData(WKNavigationDataRef navigationData, WKFrameRef)
+{
+ if (m_state != RunningTest)
+ return;
+
+ if (!m_shouldLogHistoryClientCallbacks)
+ return;
+
+ // URL
+ WKRetainPtr<WKURLRef> urlWK = adoptWK(WKNavigationDataCopyURL(navigationData));
+ WKRetainPtr<WKStringRef> urlStringWK = adoptWK(WKURLCopyString(urlWK.get()));
+ // Title
+ WKRetainPtr<WKStringRef> titleWK = adoptWK(WKNavigationDataCopyTitle(navigationData));
+ // HTTP method
+ WKRetainPtr<WKURLRequestRef> requestWK = adoptWK(WKNavigationDataCopyOriginalRequest(navigationData));
+ WKRetainPtr<WKStringRef> methodWK = adoptWK(WKURLRequestCopyHTTPMethod(requestWK.get()));
+
+ // FIXME: Determine whether the navigation was successful / a client redirect rather than hard-coding the message here.
+ 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",
+ toSTD(urlStringWK).c_str(), toSTD(titleWK).c_str(), toSTD(methodWK).c_str()));
+}
+
+void TestController::didPerformClientRedirect(WKContextRef, WKPageRef, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void* clientInfo)
+{
+ static_cast<TestController*>(const_cast<void*>(clientInfo))->didPerformClientRedirect(sourceURL, destinationURL, frame);
+}
+
+void TestController::didPerformClientRedirect(WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef)
+{
+ if (m_state != RunningTest)
+ return;
+
+ if (!m_shouldLogHistoryClientCallbacks)
+ return;
+
+ WKRetainPtr<WKStringRef> sourceStringWK = adoptWK(WKURLCopyString(sourceURL));
+ WKRetainPtr<WKStringRef> destinationStringWK = adoptWK(WKURLCopyString(destinationURL));
+
+ m_currentInvocation->outputText(String::format("WebView performed a client redirect from \"%s\" to \"%s\".\n", toSTD(sourceStringWK).c_str(), toSTD(destinationStringWK).c_str()));
+}
+
+void TestController::didPerformServerRedirect(WKContextRef, WKPageRef, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void* clientInfo)
+{
+ static_cast<TestController*>(const_cast<void*>(clientInfo))->didPerformServerRedirect(sourceURL, destinationURL, frame);
+}
+
+void TestController::didPerformServerRedirect(WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef)
+{
+ if (m_state != RunningTest)
+ return;
+
+ if (!m_shouldLogHistoryClientCallbacks)
+ return;
+
+ WKRetainPtr<WKStringRef> sourceStringWK = adoptWK(WKURLCopyString(sourceURL));
+ WKRetainPtr<WKStringRef> destinationStringWK = adoptWK(WKURLCopyString(destinationURL));
+
+ m_currentInvocation->outputText(String::format("WebView performed a server redirect from \"%s\" to \"%s\".\n", toSTD(sourceStringWK).c_str(), toSTD(destinationStringWK).c_str()));
+}
+
+void TestController::didUpdateHistoryTitle(WKContextRef, WKPageRef, WKStringRef title, WKURLRef URL, WKFrameRef frame, const void* clientInfo)
+{
+ static_cast<TestController*>(const_cast<void*>(clientInfo))->didUpdateHistoryTitle(title, URL, frame);
+}
+
+void TestController::didUpdateHistoryTitle(WKStringRef title, WKURLRef URL, WKFrameRef)
+{
+ if (m_state != RunningTest)
+ return;
+
+ if (!m_shouldLogHistoryClientCallbacks)
+ return;
+
+ WKRetainPtr<WKStringRef> urlStringWK(AdoptWK, WKURLCopyString(URL));
+ m_currentInvocation->outputText(String::format("WebView updated the title for history URL \"%s\" to \"%s\".\n", toSTD(urlStringWK).c_str(), toSTD(title).c_str()));
+}
+
+} // namespace WTR