1/* 2 * Copyright (C) 2010 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "TestController.h" 28 29#include <fcntl.h> 30#include <io.h> 31#include <shlwapi.h> 32#include <string> 33#include <WebKit2/WKContextPrivateWin.h> 34#include <WebKit2/WKStringCF.h> 35#include <wtf/RetainPtr.h> 36#include <wtf/Vector.h> 37 38using namespace std; 39 40namespace WTR { 41 42static HANDLE webProcessCrashingEvent; 43static const char webProcessCrashingEventName[] = "WebKitTestRunner.WebProcessCrashing"; 44// This is the longest we'll wait (in seconds) for the web process to finish crashing and a crash 45// log to be saved. This interval should be just a tiny bit longer than it will ever reasonably 46// take to save a crash log. 47static const double maximumWaitForWebProcessToCrash = 60; 48 49#ifdef DEBUG_ALL 50const LPWSTR testPluginDirectoryName = L"TestNetscapePlugin_Debug"; 51const char* injectedBundleDLL = "\\InjectedBundle_debug.dll"; 52#else 53const LPWSTR testPluginDirectoryName = L"TestNetscapePlugin"; 54const char* injectedBundleDLL = "\\InjectedBundle.dll"; 55#endif 56 57static void addQTDirToPATH() 58{ 59 static LPCWSTR pathEnvironmentVariable = L"PATH"; 60 static LPCWSTR quickTimeKeyName = L"Software\\Apple Computer, Inc.\\QuickTime"; 61 static LPCWSTR quickTimeSysDir = L"QTSysDir"; 62 static bool initialized; 63 64 if (initialized) 65 return; 66 initialized = true; 67 68 // Get the QuickTime dll directory from the registry. The key can be in either HKLM or HKCU. 69 WCHAR qtPath[MAX_PATH]; 70 DWORD qtPathBufferLen = sizeof(qtPath); 71 DWORD keyType; 72 HRESULT result = ::SHGetValueW(HKEY_LOCAL_MACHINE, quickTimeKeyName, quickTimeSysDir, &keyType, (LPVOID)qtPath, &qtPathBufferLen); 73 if (result != ERROR_SUCCESS || !qtPathBufferLen || keyType != REG_SZ) { 74 qtPathBufferLen = sizeof(qtPath); 75 result = ::SHGetValueW(HKEY_CURRENT_USER, quickTimeKeyName, quickTimeSysDir, &keyType, (LPVOID)qtPath, &qtPathBufferLen); 76 if (result != ERROR_SUCCESS || !qtPathBufferLen || keyType != REG_SZ) 77 return; 78 } 79 80 // Read the current PATH. 81 DWORD pathSize = ::GetEnvironmentVariableW(pathEnvironmentVariable, 0, 0); 82 Vector<WCHAR> oldPath(pathSize); 83 if (!::GetEnvironmentVariableW(pathEnvironmentVariable, oldPath.data(), oldPath.size())) 84 return; 85 86 // And add the QuickTime dll. 87 wstring newPath; 88 newPath.append(qtPath); 89 newPath.append(L";"); 90 newPath.append(oldPath.data(), oldPath.size()); 91 ::SetEnvironmentVariableW(pathEnvironmentVariable, newPath.data()); 92} 93 94static LONG WINAPI exceptionFilter(EXCEPTION_POINTERS*) 95{ 96 fputs("#CRASHED\n", stderr); 97 fflush(stderr); 98 return EXCEPTION_CONTINUE_SEARCH; 99} 100 101void TestController::notifyDone() 102{ 103} 104 105void TestController::platformInitialize() 106{ 107 // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for 108 // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the 109 // error mode here to work around Cygwin's behavior. See <http://webkit.org/b/55222>. 110 ::SetErrorMode(0); 111 112 ::SetUnhandledExceptionFilter(exceptionFilter); 113 114 _setmode(1, _O_BINARY); 115 _setmode(2, _O_BINARY); 116 117 // Add the QuickTime dll directory to PATH or QT 7.6 will fail to initialize on systems 118 // linked with older versions of qtmlclientlib.dll. 119 addQTDirToPATH(); 120 121 webProcessCrashingEvent = ::CreateEventA(0, FALSE, FALSE, webProcessCrashingEventName); 122} 123 124void TestController::initializeInjectedBundlePath() 125{ 126 CFStringRef exeContainerPath = CFURLCopyFileSystemPath(CFURLCreateCopyDeletingLastPathComponent(0, CFBundleCopyExecutableURL(CFBundleGetMainBundle())), kCFURLWindowsPathStyle); 127 CFMutableStringRef bundlePath = CFStringCreateMutableCopy(0, 0, exeContainerPath); 128 CFStringAppendCString(bundlePath, injectedBundleDLL, kCFStringEncodingWindowsLatin1); 129 m_injectedBundlePath.adopt(WKStringCreateWithCFString(bundlePath)); 130} 131 132void TestController::initializeTestPluginDirectory() 133{ 134 RetainPtr<CFURLRef> bundleURL(AdoptCF, CFBundleCopyExecutableURL(CFBundleGetMainBundle())); 135 RetainPtr<CFURLRef> bundleDirectoryURL(AdoptCF, CFURLCreateCopyDeletingLastPathComponent(0, bundleURL.get())); 136 RetainPtr<CFStringRef> testPluginDirectoryNameString(AdoptCF, CFStringCreateWithCharacters(0, reinterpret_cast<const UniChar*>(testPluginDirectoryName), wcslen(testPluginDirectoryName))); 137 RetainPtr<CFURLRef> testPluginDirectoryURL(AdoptCF, CFURLCreateCopyAppendingPathComponent(0, bundleDirectoryURL.get(), testPluginDirectoryNameString.get(), true)); 138 RetainPtr<CFStringRef> testPluginDirectoryPath(AdoptCF, CFURLCopyFileSystemPath(testPluginDirectoryURL.get(), kCFURLWindowsPathStyle)); 139 m_testPluginDirectory.adopt(WKStringCreateWithCFString(testPluginDirectoryPath.get())); 140} 141 142enum RunLoopResult { TimedOut, ObjectSignaled, ConditionSatisfied }; 143 144static RunLoopResult runRunLoopUntil(bool& condition, HANDLE object, double timeout) 145{ 146 DWORD end = ::GetTickCount() + timeout * 1000; 147 while (!condition) { 148 DWORD now = ::GetTickCount(); 149 if (now > end) 150 return TimedOut; 151 152 DWORD objectCount = object ? 1 : 0; 153 const HANDLE* objects = object ? &object : 0; 154 DWORD result = ::MsgWaitForMultipleObjectsEx(objectCount, objects, end - now, QS_ALLINPUT, MWMO_INPUTAVAILABLE); 155 if (result == WAIT_TIMEOUT) 156 return TimedOut; 157 158 if (objectCount && result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + objectCount) 159 return ObjectSignaled; 160 161 ASSERT(result == WAIT_OBJECT_0 + objectCount); 162 // There are messages in the queue. Process them. 163 MSG msg; 164 while (::PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) { 165 ::TranslateMessage(&msg); 166 ::DispatchMessageW(&msg); 167 } 168 } 169 170 return ConditionSatisfied; 171} 172 173void TestController::platformRunUntil(bool& done, double timeout) 174{ 175 RunLoopResult result = runRunLoopUntil(done, webProcessCrashingEvent, timeout); 176 if (result == TimedOut || result == ConditionSatisfied) 177 return; 178 ASSERT(result == ObjectSignaled); 179 180 // The web process is crashing. A crash log might be being saved, which can take a long 181 // time, and we don't want to time out while that happens. 182 183 // First, let the test harness know this happened so it won't think we've hung. But 184 // make sure we don't exit just yet! 185 m_shouldExitWhenWebProcessCrashes = false; 186 processDidCrash(); 187 m_shouldExitWhenWebProcessCrashes = true; 188 189 // Then spin a run loop until it finishes crashing to give time for a crash log to be saved. If 190 // it takes too long for a crash log to be saved, we'll just give up. 191 bool neverSetCondition = false; 192 result = runRunLoopUntil(neverSetCondition, 0, maximumWaitForWebProcessToCrash); 193 ASSERT_UNUSED(result, result == TimedOut); 194 exit(1); 195} 196 197static WKRetainPtr<WKStringRef> toWK(const char* string) 198{ 199 return WKRetainPtr<WKStringRef>(AdoptWK, WKStringCreateWithUTF8CString(string)); 200} 201 202void TestController::platformInitializeContext() 203{ 204 // FIXME: Make DRT pass with Windows native controls. <http://webkit.org/b/25592> 205 WKContextSetShouldPaintNativeControls(m_context.get(), false); 206 207 WKContextSetInitializationUserDataForInjectedBundle(m_context.get(), toWK(webProcessCrashingEventName).get()); 208} 209 210void TestController::runModal(PlatformWebView*) 211{ 212 // FIXME: Need to implement this to test showModalDialog. 213} 214 215const char* TestController::platformLibraryPathForTesting() 216{ 217 return 0; 218} 219 220} // namespace WTR 221