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
38 using namespace std;
39
40 namespace WTR {
41
42 static HANDLE webProcessCrashingEvent;
43 static 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.
47 static const double maximumWaitForWebProcessToCrash = 60;
48
49 #ifdef DEBUG_ALL
50 const LPWSTR testPluginDirectoryName = L"TestNetscapePlugin_Debug";
51 const char* injectedBundleDLL = "\\InjectedBundle_debug.dll";
52 #else
53 const LPWSTR testPluginDirectoryName = L"TestNetscapePlugin";
54 const char* injectedBundleDLL = "\\InjectedBundle.dll";
55 #endif
56
addQTDirToPATH()57 static 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
exceptionFilter(EXCEPTION_POINTERS *)94 static LONG WINAPI exceptionFilter(EXCEPTION_POINTERS*)
95 {
96 fputs("#CRASHED\n", stderr);
97 fflush(stderr);
98 return EXCEPTION_CONTINUE_SEARCH;
99 }
100
notifyDone()101 void TestController::notifyDone()
102 {
103 }
104
platformInitialize()105 void 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
initializeInjectedBundlePath()124 void 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
initializeTestPluginDirectory()132 void 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
142 enum RunLoopResult { TimedOut, ObjectSignaled, ConditionSatisfied };
143
runRunLoopUntil(bool & condition,HANDLE object,double timeout)144 static 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
platformRunUntil(bool & done,double timeout)173 void 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
toWK(const char * string)197 static WKRetainPtr<WKStringRef> toWK(const char* string)
198 {
199 return WKRetainPtr<WKStringRef>(AdoptWK, WKStringCreateWithUTF8CString(string));
200 }
201
platformInitializeContext()202 void 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
runModal(PlatformWebView *)210 void TestController::runModal(PlatformWebView*)
211 {
212 // FIXME: Need to implement this to test showModalDialog.
213 }
214
platformLibraryPathForTesting()215 const char* TestController::platformLibraryPathForTesting()
216 {
217 return 0;
218 }
219
220 } // namespace WTR
221