Home | History | Annotate | Download | only in win
      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 
     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 
     94 static LONG WINAPI exceptionFilter(EXCEPTION_POINTERS*)
     95 {
     96     fputs("#CRASHED\n", stderr);
     97     fflush(stderr);
     98     return EXCEPTION_CONTINUE_SEARCH;
     99 }
    100 
    101 void TestController::notifyDone()
    102 {
    103 }
    104 
    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 
    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 
    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 
    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 
    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 
    197 static WKRetainPtr<WKStringRef> toWK(const char* string)
    198 {
    199     return WKRetainPtr<WKStringRef>(AdoptWK, WKStringCreateWithUTF8CString(string));
    200 }
    201 
    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 
    210 void TestController::runModal(PlatformWebView*)
    211 {
    212     // FIXME: Need to implement this to test showModalDialog.
    213 }
    214 
    215 const char* TestController::platformLibraryPathForTesting()
    216 {
    217     return 0;
    218 }
    219 
    220 } // namespace WTR
    221