2 * Copyright (C) 2010 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 "PlatformWebView.h"
30 #include "StringFunctions.h"
31 #include "TestInvocation.h"
32 #include <WebKit2/WKContextPrivate.h>
33 #include <WebKit2/WKNotification.h>
34 #include <WebKit2/WKNotificationManager.h>
35 #include <WebKit2/WKNotificationPermissionRequest.h>
36 #include <WebKit2/WKNumber.h>
37 #include <WebKit2/WKPageGroup.h>
38 #include <WebKit2/WKPagePrivate.h>
39 #include <WebKit2/WKPreferencesPrivate.h>
40 #include <WebKit2/WKRetainPtr.h>
46 #include <wtf/PassOwnPtr.h>
49 #include <WebKit2/WKPagePrivateMac.h>
52 #if PLATFORM(MAC) || PLATFORM(QT) || PLATFORM(GTK) || PLATFORM(EFL)
53 #include "EventSenderProxy.h"
58 // defaultLongTimeout + defaultShortTimeout should be less than 80,
59 // the default timeout value of the test harness so we can detect an
60 // unresponsive web process.
61 static const double defaultLongTimeout = 60;
62 static const double defaultShortTimeout = 15;
63 static const double defaultNoTimeout = -1;
65 static WKURLRef blankURL()
67 static WKURLRef staticBlankURL = WKURLCreateWithUTF8CString("about:blank");
68 return staticBlankURL;
71 static TestController* controller;
73 TestController& TestController::shared()
79 TestController::TestController(int argc, const char* argv[])
81 , m_printSeparators(false)
82 , m_usingServerMode(false)
83 , m_gcBetweenTests(false)
85 , m_doneResetting(false)
86 , m_longTimeout(defaultLongTimeout)
87 , m_shortTimeout(defaultShortTimeout)
88 , m_noTimeout(defaultNoTimeout)
89 , m_useWaitToDumpWatchdogTimer(true)
90 , m_forceNoTimeout(false)
91 , m_didPrintWebProcessCrashedMessage(false)
92 , m_shouldExitWhenWebProcessCrashes(true)
93 , m_beforeUnloadReturnValue(true)
94 , m_isGeolocationPermissionSet(false)
95 , m_isGeolocationPermissionAllowed(false)
96 , m_policyDelegateEnabled(false)
97 , m_policyDelegatePermissive(false)
98 #if PLATFORM(MAC) || PLATFORM(QT) || PLATFORM(GTK) || PLATFORM(EFL)
99 , m_eventSenderProxy(new EventSenderProxy(this))
102 initialize(argc, argv);
108 TestController::~TestController()
112 static WKRect getWindowFrame(WKPageRef page, const void* clientInfo)
114 PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
115 return view->windowFrame();
118 static void setWindowFrame(WKPageRef page, WKRect frame, const void* clientInfo)
120 PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
121 view->setWindowFrame(frame);
124 static bool runBeforeUnloadConfirmPanel(WKPageRef page, WKStringRef message, WKFrameRef frame, const void*)
126 printf("CONFIRM NAVIGATION: %s\n", toSTD(message).c_str());
127 return TestController::shared().beforeUnloadReturnValue();
130 static unsigned long long exceededDatabaseQuota(WKPageRef, WKFrameRef, WKSecurityOriginRef, WKStringRef, WKStringRef, unsigned long long, unsigned long long, unsigned long long, unsigned long long, const void*)
132 static const unsigned long long defaultQuota = 5 * 1024 * 1024;
137 void TestController::runModal(WKPageRef page, const void* clientInfo)
139 PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
140 view->setWindowIsKey(false);
142 view->setWindowIsKey(true);
145 static void closeOtherPage(WKPageRef page, const void* clientInfo)
148 PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
152 static void focus(WKPageRef page, const void* clientInfo)
154 PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
156 view->setWindowIsKey(true);
159 static void unfocus(WKPageRef page, const void* clientInfo)
161 PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
162 view->setWindowIsKey(false);
165 static void decidePolicyForGeolocationPermissionRequest(WKPageRef, WKFrameRef, WKSecurityOriginRef, WKGeolocationPermissionRequestRef permissionRequest, const void* clientInfo)
167 TestController::shared().handleGeolocationPermissionRequest(permissionRequest);
170 WKPageRef TestController::createOtherPage(WKPageRef oldPage, WKURLRequestRef, WKDictionaryRef, WKEventModifiers, WKEventMouseButton, const void*)
172 PlatformWebView* view = new PlatformWebView(WKPageGetContext(oldPage), WKPageGetPageGroup(oldPage));
173 WKPageRef newPage = view->page();
175 view->resizeTo(800, 600);
177 WKPageUIClient otherPageUIClient = {
178 kWKPageUIClientCurrentVersion,
180 0, // createNewPage_deprecatedForUseWithV0
186 0, // runJavaScriptAlert
187 0, // runJavaScriptConfirm
188 0, // runJavaScriptPrompt
190 0, // mouseDidMoveOverElement_deprecatedForUseWithV0
191 0, // missingPluginButtonClicked
192 0, // didNotHandleKeyEvent
193 0, // didNotHandleWheelEvent
194 0, // toolbarsAreVisible
195 0, // setToolbarsAreVisible
196 0, // menuBarIsVisible
197 0, // setMenuBarIsVisible
198 0, // statusBarIsVisible
199 0, // setStatusBarIsVisible
204 runBeforeUnloadConfirmPanel,
207 exceededDatabaseQuota,
209 decidePolicyForGeolocationPermissionRequest,
216 0, // didCompleteRubberBandForMainFrame
217 0, // saveDataToFileInDownloadsFolder
218 0, // shouldInterruptJavaScript
220 0, // mouseDidMoveOverElement
221 0, // decidePolicyForNotificationPermissionRequest
222 0, // unavailablePluginButtonClicked
223 0, // showColorPicker
224 0, // hideColorPicker
226 WKPageSetPageUIClient(newPage, &otherPageUIClient);
232 const char* TestController::libraryPathForTesting()
234 // FIXME: This may not be sufficient to prevent interactions/crashes
235 // when running more than one copy of DumpRenderTree.
236 // See https://bugs.webkit.org/show_bug.cgi?id=10906
237 char* dumpRenderTreeTemp = getenv("DUMPRENDERTREE_TEMP");
238 if (dumpRenderTreeTemp)
239 return dumpRenderTreeTemp;
240 return platformLibraryPathForTesting();
244 void TestController::initialize(int argc, const char* argv[])
246 platformInitialize();
249 fputs("Usage: WebKitTestRunner [options] filename [filename2..n]\n", stderr);
250 // FIXME: Refactor option parsing to allow us to print
251 // an auto-generated list of options.
255 bool printSupportedFeatures = false;
257 for (int i = 1; i < argc; ++i) {
258 std::string argument(argv[i]);
260 if (argument == "--timeout" && i + 1 < argc) {
261 m_longTimeout = atoi(argv[++i]);
262 // Scale up the short timeout to match.
263 m_shortTimeout = defaultShortTimeout * m_longTimeout / defaultLongTimeout;
267 if (argument == "--no-timeout") {
268 m_useWaitToDumpWatchdogTimer = false;
272 if (argument == "--no-timeout-at-all") {
273 m_useWaitToDumpWatchdogTimer = false;
274 m_forceNoTimeout = true;
278 if (argument == "--verbose") {
282 if (argument == "--gc-between-tests") {
283 m_gcBetweenTests = true;
286 if (argument == "--print-supported-features") {
287 printSupportedFeatures = true;
291 // Skip any other arguments that begin with '--'.
292 if (argument.length() >= 2 && argument[0] == '-' && argument[1] == '-')
295 m_paths.push_back(argument);
298 if (printSupportedFeatures) {
299 // FIXME: On Windows, DumpRenderTree uses this to expose whether it supports 3d
300 // transforms and accelerated compositing. When we support those features, we
301 // should match DRT's behavior.
305 m_usingServerMode = (m_paths.size() == 1 && m_paths[0] == "-");
306 if (m_usingServerMode)
307 m_printSeparators = true;
309 m_printSeparators = m_paths.size() > 1;
311 initializeInjectedBundlePath();
312 initializeTestPluginDirectory();
314 WKRetainPtr<WKStringRef> pageGroupIdentifier(AdoptWK, WKStringCreateWithUTF8CString("WebKitTestRunnerPageGroup"));
315 m_pageGroup.adopt(WKPageGroupCreateWithIdentifier(pageGroupIdentifier.get()));
317 m_context.adopt(WKContextCreateWithInjectedBundlePath(injectedBundlePath()));
318 m_geolocationProvider = adoptPtr(new GeolocationProviderMock(m_context.get()));
320 const char* path = libraryPathForTesting();
322 Vector<char> databaseDirectory(strlen(path) + strlen("/Databases") + 1);
323 sprintf(databaseDirectory.data(), "%s%s", path, "/Databases");
324 WKRetainPtr<WKStringRef> databaseDirectoryWK(AdoptWK, WKStringCreateWithUTF8CString(databaseDirectory.data()));
325 WKContextSetDatabaseDirectory(m_context.get(), databaseDirectoryWK.get());
328 platformInitializeContext();
330 WKContextInjectedBundleClient injectedBundleClient = {
331 kWKContextInjectedBundleClientCurrentVersion,
333 didReceiveMessageFromInjectedBundle,
334 didReceiveSynchronousMessageFromInjectedBundle,
335 0 // getInjectedBundleInitializationUserData
337 WKContextSetInjectedBundleClient(m_context.get(), &injectedBundleClient);
339 WKNotificationManagerRef notificationManager = WKContextGetNotificationManager(m_context.get());
340 WKNotificationProvider notificationKit = m_webNotificationProvider.provider();
341 WKNotificationManagerSetProvider(notificationManager, ¬ificationKit);
343 if (testPluginDirectory())
344 WKContextSetAdditionalPluginsDirectory(m_context.get(), testPluginDirectory());
346 m_mainWebView = adoptPtr(new PlatformWebView(m_context.get(), m_pageGroup.get()));
348 WKPageUIClient pageUIClient = {
349 kWKPageUIClientCurrentVersion,
351 0, // createNewPage_deprecatedForUseWithV0
357 0, // runJavaScriptAlert
358 0, // runJavaScriptConfirm
359 0, // runJavaScriptPrompt
361 0, // mouseDidMoveOverElement_deprecatedForUseWithV0
362 0, // missingPluginButtonClicked
363 0, // didNotHandleKeyEvent
364 0, // didNotHandleWheelEvent
365 0, // toolbarsAreVisible
366 0, // setToolbarsAreVisible
367 0, // menuBarIsVisible
368 0, // setMenuBarIsVisible
369 0, // statusBarIsVisible
370 0, // setStatusBarIsVisible
375 runBeforeUnloadConfirmPanel,
378 exceededDatabaseQuota,
380 decidePolicyForGeolocationPermissionRequest,
387 0, // didCompleteRubberBandForMainFrame
388 0, // saveDataToFileInDownloadsFolder
389 0, // shouldInterruptJavaScript
391 0, // mouseDidMoveOverElement
392 decidePolicyForNotificationPermissionRequest, // decidePolicyForNotificationPermissionRequest
393 0, // unavailablePluginButtonClicked
394 0, // showColorPicker
395 0, // hideColorPicker
397 WKPageSetPageUIClient(m_mainWebView->page(), &pageUIClient);
399 WKPageLoaderClient pageLoaderClient = {
400 kWKPageLoaderClientCurrentVersion,
402 0, // didStartProvisionalLoadForFrame
403 0, // didReceiveServerRedirectForProvisionalLoadForFrame
404 0, // didFailProvisionalLoadWithErrorForFrame
405 didCommitLoadForFrame,
406 0, // didFinishDocumentLoadForFrame
407 didFinishLoadForFrame,
408 0, // didFailLoadWithErrorForFrame
409 0, // didSameDocumentNavigationForFrame
410 0, // didReceiveTitleForFrame
411 0, // didFirstLayoutForFrame
412 0, // didFirstVisuallyNonEmptyLayoutForFrame
413 0, // didRemoveFrameFromHierarchy
414 0, // didFailToInitializePlugin
415 0, // didDisplayInsecureContentForFrame
416 0, // canAuthenticateAgainstProtectionSpaceInFrame
417 0, // didReceiveAuthenticationChallengeInFrame
418 0, // didStartProgress
419 0, // didChangeProgress
420 0, // didFinishProgress
421 0, // didBecomeUnresponsive
422 0, // didBecomeResponsive
424 0, // didChangeBackForwardList
425 0, // shouldGoToBackForwardListItem
426 0, // didRunInsecureContentForFrame
427 0, // didDetectXSSForFrame
428 0, // didNewFirstVisuallyNonEmptyLayout
429 0, // willGoToBackForwardListItem
430 0, // interactionOccurredWhileProcessUnresponsive
432 0, // didReceiveIntentForFrame
433 0, // registerIntentServiceForFrame
436 WKPageSetPageLoaderClient(m_mainWebView->page(), &pageLoaderClient);
438 WKPagePolicyClient pagePolicyClient = {
439 kWKPagePolicyClientCurrentVersion,
441 decidePolicyForNavigationAction,
442 0, // decidePolicyForNewWindowAction
443 decidePolicyForResponse,
444 0, // unableToImplementPolicy
446 WKPageSetPagePolicyClient(m_mainWebView->page(), &pagePolicyClient);
449 bool TestController::resetStateToConsistentValues()
453 m_beforeUnloadReturnValue = true;
455 WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("Reset"));
456 WKRetainPtr<WKMutableDictionaryRef> resetMessageBody = adoptWK(WKMutableDictionaryCreate());
458 WKRetainPtr<WKStringRef> shouldGCKey = adoptWK(WKStringCreateWithUTF8CString("ShouldGC"));
459 WKRetainPtr<WKBooleanRef> shouldGCValue = adoptWK(WKBooleanCreate(m_gcBetweenTests));
460 WKDictionaryAddItem(resetMessageBody.get(), shouldGCKey.get(), shouldGCValue.get());
462 WKContextPostMessageToInjectedBundle(TestController::shared().context(), messageName.get(), resetMessageBody.get());
464 WKContextSetShouldUseFontSmoothing(TestController::shared().context(), false);
466 WKContextSetCacheModel(TestController::shared().context(), kWKCacheModelDocumentBrowser);
468 // FIXME: This function should also ensure that there is only one page open.
471 WKPreferencesRef preferences = WKPageGroupGetPreferences(m_pageGroup.get());
472 WKPreferencesResetTestRunnerOverrides(preferences);
473 WKPreferencesSetOfflineWebApplicationCacheEnabled(preferences, true);
474 WKPreferencesSetFontSmoothingLevel(preferences, kWKFontSmoothingLevelNoSubpixelAntiAliasing);
475 WKPreferencesSetXSSAuditorEnabled(preferences, false);
476 WKPreferencesSetWebAudioEnabled(preferences, true);
477 WKPreferencesSetDeveloperExtrasEnabled(preferences, true);
478 WKPreferencesSetJavaScriptExperimentsEnabled(preferences, true);
479 WKPreferencesSetJavaScriptCanOpenWindowsAutomatically(preferences, true);
480 WKPreferencesSetJavaScriptCanAccessClipboard(preferences, true);
481 WKPreferencesSetDOMPasteAllowed(preferences, true);
482 WKPreferencesSetUniversalAccessFromFileURLsAllowed(preferences, true);
483 WKPreferencesSetFileAccessFromFileURLsAllowed(preferences, true);
484 #if ENABLE(FULLSCREEN_API)
485 WKPreferencesSetFullScreenEnabled(preferences, true);
487 WKPreferencesSetPageCacheEnabled(preferences, false);
488 WKPreferencesSetAsynchronousPluginInitializationEnabled(preferences, false);
489 WKPreferencesSetAsynchronousPluginInitializationEnabledForAllPlugins(preferences, false);
490 WKPreferencesSetArtificialPluginInitializationDelayEnabled(preferences, false);
491 WKPreferencesSetTabToLinksEnabled(preferences, false);
492 WKPreferencesSetInteractiveFormValidationEnabled(preferences, true);
493 WKPreferencesSetMockScrollbarsEnabled(preferences, true);
496 static WKStringRef standardFontFamily = WKStringCreateWithUTF8CString("Times");
497 static WKStringRef cursiveFontFamily = WKStringCreateWithUTF8CString("Apple Chancery");
498 static WKStringRef fantasyFontFamily = WKStringCreateWithUTF8CString("Papyrus");
499 static WKStringRef fixedFontFamily = WKStringCreateWithUTF8CString("Courier");
500 static WKStringRef pictographFontFamily = WKStringCreateWithUTF8CString("Apple Color Emoji");
501 static WKStringRef sansSerifFontFamily = WKStringCreateWithUTF8CString("Helvetica");
502 static WKStringRef serifFontFamily = WKStringCreateWithUTF8CString("Times");
504 WKPreferencesSetStandardFontFamily(preferences, standardFontFamily);
505 WKPreferencesSetCursiveFontFamily(preferences, cursiveFontFamily);
506 WKPreferencesSetFantasyFontFamily(preferences, fantasyFontFamily);
507 WKPreferencesSetFixedFontFamily(preferences, fixedFontFamily);
508 WKPreferencesSetPictographFontFamily(preferences, pictographFontFamily);
509 WKPreferencesSetSansSerifFontFamily(preferences, sansSerifFontFamily);
510 WKPreferencesSetSerifFontFamily(preferences, serifFontFamily);
512 WKPreferencesSetScreenFontSubstitutionEnabled(preferences, true);
513 WKPreferencesSetInspectorUsesWebKitUserInterface(preferences, true);
515 // in the case that a test using the chrome input field failed, be sure to clean up for the next test
516 m_mainWebView->removeChromeInputField();
517 m_mainWebView->focus();
519 // Re-set to the default backing scale factor by setting the custom scale factor to 0.
520 WKPageSetCustomBackingScaleFactor(m_mainWebView->page(), 0);
522 // Reset notification permissions
523 m_webNotificationProvider.reset();
525 // Reset Geolocation permissions.
526 m_geolocationPermissionRequests.clear();
527 m_isGeolocationPermissionSet = false;
528 m_isGeolocationPermissionAllowed = false;
530 // Reset Custom Policy Delegate.
531 setCustomPolicyDelegate(false, false);
533 // Reset main page back to about:blank
534 m_doneResetting = false;
536 WKPageLoadURL(m_mainWebView->page(), blankURL());
537 runUntil(m_doneResetting, ShortTimeout);
538 return m_doneResetting;
542 TestCommand() : shouldDumpPixels(false) { }
544 std::string pathOrURL;
545 bool shouldDumpPixels;
546 std::string expectedPixelHash;
549 class CommandTokenizer {
551 explicit CommandTokenizer(const std::string& input)
553 , m_posNextSeparator(0)
558 bool hasNext() const;
563 static const char kSeparator = '\'';
564 const std::string& m_input;
566 size_t m_posNextSeparator;
569 void CommandTokenizer::pump()
571 if (m_posNextSeparator == std::string::npos || m_posNextSeparator == m_input.size()) {
572 m_next = std::string();
575 size_t start = m_posNextSeparator ? m_posNextSeparator + 1 : 0;
576 m_posNextSeparator = m_input.find(kSeparator, start);
577 size_t size = m_posNextSeparator == std::string::npos ? std::string::npos : m_posNextSeparator - start;
578 m_next = std::string(m_input, start, size);
581 std::string CommandTokenizer::next()
585 std::string oldNext = m_next;
590 bool CommandTokenizer::hasNext() const
592 return !m_next.empty();
595 NO_RETURN static void die(const std::string& inputLine)
597 fprintf(stderr, "Unexpected input line: %s\n", inputLine.c_str());
601 TestCommand parseInputLine(const std::string& inputLine)
604 CommandTokenizer tokenizer(inputLine);
605 if (!tokenizer.hasNext())
608 result.pathOrURL = tokenizer.next();
609 if (!tokenizer.hasNext())
612 std::string arg = tokenizer.next();
613 if (arg != std::string("-p") && arg != std::string("--pixel-test"))
615 result.shouldDumpPixels = true;
617 if (tokenizer.hasNext())
618 result.expectedPixelHash = tokenizer.next();
623 bool TestController::runTest(const char* inputLine)
625 TestCommand command = parseInputLine(std::string(inputLine));
627 m_state = RunningTest;
629 m_currentInvocation = adoptPtr(new TestInvocation(command.pathOrURL));
630 if (command.shouldDumpPixels)
631 m_currentInvocation->setIsPixelTest(command.expectedPixelHash);
633 m_currentInvocation->invoke();
634 m_currentInvocation.clear();
639 void TestController::runTestingServerLoop()
641 char filenameBuffer[2048];
642 while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
643 char* newLineCharacter = strchr(filenameBuffer, '\n');
644 if (newLineCharacter)
645 *newLineCharacter = '\0';
647 if (strlen(filenameBuffer) == 0)
650 if (!runTest(filenameBuffer))
655 void TestController::run()
657 if (!resetStateToConsistentValues()) {
658 TestInvocation::dumpWebProcessUnresponsiveness("Failed to reset to consistent state before the first test");
662 if (m_usingServerMode)
663 runTestingServerLoop();
665 for (size_t i = 0; i < m_paths.size(); ++i) {
666 if (!runTest(m_paths[i].c_str()))
672 void TestController::runUntil(bool& done, TimeoutDuration timeoutDuration)
674 double timeout = m_noTimeout;
675 if (!m_forceNoTimeout) {
676 switch (timeoutDuration) {
678 timeout = m_shortTimeout;
681 timeout = m_longTimeout;
685 timeout = m_noTimeout;
690 platformRunUntil(done, timeout);
693 // WKContextInjectedBundleClient
695 void TestController::didReceiveMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
697 static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveMessageFromInjectedBundle(messageName, messageBody);
700 void TestController::didReceiveSynchronousMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, WKTypeRef* returnData, const void* clientInfo)
702 *returnData = static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody).leakRef();
705 void TestController::didReceiveMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
707 #if PLATFORM(MAC) || PLATFORM(QT) || PLATFORM(GTK) || PLATFORM(EFL)
708 if (WKStringIsEqualToUTF8CString(messageName, "EventSender")) {
709 ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
710 WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
712 WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
713 WKStringRef subMessageName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, subMessageKey.get()));
715 if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown") || WKStringIsEqualToUTF8CString(subMessageName, "MouseUp")) {
716 WKRetainPtr<WKStringRef> buttonKey = adoptWK(WKStringCreateWithUTF8CString("Button"));
717 unsigned button = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, buttonKey.get()))));
719 WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
720 WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
722 // Forward to WebProcess
723 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
724 if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown"))
725 m_eventSenderProxy->mouseDown(button, modifiers);
727 m_eventSenderProxy->mouseUp(button, modifiers);
731 ASSERT_NOT_REACHED();
735 if (!m_currentInvocation)
738 m_currentInvocation->didReceiveMessageFromInjectedBundle(messageName, messageBody);
741 WKRetainPtr<WKTypeRef> TestController::didReceiveSynchronousMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
743 #if PLATFORM(MAC) || PLATFORM(QT) || PLATFORM(GTK) || PLATFORM(EFL)
744 if (WKStringIsEqualToUTF8CString(messageName, "EventSender")) {
745 ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
746 WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
748 WKRetainPtr<WKStringRef> subMessageKey(AdoptWK, WKStringCreateWithUTF8CString("SubMessage"));
749 WKStringRef subMessageName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, subMessageKey.get()));
751 if (WKStringIsEqualToUTF8CString(subMessageName, "KeyDown")) {
752 WKRetainPtr<WKStringRef> keyKey = adoptWK(WKStringCreateWithUTF8CString("Key"));
753 WKStringRef key = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, keyKey.get()));
755 WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
756 WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
758 WKRetainPtr<WKStringRef> locationKey = adoptWK(WKStringCreateWithUTF8CString("Location"));
759 unsigned location = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, locationKey.get()))));
761 // Forward to WebProcess
762 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
763 m_eventSenderProxy->keyDown(key, modifiers, location);
764 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
768 if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown") || WKStringIsEqualToUTF8CString(subMessageName, "MouseUp")) {
769 WKRetainPtr<WKStringRef> buttonKey = adoptWK(WKStringCreateWithUTF8CString("Button"));
770 unsigned button = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, buttonKey.get()))));
772 WKRetainPtr<WKStringRef> modifiersKey = adoptWK(WKStringCreateWithUTF8CString("Modifiers"));
773 WKEventModifiers modifiers = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifiersKey.get()))));
775 // Forward to WebProcess
776 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
777 if (WKStringIsEqualToUTF8CString(subMessageName, "MouseDown"))
778 m_eventSenderProxy->mouseDown(button, modifiers);
780 m_eventSenderProxy->mouseUp(button, modifiers);
781 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
785 if (WKStringIsEqualToUTF8CString(subMessageName, "MouseMoveTo")) {
786 WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
787 double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
789 WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
790 double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
792 // Forward to WebProcess
793 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
794 m_eventSenderProxy->mouseMoveTo(x, y);
795 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
799 if (WKStringIsEqualToUTF8CString(subMessageName, "MouseScrollBy")) {
800 WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
801 double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
803 WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
804 double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
806 // Forward to WebProcess
807 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
808 m_eventSenderProxy->mouseScrollBy(x, y);
809 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
813 if (WKStringIsEqualToUTF8CString(subMessageName, "ContinuousMouseScrollBy")) {
814 WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
815 double x = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get())));
817 WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
818 double y = WKDoubleGetValue(static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get())));
820 WKRetainPtr<WKStringRef> pagedKey = adoptWK(WKStringCreateWithUTF8CString("Paged"));
821 bool paged = static_cast<bool>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, pagedKey.get()))));
823 // Forward to WebProcess
824 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
825 m_eventSenderProxy->continuousMouseScrollBy(x, y, paged);
826 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
830 if (WKStringIsEqualToUTF8CString(subMessageName, "LeapForward")) {
831 WKRetainPtr<WKStringRef> timeKey = adoptWK(WKStringCreateWithUTF8CString("TimeInMilliseconds"));
832 unsigned time = static_cast<unsigned>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, timeKey.get()))));
834 m_eventSenderProxy->leapForward(time);
838 #if ENABLE(TOUCH_EVENTS)
839 if (WKStringIsEqualToUTF8CString(subMessageName, "AddTouchPoint")) {
840 WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
841 int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
843 WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
844 int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
846 m_eventSenderProxy->addTouchPoint(x, y);
850 if (WKStringIsEqualToUTF8CString(subMessageName, "UpdateTouchPoint")) {
851 WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
852 int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
854 WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("X"));
855 int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
857 WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("Y"));
858 int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
860 m_eventSenderProxy->updateTouchPoint(index, x, y);
864 if (WKStringIsEqualToUTF8CString(subMessageName, "SetTouchModifier")) {
865 WKRetainPtr<WKStringRef> modifierKey = adoptWK(WKStringCreateWithUTF8CString("Modifier"));
866 WKEventModifiers modifier = static_cast<WKEventModifiers>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, modifierKey.get()))));
868 WKRetainPtr<WKStringRef> enableKey = adoptWK(WKStringCreateWithUTF8CString("Enable"));
869 bool enable = static_cast<bool>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, enableKey.get()))));
871 m_eventSenderProxy->setTouchModifier(modifier, enable);
875 if (WKStringIsEqualToUTF8CString(subMessageName, "SetTouchPointRadius")) {
876 WKRetainPtr<WKStringRef> xKey = adoptWK(WKStringCreateWithUTF8CString("RadiusX"));
877 int x = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, xKey.get()))));
879 WKRetainPtr<WKStringRef> yKey = adoptWK(WKStringCreateWithUTF8CString("RadiusY"));
880 int y = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, yKey.get()))));
882 m_eventSenderProxy->setTouchPointRadius(x, y);
886 if (WKStringIsEqualToUTF8CString(subMessageName, "TouchStart")) {
887 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
888 m_eventSenderProxy->touchStart();
889 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
893 if (WKStringIsEqualToUTF8CString(subMessageName, "TouchMove")) {
894 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
895 m_eventSenderProxy->touchMove();
896 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
900 if (WKStringIsEqualToUTF8CString(subMessageName, "TouchEnd")) {
901 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
902 m_eventSenderProxy->touchEnd();
903 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
907 if (WKStringIsEqualToUTF8CString(subMessageName, "TouchCancel")) {
908 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), true);
909 m_eventSenderProxy->touchCancel();
910 WKPageSetShouldSendEventsSynchronously(mainWebView()->page(), false);
914 if (WKStringIsEqualToUTF8CString(subMessageName, "ClearTouchPoints")) {
915 m_eventSenderProxy->clearTouchPoints();
919 if (WKStringIsEqualToUTF8CString(subMessageName, "ReleaseTouchPoint")) {
920 WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
921 int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
922 m_eventSenderProxy->releaseTouchPoint(index);
926 if (WKStringIsEqualToUTF8CString(subMessageName, "CancelTouchPoint")) {
927 WKRetainPtr<WKStringRef> indexKey = adoptWK(WKStringCreateWithUTF8CString("Index"));
928 int index = static_cast<int>(WKUInt64GetValue(static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, indexKey.get()))));
929 m_eventSenderProxy->cancelTouchPoint(index);
933 ASSERT_NOT_REACHED();
936 return m_currentInvocation->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody);
939 // WKPageLoaderClient
941 void TestController::didCommitLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef, const void* clientInfo)
943 static_cast<TestController*>(const_cast<void*>(clientInfo))->didCommitLoadForFrame(page, frame);
946 void TestController::didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef, const void* clientInfo)
948 static_cast<TestController*>(const_cast<void*>(clientInfo))->didFinishLoadForFrame(page, frame);
951 void TestController::processDidCrash(WKPageRef page, const void* clientInfo)
953 static_cast<TestController*>(const_cast<void*>(clientInfo))->processDidCrash();
956 void TestController::didCommitLoadForFrame(WKPageRef page, WKFrameRef frame)
958 if (!WKFrameIsMainFrame(frame))
961 mainWebView()->focus();
964 void TestController::didFinishLoadForFrame(WKPageRef page, WKFrameRef frame)
966 if (m_state != Resetting)
969 if (!WKFrameIsMainFrame(frame))
972 WKRetainPtr<WKURLRef> wkURL(AdoptWK, WKFrameCopyURL(frame));
973 if (!WKURLIsEqual(wkURL.get(), blankURL()))
976 m_doneResetting = true;
977 shared().notifyDone();
980 void TestController::processDidCrash()
982 // This function can be called multiple times when crash logs are being saved on Windows, so
983 // ensure we only print the crashed message once.
984 if (!m_didPrintWebProcessCrashedMessage) {
986 pid_t pid = WKPageGetProcessIdentifier(m_mainWebView->page());
987 fprintf(stderr, "#CRASHED - WebProcess (pid %ld)\n", static_cast<long>(pid));
989 fputs("#CRASHED - WebProcess\n", stderr);
992 m_didPrintWebProcessCrashedMessage = true;
995 if (m_shouldExitWhenWebProcessCrashes)
999 void TestController::simulateWebNotificationClick(uint64_t notificationID)
1001 m_webNotificationProvider.simulateWebNotificationClick(notificationID);
1004 void TestController::setGeolocationPermission(bool enabled)
1006 m_isGeolocationPermissionSet = true;
1007 m_isGeolocationPermissionAllowed = enabled;
1008 decidePolicyForGeolocationPermissionRequestIfPossible();
1011 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)
1013 m_geolocationProvider->setPosition(latitude, longitude, accuracy, providesAltitude, altitude, providesAltitudeAccuracy, altitudeAccuracy, providesHeading, heading, providesSpeed, speed);
1016 void TestController::setMockGeolocationPositionUnavailableError(WKStringRef errorMessage)
1018 m_geolocationProvider->setPositionUnavailableError(errorMessage);
1021 void TestController::handleGeolocationPermissionRequest(WKGeolocationPermissionRequestRef geolocationPermissionRequest)
1023 m_geolocationPermissionRequests.append(geolocationPermissionRequest);
1024 decidePolicyForGeolocationPermissionRequestIfPossible();
1027 void TestController::setCustomPolicyDelegate(bool enabled, bool permissive)
1029 m_policyDelegateEnabled = enabled;
1030 m_policyDelegatePermissive = permissive;
1033 void TestController::decidePolicyForGeolocationPermissionRequestIfPossible()
1035 if (!m_isGeolocationPermissionSet)
1038 for (size_t i = 0; i < m_geolocationPermissionRequests.size(); ++i) {
1039 WKGeolocationPermissionRequestRef permissionRequest = m_geolocationPermissionRequests[i].get();
1040 if (m_isGeolocationPermissionAllowed)
1041 WKGeolocationPermissionRequestAllow(permissionRequest);
1043 WKGeolocationPermissionRequestDeny(permissionRequest);
1045 m_geolocationPermissionRequests.clear();
1048 void TestController::decidePolicyForNotificationPermissionRequest(WKPageRef page, WKSecurityOriginRef origin, WKNotificationPermissionRequestRef request, const void*)
1050 TestController::shared().decidePolicyForNotificationPermissionRequest(page, origin, request);
1053 void TestController::decidePolicyForNotificationPermissionRequest(WKPageRef, WKSecurityOriginRef, WKNotificationPermissionRequestRef request)
1055 WKNotificationPermissionRequestAllow(request);
1058 void TestController::decidePolicyForNavigationAction(WKPageRef, WKFrameRef, WKFrameNavigationType, WKEventModifiers, WKEventMouseButton, WKURLRequestRef, WKFramePolicyListenerRef listener, WKTypeRef, const void* clientInfo)
1060 static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForNavigationAction(listener);
1063 void TestController::decidePolicyForNavigationAction(WKFramePolicyListenerRef listener)
1065 if (m_policyDelegateEnabled && !m_policyDelegatePermissive) {
1066 WKFramePolicyListenerIgnore(listener);
1070 WKFramePolicyListenerUse(listener);
1073 void TestController::decidePolicyForResponse(WKPageRef, WKFrameRef, WKURLResponseRef, WKURLRequestRef, WKFramePolicyListenerRef listener, WKTypeRef, const void* clientInfo)
1075 static_cast<TestController*>(const_cast<void*>(clientInfo))->decidePolicyForResponse(listener);
1078 void TestController::decidePolicyForResponse(WKFramePolicyListenerRef listener)
1080 // Response was already checked by WKBundlePagePolicyClient, so if we are here we're supposed to ignore.
1081 WKFramePolicyListenerIgnore(listener);