TestControllerWin.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  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. #include "config.h"
  26. #include "TestController.h"
  27. #include <fcntl.h>
  28. #include <io.h>
  29. #include <shlwapi.h>
  30. #include <string>
  31. #include <WebKit2/WKContextPrivateWin.h>
  32. #include <WebKit2/WKStringCF.h>
  33. #include <wtf/RetainPtr.h>
  34. #include <wtf/Vector.h>
  35. using namespace std;
  36. namespace WTR {
  37. static HANDLE webProcessCrashingEvent;
  38. static const char webProcessCrashingEventName[] = "WebKitTestRunner.WebProcessCrashing";
  39. // This is the longest we'll wait (in seconds) for the web process to finish crashing and a crash
  40. // log to be saved. This interval should be just a tiny bit longer than it will ever reasonably
  41. // take to save a crash log.
  42. static const double maximumWaitForWebProcessToCrash = 60;
  43. #ifdef DEBUG_ALL
  44. const LPWSTR testPluginDirectoryName = L"TestNetscapePlugin_Debug";
  45. const char* injectedBundleDLL = "\\InjectedBundle_debug.dll";
  46. #else
  47. const LPWSTR testPluginDirectoryName = L"TestNetscapePlugin";
  48. const char* injectedBundleDLL = "\\InjectedBundle.dll";
  49. #endif
  50. static void addQTDirToPATH()
  51. {
  52. static LPCWSTR pathEnvironmentVariable = L"PATH";
  53. static LPCWSTR quickTimeKeyName = L"Software\\Apple Computer, Inc.\\QuickTime";
  54. static LPCWSTR quickTimeSysDir = L"QTSysDir";
  55. static bool initialized;
  56. if (initialized)
  57. return;
  58. initialized = true;
  59. // Get the QuickTime dll directory from the registry. The key can be in either HKLM or HKCU.
  60. WCHAR qtPath[MAX_PATH];
  61. DWORD qtPathBufferLen = sizeof(qtPath);
  62. DWORD keyType;
  63. HRESULT result = ::SHGetValueW(HKEY_LOCAL_MACHINE, quickTimeKeyName, quickTimeSysDir, &keyType, (LPVOID)qtPath, &qtPathBufferLen);
  64. if (result != ERROR_SUCCESS || !qtPathBufferLen || keyType != REG_SZ) {
  65. qtPathBufferLen = sizeof(qtPath);
  66. result = ::SHGetValueW(HKEY_CURRENT_USER, quickTimeKeyName, quickTimeSysDir, &keyType, (LPVOID)qtPath, &qtPathBufferLen);
  67. if (result != ERROR_SUCCESS || !qtPathBufferLen || keyType != REG_SZ)
  68. return;
  69. }
  70. // Read the current PATH.
  71. DWORD pathSize = ::GetEnvironmentVariableW(pathEnvironmentVariable, 0, 0);
  72. Vector<WCHAR> oldPath(pathSize);
  73. if (!::GetEnvironmentVariableW(pathEnvironmentVariable, oldPath.data(), oldPath.size()))
  74. return;
  75. // And add the QuickTime dll.
  76. wstring newPath;
  77. newPath.append(qtPath);
  78. newPath.append(L";");
  79. newPath.append(oldPath.data(), oldPath.size());
  80. ::SetEnvironmentVariableW(pathEnvironmentVariable, newPath.data());
  81. }
  82. static LONG WINAPI exceptionFilter(EXCEPTION_POINTERS*)
  83. {
  84. fputs("#CRASHED\n", stderr);
  85. fflush(stderr);
  86. return EXCEPTION_CONTINUE_SEARCH;
  87. }
  88. void TestController::notifyDone()
  89. {
  90. }
  91. void TestController::platformInitialize()
  92. {
  93. // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for
  94. // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the
  95. // error mode here to work around Cygwin's behavior. See <http://webkit.org/b/55222>.
  96. ::SetErrorMode(0);
  97. ::SetUnhandledExceptionFilter(exceptionFilter);
  98. _setmode(1, _O_BINARY);
  99. _setmode(2, _O_BINARY);
  100. // Add the QuickTime dll directory to PATH or QT 7.6 will fail to initialize on systems
  101. // linked with older versions of qtmlclientlib.dll.
  102. addQTDirToPATH();
  103. webProcessCrashingEvent = ::CreateEventA(0, FALSE, FALSE, webProcessCrashingEventName);
  104. }
  105. void TestController::platformDestroy()
  106. {
  107. }
  108. void TestController::initializeInjectedBundlePath()
  109. {
  110. CFStringRef exeContainerPath = CFURLCopyFileSystemPath(CFURLCreateCopyDeletingLastPathComponent(0, CFBundleCopyExecutableURL(CFBundleGetMainBundle())), kCFURLWindowsPathStyle);
  111. CFMutableStringRef bundlePath = CFStringCreateMutableCopy(0, 0, exeContainerPath);
  112. CFStringAppendCString(bundlePath, injectedBundleDLL, kCFStringEncodingWindowsLatin1);
  113. m_injectedBundlePath.adopt(WKStringCreateWithCFString(bundlePath));
  114. }
  115. void TestController::initializeTestPluginDirectory()
  116. {
  117. RetainPtr<CFURLRef> bundleURL = adoptCF(CFBundleCopyExecutableURL(CFBundleGetMainBundle()));
  118. RetainPtr<CFURLRef> bundleDirectoryURL = adoptCF(CFURLCreateCopyDeletingLastPathComponent(0, bundleURL.get()));
  119. RetainPtr<CFStringRef> testPluginDirectoryNameString = adoptCF(CFStringCreateWithCharacters(0, reinterpret_cast<const UniChar*>(testPluginDirectoryName), wcslen(testPluginDirectoryName)));
  120. RetainPtr<CFURLRef> testPluginDirectoryURL = adoptCF(CFURLCreateCopyAppendingPathComponent(0, bundleDirectoryURL.get(), testPluginDirectoryNameString.get(), true));
  121. RetainPtr<CFStringRef> testPluginDirectoryPath = adoptCF(CFURLCopyFileSystemPath(testPluginDirectoryURL.get(), kCFURLWindowsPathStyle));
  122. m_testPluginDirectory.adopt(WKStringCreateWithCFString(testPluginDirectoryPath.get()));
  123. }
  124. enum RunLoopResult { TimedOut, ObjectSignaled, ConditionSatisfied };
  125. static RunLoopResult runRunLoopUntil(bool& condition, HANDLE object, double timeout)
  126. {
  127. DWORD end = ::GetTickCount() + timeout * 1000;
  128. while (!condition) {
  129. DWORD now = ::GetTickCount();
  130. if (now > end)
  131. return TimedOut;
  132. DWORD objectCount = object ? 1 : 0;
  133. const HANDLE* objects = object ? &object : 0;
  134. DWORD result = ::MsgWaitForMultipleObjectsEx(objectCount, objects, end - now, QS_ALLINPUT, MWMO_INPUTAVAILABLE);
  135. if (result == WAIT_TIMEOUT)
  136. return TimedOut;
  137. if (objectCount && result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + objectCount)
  138. return ObjectSignaled;
  139. ASSERT(result == WAIT_OBJECT_0 + objectCount);
  140. // There are messages in the queue. Process them.
  141. MSG msg;
  142. while (::PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) {
  143. ::TranslateMessage(&msg);
  144. ::DispatchMessageW(&msg);
  145. }
  146. }
  147. return ConditionSatisfied;
  148. }
  149. void TestController::platformRunUntil(bool& done, double timeout)
  150. {
  151. // FIXME: No timeout should occur if timeout is equal to m_noTimeout (necessary when running performance tests).
  152. RunLoopResult result = runRunLoopUntil(done, webProcessCrashingEvent, timeout);
  153. if (result == TimedOut || result == ConditionSatisfied)
  154. return;
  155. ASSERT(result == ObjectSignaled);
  156. // The web process is crashing. A crash log might be being saved, which can take a long
  157. // time, and we don't want to time out while that happens.
  158. // First, let the test harness know this happened so it won't think we've hung. But
  159. // make sure we don't exit just yet!
  160. m_shouldExitWhenWebProcessCrashes = false;
  161. processDidCrash();
  162. m_shouldExitWhenWebProcessCrashes = true;
  163. // Then spin a run loop until it finishes crashing to give time for a crash log to be saved. If
  164. // it takes too long for a crash log to be saved, we'll just give up.
  165. bool neverSetCondition = false;
  166. result = runRunLoopUntil(neverSetCondition, 0, maximumWaitForWebProcessToCrash);
  167. ASSERT_UNUSED(result, result == TimedOut);
  168. exit(1);
  169. }
  170. static WKRetainPtr<WKStringRef> toWK(const char* string)
  171. {
  172. return WKRetainPtr<WKStringRef>(AdoptWK, WKStringCreateWithUTF8CString(string));
  173. }
  174. void TestController::platformInitializeContext()
  175. {
  176. // FIXME: Make DRT pass with Windows native controls. <http://webkit.org/b/25592>
  177. WKContextSetShouldPaintNativeControls(m_context.get(), false);
  178. WKContextSetInitializationUserDataForInjectedBundle(m_context.get(), toWK(webProcessCrashingEventName).get());
  179. }
  180. void TestController::runModal(PlatformWebView*)
  181. {
  182. // FIXME: Need to implement this to test showModalDialog.
  183. }
  184. const char* TestController::platformLibraryPathForTesting()
  185. {
  186. return 0;
  187. }
  188. } // namespace WTR