1 /* 2 * Copyright (C) 2005, 2006, 2007, 2008, 2009 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 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 14 * its contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "config.h" 30 #include "DumpRenderTree.h" 31 32 #include "EditingDelegate.h" 33 #include "FrameLoadDelegate.h" 34 #include "HistoryDelegate.h" 35 #include "LayoutTestController.h" 36 #include "PixelDumpSupport.h" 37 #include "PolicyDelegate.h" 38 #include "ResourceLoadDelegate.h" 39 #include "UIDelegate.h" 40 #include "WorkQueueItem.h" 41 #include "WorkQueue.h" 42 43 #include <comutil.h> 44 #include <fcntl.h> 45 #include <io.h> 46 #include <math.h> 47 #include <pthread.h> 48 #include <shlwapi.h> 49 #include <stdio.h> 50 #include <string.h> 51 #include <tchar.h> 52 #include <wtf/RetainPtr.h> 53 #include <wtf/Vector.h> 54 #include <windows.h> 55 #include <CoreFoundation/CoreFoundation.h> 56 #include <JavaScriptCore/JavaScriptCore.h> 57 #include <WebKit/WebKit.h> 58 #include <WebKit/WebKitCOMAPI.h> 59 60 #if USE(CFNETWORK) 61 #include <CFNetwork/CFURLCachePriv.h> 62 #endif 63 64 #if USE(CFNETWORK) 65 #include <CFNetwork/CFHTTPCookiesPriv.h> 66 #endif 67 68 using namespace std; 69 70 #ifdef DEBUG_ALL 71 const LPWSTR TestPluginDir = L"TestNetscapePlugin_Debug"; 72 #else 73 const LPWSTR TestPluginDir = L"TestNetscapePlugin"; 74 #endif 75 76 static LPCWSTR fontsEnvironmentVariable = L"WEBKIT_TESTFONTS"; 77 #define USE_MAC_FONTS 78 79 const LPCWSTR kDumpRenderTreeClassName = L"DumpRenderTreeWindow"; 80 81 static bool dumpTree = true; 82 static bool dumpPixels; 83 static bool dumpAllPixels; 84 static bool printSeparators; 85 static bool leakChecking = false; 86 static bool threaded = false; 87 static bool forceComplexText = false; 88 static bool printSupportedFeatures = false; 89 static RetainPtr<CFStringRef> persistentUserStyleSheetLocation; 90 91 volatile bool done; 92 // This is the topmost frame that is loading, during a given load, or nil when no load is 93 // in progress. Usually this is the same as the main frame, but not always. In the case 94 // where a frameset is loaded, and then new content is loaded into one of the child frames, 95 // that child frame is the "topmost frame that is loading". 96 IWebFrame* topLoadingFrame; // !nil iff a load is in progress 97 static COMPtr<IWebHistoryItem> prevTestBFItem; // current b/f item at the end of the previous test 98 PolicyDelegate* policyDelegate; 99 COMPtr<FrameLoadDelegate> sharedFrameLoadDelegate; 100 COMPtr<UIDelegate> sharedUIDelegate; 101 COMPtr<EditingDelegate> sharedEditingDelegate; 102 COMPtr<HistoryDelegate> sharedHistoryDelegate; 103 104 IWebFrame* frame; 105 HWND webViewWindow; 106 107 RefPtr<LayoutTestController> gLayoutTestController; 108 109 UINT_PTR waitToDumpWatchdog = 0; 110 111 void setPersistentUserStyleSheetLocation(CFStringRef url) 112 { 113 persistentUserStyleSheetLocation = url; 114 } 115 116 bool setAlwaysAcceptCookies(bool alwaysAcceptCookies) 117 { 118 #if USE(CFNETWORK) 119 COMPtr<IWebCookieManager> cookieManager; 120 if (FAILED(WebKitCreateInstance(CLSID_WebCookieManager, 0, IID_IWebCookieManager, reinterpret_cast<void**>(&cookieManager)))) 121 return false; 122 CFHTTPCookieStorageRef cookieStorage = 0; 123 if (FAILED(cookieManager->cookieStorage(&cookieStorage)) || !cookieStorage) 124 return false; 125 126 WebKitCookieStorageAcceptPolicy cookieAcceptPolicy = alwaysAcceptCookies ? WebKitCookieStorageAcceptPolicyAlways : WebKitCookieStorageAcceptPolicyOnlyFromMainDocumentDomain; 127 CFHTTPCookieStorageSetCookieAcceptPolicy(cookieStorage, cookieAcceptPolicy); 128 return true; 129 #else 130 // FIXME: Implement! 131 return false; 132 #endif 133 } 134 135 static RetainPtr<CFStringRef> substringFromIndex(CFStringRef string, CFIndex index) 136 { 137 return RetainPtr<CFStringRef>(AdoptCF, CFStringCreateWithSubstring(kCFAllocatorDefault, string, CFRangeMake(index, CFStringGetLength(string) - index))); 138 } 139 140 wstring urlSuitableForTestResult(const wstring& urlString) 141 { 142 RetainPtr<CFURLRef> url(AdoptCF, CFURLCreateWithBytes(kCFAllocatorDefault, reinterpret_cast<const UInt8*>(urlString.c_str()), urlString.length() * sizeof(wstring::value_type), kCFStringEncodingUTF16, 0)); 143 144 RetainPtr<CFStringRef> scheme(AdoptCF, CFURLCopyScheme(url.get())); 145 if (scheme && CFStringCompare(scheme.get(), CFSTR("file"), kCFCompareCaseInsensitive) != kCFCompareEqualTo) 146 return urlString; 147 148 COMPtr<IWebDataSource> dataSource; 149 if (FAILED(frame->dataSource(&dataSource))) { 150 if (FAILED(frame->provisionalDataSource(&dataSource))) 151 return urlString; 152 } 153 154 COMPtr<IWebMutableURLRequest> request; 155 if (FAILED(dataSource->request(&request))) 156 return urlString; 157 158 _bstr_t requestURLString; 159 if (FAILED(request->URL(requestURLString.GetAddress()))) 160 return urlString; 161 162 RetainPtr<CFURLRef> requestURL(AdoptCF, CFURLCreateWithBytes(kCFAllocatorDefault, reinterpret_cast<const UInt8*>(requestURLString.GetBSTR()), requestURLString.length() * sizeof(OLECHAR), kCFStringEncodingUTF16, 0)); 163 RetainPtr<CFURLRef> baseURL(AdoptCF, CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, requestURL.get())); 164 165 RetainPtr<CFStringRef> basePath(AdoptCF, CFURLCopyPath(baseURL.get())); 166 RetainPtr<CFStringRef> path(AdoptCF, CFURLCopyPath(url.get())); 167 168 return cfStringRefToWString(substringFromIndex(path.get(), CFStringGetLength(basePath.get())).get()); 169 } 170 171 wstring lastPathComponent(const wstring& urlString) 172 { 173 if (urlString.empty()) 174 return urlString; 175 176 RetainPtr<CFURLRef> url(AdoptCF, CFURLCreateWithBytes(kCFAllocatorDefault, reinterpret_cast<const UInt8*>(urlString.c_str()), urlString.length() * sizeof(wstring::value_type), kCFStringEncodingUTF16, 0)); 177 RetainPtr<CFStringRef> lastPathComponent(CFURLCopyLastPathComponent(url.get())); 178 179 return cfStringRefToWString(lastPathComponent.get()); 180 } 181 182 static string toUTF8(const wchar_t* wideString, size_t length) 183 { 184 int result = WideCharToMultiByte(CP_UTF8, 0, wideString, length + 1, 0, 0, 0, 0); 185 Vector<char> utf8Vector(result); 186 result = WideCharToMultiByte(CP_UTF8, 0, wideString, length + 1, utf8Vector.data(), result, 0, 0); 187 if (!result) 188 return string(); 189 190 return string(utf8Vector.data(), utf8Vector.size() - 1); 191 } 192 193 string toUTF8(BSTR bstr) 194 { 195 return toUTF8(bstr, SysStringLen(bstr)); 196 } 197 198 string toUTF8(const wstring& wideString) 199 { 200 return toUTF8(wideString.c_str(), wideString.length()); 201 } 202 203 wstring cfStringRefToWString(CFStringRef cfStr) 204 { 205 Vector<wchar_t> v(CFStringGetLength(cfStr)); 206 CFStringGetCharacters(cfStr, CFRangeMake(0, CFStringGetLength(cfStr)), (UniChar *)v.data()); 207 208 return wstring(v.data(), v.size()); 209 } 210 211 static LRESULT CALLBACK DumpRenderTreeWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 212 { 213 switch (msg) { 214 case WM_DESTROY: 215 for (unsigned i = openWindows().size() - 1; i >= 0; --i) { 216 if (openWindows()[i] == hWnd) { 217 openWindows().remove(i); 218 windowToWebViewMap().remove(hWnd); 219 break; 220 } 221 } 222 return 0; 223 break; 224 default: 225 return DefWindowProc(hWnd, msg, wParam, lParam); 226 } 227 } 228 229 static const wstring& exePath() 230 { 231 static wstring path; 232 static bool initialized; 233 234 if (initialized) 235 return path; 236 initialized = true; 237 238 TCHAR buffer[MAX_PATH]; 239 GetModuleFileName(GetModuleHandle(0), buffer, ARRAYSIZE(buffer)); 240 path = buffer; 241 int lastSlash = path.rfind('\\'); 242 if (lastSlash != -1 && lastSlash + 1 < path.length()) 243 path = path.substr(0, lastSlash + 1); 244 245 return path; 246 } 247 248 static const wstring& fontsPath() 249 { 250 static wstring path; 251 static bool initialized; 252 253 if (initialized) 254 return path; 255 initialized = true; 256 257 DWORD size = GetEnvironmentVariable(fontsEnvironmentVariable, 0, 0); 258 Vector<TCHAR> buffer(size); 259 if (GetEnvironmentVariable(fontsEnvironmentVariable, buffer.data(), buffer.size())) { 260 path = buffer.data(); 261 if (path[path.length() - 1] != '\\') 262 path.append(L"\\"); 263 return path; 264 } 265 266 path = exePath() + TEXT("DumpRenderTree.resources\\"); 267 return path; 268 } 269 270 static void addQTDirToPATH() 271 { 272 static LPCWSTR pathEnvironmentVariable = L"PATH"; 273 static LPCWSTR quickTimeKeyName = L"Software\\Apple Computer, Inc.\\QuickTime"; 274 static LPCWSTR quickTimeSysDir = L"QTSysDir"; 275 static bool initialized; 276 277 if (initialized) 278 return; 279 initialized = true; 280 281 // Get the QuickTime dll directory from the registry. The key can be in either HKLM or HKCU. 282 WCHAR qtPath[MAX_PATH]; 283 DWORD qtPathBufferLen = sizeof(qtPath); 284 DWORD keyType; 285 HRESULT result = SHGetValue(HKEY_LOCAL_MACHINE, quickTimeKeyName, quickTimeSysDir, &keyType, (LPVOID)qtPath, &qtPathBufferLen); 286 if (result != ERROR_SUCCESS || !qtPathBufferLen || keyType != REG_SZ) { 287 qtPathBufferLen = sizeof(qtPath); 288 result = SHGetValue(HKEY_CURRENT_USER, quickTimeKeyName, quickTimeSysDir, &keyType, (LPVOID)qtPath, &qtPathBufferLen); 289 if (result != ERROR_SUCCESS || !qtPathBufferLen || keyType != REG_SZ) 290 return; 291 } 292 293 // Read the current PATH. 294 DWORD pathSize = GetEnvironmentVariableW(pathEnvironmentVariable, 0, 0); 295 Vector<WCHAR> oldPath(pathSize); 296 if (!GetEnvironmentVariableW(pathEnvironmentVariable, oldPath.data(), oldPath.size())) 297 return; 298 299 // And add the QuickTime dll. 300 wstring newPath; 301 newPath.append(qtPath); 302 newPath.append(L";"); 303 newPath.append(oldPath.data(), oldPath.size()); 304 SetEnvironmentVariableW(pathEnvironmentVariable, newPath.data()); 305 } 306 307 #ifdef DEBUG_ALL 308 #define WEBKITDLL TEXT("WebKit_debug.dll") 309 #else 310 #define WEBKITDLL TEXT("WebKit.dll") 311 #endif 312 313 static void initialize() 314 { 315 if (HMODULE webKitModule = LoadLibrary(WEBKITDLL)) 316 if (FARPROC dllRegisterServer = GetProcAddress(webKitModule, "DllRegisterServer")) 317 dllRegisterServer(); 318 319 // Init COM 320 OleInitialize(0); 321 322 static LPCTSTR fontsToInstall[] = { 323 TEXT("AHEM____.ttf"), 324 TEXT("Apple Chancery.ttf"), 325 TEXT("Courier Bold.ttf"), 326 TEXT("Courier.ttf"), 327 TEXT("Helvetica Bold Oblique.ttf"), 328 TEXT("Helvetica Bold.ttf"), 329 TEXT("Helvetica Oblique.ttf"), 330 TEXT("Helvetica.ttf"), 331 TEXT("Helvetica Neue Bold Italic.ttf"), 332 TEXT("Helvetica Neue Bold.ttf"), 333 TEXT("Helvetica Neue Condensed Black.ttf"), 334 TEXT("Helvetica Neue Condensed Bold.ttf"), 335 TEXT("Helvetica Neue Italic.ttf"), 336 TEXT("Helvetica Neue Light Italic.ttf"), 337 TEXT("Helvetica Neue Light.ttf"), 338 TEXT("Helvetica Neue UltraLight Italic.ttf"), 339 TEXT("Helvetica Neue UltraLight.ttf"), 340 TEXT("Helvetica Neue.ttf"), 341 TEXT("Lucida Grande.ttf"), 342 TEXT("Lucida Grande Bold.ttf"), 343 TEXT("Monaco.ttf"), 344 TEXT("Papyrus.ttf"), 345 TEXT("Times Bold Italic.ttf"), 346 TEXT("Times Bold.ttf"), 347 TEXT("Times Italic.ttf"), 348 TEXT("Times Roman.ttf"), 349 TEXT("WebKit Layout Tests 2.ttf"), 350 TEXT("WebKit Layout Tests.ttf"), 351 TEXT("WebKitWeightWatcher100.ttf"), 352 TEXT("WebKitWeightWatcher200.ttf"), 353 TEXT("WebKitWeightWatcher300.ttf"), 354 TEXT("WebKitWeightWatcher400.ttf"), 355 TEXT("WebKitWeightWatcher500.ttf"), 356 TEXT("WebKitWeightWatcher600.ttf"), 357 TEXT("WebKitWeightWatcher700.ttf"), 358 TEXT("WebKitWeightWatcher800.ttf"), 359 TEXT("WebKitWeightWatcher900.ttf") 360 }; 361 362 wstring resourcesPath = fontsPath(); 363 364 COMPtr<IWebTextRenderer> textRenderer; 365 if (SUCCEEDED(WebKitCreateInstance(CLSID_WebTextRenderer, 0, IID_IWebTextRenderer, (void**)&textRenderer))) 366 for (int i = 0; i < ARRAYSIZE(fontsToInstall); ++i) 367 textRenderer->registerPrivateFont(wstring(resourcesPath + fontsToInstall[i]).c_str()); 368 369 // Add the QuickTime dll directory to PATH or QT 7.6 will fail to initialize on systems 370 // linked with older versions of qtmlclientlib.dll. 371 addQTDirToPATH(); 372 373 // Register a host window 374 WNDCLASSEX wcex; 375 376 wcex.cbSize = sizeof(WNDCLASSEX); 377 378 wcex.style = CS_HREDRAW | CS_VREDRAW; 379 wcex.lpfnWndProc = DumpRenderTreeWndProc; 380 wcex.cbClsExtra = 0; 381 wcex.cbWndExtra = 0; 382 wcex.hInstance = GetModuleHandle(0); 383 wcex.hIcon = 0; 384 wcex.hCursor = LoadCursor(0, IDC_ARROW); 385 wcex.hbrBackground = 0; 386 wcex.lpszMenuName = 0; 387 wcex.lpszClassName = kDumpRenderTreeClassName; 388 wcex.hIconSm = 0; 389 390 RegisterClassEx(&wcex); 391 } 392 393 void displayWebView() 394 { 395 ::InvalidateRect(webViewWindow, 0, TRUE); 396 ::SendMessage(webViewWindow, WM_PAINT, 0, 0); 397 } 398 399 void dumpFrameScrollPosition(IWebFrame* frame) 400 { 401 if (!frame) 402 return; 403 404 COMPtr<IWebFramePrivate> framePrivate; 405 if (FAILED(frame->QueryInterface(&framePrivate))) 406 return; 407 408 SIZE scrollPosition; 409 if (FAILED(framePrivate->scrollOffset(&scrollPosition))) 410 return; 411 412 if (abs(scrollPosition.cx) > 0.00000001 || abs(scrollPosition.cy) > 0.00000001) { 413 COMPtr<IWebFrame> parent; 414 if (FAILED(frame->parentFrame(&parent))) 415 return; 416 if (parent) { 417 BSTR name; 418 if (FAILED(frame->name(&name))) 419 return; 420 printf("frame '%S' ", name ? name : L""); 421 SysFreeString(name); 422 } 423 printf("scrolled to %.f,%.f\n", (double)scrollPosition.cx, (double)scrollPosition.cy); 424 } 425 426 if (::gLayoutTestController->dumpChildFrameScrollPositions()) { 427 COMPtr<IEnumVARIANT> enumKids; 428 if (FAILED(frame->childFrames(&enumKids))) 429 return; 430 VARIANT var; 431 VariantInit(&var); 432 while (enumKids->Next(1, &var, 0) == S_OK) { 433 ASSERT(V_VT(&var) == VT_UNKNOWN); 434 COMPtr<IWebFrame> framePtr; 435 V_UNKNOWN(&var)->QueryInterface(IID_IWebFrame, (void**)&framePtr); 436 dumpFrameScrollPosition(framePtr.get()); 437 VariantClear(&var); 438 } 439 } 440 } 441 442 static wstring dumpFramesAsText(IWebFrame* frame) 443 { 444 if (!frame) 445 return L""; 446 447 COMPtr<IDOMDocument> document; 448 if (FAILED(frame->DOMDocument(&document))) 449 return L""; 450 451 COMPtr<IDOMElement> documentElement; 452 if (FAILED(document->documentElement(&documentElement))) 453 return L""; 454 455 wstring result; 456 457 // Add header for all but the main frame. 458 COMPtr<IWebFrame> parent; 459 if (FAILED(frame->parentFrame(&parent))) 460 return L""; 461 if (parent) { 462 BSTR name = L""; 463 if (FAILED(frame->name(&name))) 464 return L""; 465 466 result.append(L"\n--------\nFrame: '"); 467 result.append(name ? name : L"", SysStringLen(name)); 468 result.append(L"'\n--------\n"); 469 470 SysFreeString(name); 471 } 472 473 BSTR innerText = 0; 474 COMPtr<IDOMElementPrivate> docPrivate; 475 if (SUCCEEDED(documentElement->QueryInterface(&docPrivate))) 476 docPrivate->innerText(&innerText); 477 478 result.append(innerText ? innerText : L"", SysStringLen(innerText)); 479 result.append(L"\n"); 480 481 SysFreeString(innerText); 482 483 if (::gLayoutTestController->dumpChildFramesAsText()) { 484 COMPtr<IEnumVARIANT> enumKids; 485 if (FAILED(frame->childFrames(&enumKids))) 486 return L""; 487 VARIANT var; 488 VariantInit(&var); 489 while (enumKids->Next(1, &var, 0) == S_OK) { 490 ASSERT(V_VT(&var) == VT_UNKNOWN); 491 COMPtr<IWebFrame> framePtr; 492 V_UNKNOWN(&var)->QueryInterface(IID_IWebFrame, (void**)&framePtr); 493 result.append(dumpFramesAsText(framePtr.get())); 494 VariantClear(&var); 495 } 496 } 497 498 return result; 499 } 500 501 static int compareHistoryItems(const void* item1, const void* item2) 502 { 503 COMPtr<IWebHistoryItemPrivate> itemA; 504 if (FAILED((*(COMPtr<IUnknown>*)item1)->QueryInterface(&itemA))) 505 return 0; 506 507 COMPtr<IWebHistoryItemPrivate> itemB; 508 if (FAILED((*(COMPtr<IUnknown>*)item2)->QueryInterface(&itemB))) 509 return 0; 510 511 BSTR targetA; 512 if (FAILED(itemA->target(&targetA))) 513 return 0; 514 515 BSTR targetB; 516 if (FAILED(itemB->target(&targetB))) { 517 SysFreeString(targetA); 518 return 0; 519 } 520 521 int result = wcsicmp(wstring(targetA, SysStringLen(targetA)).c_str(), wstring(targetB, SysStringLen(targetB)).c_str()); 522 SysFreeString(targetA); 523 SysFreeString(targetB); 524 return result; 525 } 526 527 static void dumpHistoryItem(IWebHistoryItem* item, int indent, bool current) 528 { 529 assert(item); 530 531 int start = 0; 532 if (current) { 533 printf("curr->"); 534 start = 6; 535 } 536 for (int i = start; i < indent; i++) 537 putchar(' '); 538 539 BSTR url; 540 if (FAILED(item->URLString(&url))) 541 return; 542 543 if (wcsstr(url, L"file:/") == url) { 544 static wchar_t* layoutTestsString = L"/LayoutTests/"; 545 static wchar_t* fileTestString = L"(file test):"; 546 547 wchar_t* result = wcsstr(url, layoutTestsString); 548 if (result == NULL) 549 return; 550 wchar_t* start = result + wcslen(layoutTestsString); 551 552 BSTR newURL = SysAllocStringLen(NULL, SysStringLen(url)); 553 wcscpy(newURL, fileTestString); 554 wcscpy(newURL + wcslen(fileTestString), start); 555 556 SysFreeString(url); 557 url = newURL; 558 } 559 560 printf("%S", url ? url : L""); 561 SysFreeString(url); 562 563 COMPtr<IWebHistoryItemPrivate> itemPrivate; 564 if (FAILED(item->QueryInterface(&itemPrivate))) 565 return; 566 567 BSTR target; 568 if (FAILED(itemPrivate->target(&target))) 569 return; 570 if (SysStringLen(target)) 571 printf(" (in frame \"%S\")", target); 572 SysFreeString(target); 573 BOOL isTargetItem = FALSE; 574 if (FAILED(itemPrivate->isTargetItem(&isTargetItem))) 575 return; 576 if (isTargetItem) 577 printf(" **nav target**"); 578 putchar('\n'); 579 580 unsigned kidsCount; 581 SAFEARRAY* arrPtr; 582 if (FAILED(itemPrivate->children(&kidsCount, &arrPtr)) || !kidsCount) 583 return; 584 585 Vector<COMPtr<IUnknown> > kidsVector; 586 587 LONG lowerBound; 588 if (FAILED(::SafeArrayGetLBound(arrPtr, 1, &lowerBound))) 589 goto exit; 590 591 LONG upperBound; 592 if (FAILED(::SafeArrayGetUBound(arrPtr, 1, &upperBound))) 593 goto exit; 594 595 LONG length = upperBound - lowerBound + 1; 596 if (!length) 597 goto exit; 598 ASSERT(length == kidsCount); 599 600 IUnknown** safeArrayData; 601 if (FAILED(::SafeArrayAccessData(arrPtr, (void**)&safeArrayData))) 602 goto exit; 603 604 for (int i = 0; i < length; ++i) 605 kidsVector.append(safeArrayData[i]); 606 ::SafeArrayUnaccessData(arrPtr); 607 608 // must sort to eliminate arbitrary result ordering which defeats reproducible testing 609 qsort(kidsVector.data(), kidsCount, sizeof(kidsVector[0]), compareHistoryItems); 610 611 for (unsigned i = 0; i < kidsCount; ++i) { 612 COMPtr<IWebHistoryItem> item; 613 kidsVector[i]->QueryInterface(&item); 614 dumpHistoryItem(item.get(), indent + 4, false); 615 } 616 617 exit: 618 if (arrPtr && SUCCEEDED(::SafeArrayUnlock(arrPtr))) 619 ::SafeArrayDestroy(arrPtr); 620 } 621 622 static void dumpBackForwardList(IWebView* webView) 623 { 624 ASSERT(webView); 625 626 printf("\n============== Back Forward List ==============\n"); 627 628 COMPtr<IWebBackForwardList> bfList; 629 if (FAILED(webView->backForwardList(&bfList))) 630 return; 631 632 // Print out all items in the list after prevTestBFItem, which was from the previous test 633 // Gather items from the end of the list, the print them out from oldest to newest 634 635 Vector<COMPtr<IUnknown> > itemsToPrint; 636 637 int forwardListCount; 638 if (FAILED(bfList->forwardListCount(&forwardListCount))) 639 return; 640 641 for (int i = forwardListCount; i > 0; --i) { 642 COMPtr<IWebHistoryItem> item; 643 if (FAILED(bfList->itemAtIndex(i, &item))) 644 return; 645 // something is wrong if the item from the last test is in the forward part of the b/f list 646 assert(item != prevTestBFItem); 647 COMPtr<IUnknown> itemUnknown; 648 item->QueryInterface(&itemUnknown); 649 itemsToPrint.append(itemUnknown); 650 } 651 652 COMPtr<IWebHistoryItem> currentItem; 653 if (FAILED(bfList->currentItem(¤tItem))) 654 return; 655 656 assert(currentItem != prevTestBFItem); 657 COMPtr<IUnknown> currentItemUnknown; 658 currentItem->QueryInterface(¤tItemUnknown); 659 itemsToPrint.append(currentItemUnknown); 660 int currentItemIndex = itemsToPrint.size() - 1; 661 662 int backListCount; 663 if (FAILED(bfList->backListCount(&backListCount))) 664 return; 665 666 for (int i = -1; i >= -backListCount; --i) { 667 COMPtr<IWebHistoryItem> item; 668 if (FAILED(bfList->itemAtIndex(i, &item))) 669 return; 670 if (item == prevTestBFItem) 671 break; 672 COMPtr<IUnknown> itemUnknown; 673 item->QueryInterface(&itemUnknown); 674 itemsToPrint.append(itemUnknown); 675 } 676 677 for (int i = itemsToPrint.size() - 1; i >= 0; --i) { 678 COMPtr<IWebHistoryItem> historyItemToPrint; 679 itemsToPrint[i]->QueryInterface(&historyItemToPrint); 680 dumpHistoryItem(historyItemToPrint.get(), 8, i == currentItemIndex); 681 } 682 683 printf("===============================================\n"); 684 } 685 686 static void dumpBackForwardListForAllWindows() 687 { 688 unsigned count = openWindows().size(); 689 for (unsigned i = 0; i < count; i++) { 690 HWND window = openWindows()[i]; 691 IWebView* webView = windowToWebViewMap().get(window).get(); 692 dumpBackForwardList(webView); 693 } 694 } 695 696 static void invalidateAnyPreviousWaitToDumpWatchdog() 697 { 698 if (!waitToDumpWatchdog) 699 return; 700 701 KillTimer(0, waitToDumpWatchdog); 702 waitToDumpWatchdog = 0; 703 } 704 705 void dump() 706 { 707 invalidateAnyPreviousWaitToDumpWatchdog(); 708 709 COMPtr<IWebDataSource> dataSource; 710 if (SUCCEEDED(frame->dataSource(&dataSource))) { 711 COMPtr<IWebURLResponse> response; 712 if (SUCCEEDED(dataSource->response(&response)) && response) { 713 BSTR mimeType; 714 if (SUCCEEDED(response->MIMEType(&mimeType)) && !_tcscmp(mimeType, TEXT("text/plain"))) { 715 ::gLayoutTestController->setDumpAsText(true); 716 ::gLayoutTestController->setGeneratePixelResults(false); 717 } 718 SysFreeString(mimeType); 719 } 720 } 721 722 BSTR resultString = 0; 723 724 if (dumpTree) { 725 if (::gLayoutTestController->dumpAsText()) { 726 ::InvalidateRect(webViewWindow, 0, TRUE); 727 ::SendMessage(webViewWindow, WM_PAINT, 0, 0); 728 wstring result = dumpFramesAsText(frame); 729 resultString = SysAllocStringLen(result.data(), result.size()); 730 } else { 731 bool isSVGW3CTest = (gLayoutTestController->testPathOrURL().find("svg\\W3C-SVG-1.1") != string::npos); 732 unsigned width; 733 unsigned height; 734 if (isSVGW3CTest) { 735 width = 480; 736 height = 360; 737 } else { 738 width = LayoutTestController::maxViewWidth; 739 height = LayoutTestController::maxViewHeight; 740 } 741 742 ::SetWindowPos(webViewWindow, 0, 0, 0, width, height, SWP_NOMOVE); 743 ::InvalidateRect(webViewWindow, 0, TRUE); 744 ::SendMessage(webViewWindow, WM_PAINT, 0, 0); 745 746 COMPtr<IWebFramePrivate> framePrivate; 747 if (FAILED(frame->QueryInterface(&framePrivate))) 748 goto fail; 749 framePrivate->renderTreeAsExternalRepresentation(gLayoutTestController->isPrinting(), &resultString); 750 } 751 752 if (!resultString) 753 printf("ERROR: nil result from %s", ::gLayoutTestController->dumpAsText() ? "IDOMElement::innerText" : "IFrameViewPrivate::renderTreeAsExternalRepresentation"); 754 else { 755 unsigned stringLength = SysStringLen(resultString); 756 int bufferSize = ::WideCharToMultiByte(CP_UTF8, 0, resultString, stringLength, 0, 0, 0, 0); 757 char* buffer = (char*)malloc(bufferSize + 1); 758 ::WideCharToMultiByte(CP_UTF8, 0, resultString, stringLength, buffer, bufferSize + 1, 0, 0); 759 fwrite(buffer, 1, bufferSize, stdout); 760 free(buffer); 761 if (!::gLayoutTestController->dumpAsText()) 762 dumpFrameScrollPosition(frame); 763 } 764 if (::gLayoutTestController->dumpBackForwardList()) 765 dumpBackForwardListForAllWindows(); 766 } 767 768 if (printSeparators) { 769 puts("#EOF"); // terminate the content block 770 fputs("#EOF\n", stderr); 771 fflush(stdout); 772 fflush(stderr); 773 } 774 775 if (dumpPixels 776 && gLayoutTestController->generatePixelResults() 777 && !gLayoutTestController->dumpDOMAsWebArchive() 778 && !gLayoutTestController->dumpSourceAsWebArchive()) 779 dumpWebViewAsPixelsAndCompareWithExpected(gLayoutTestController->expectedPixelHash()); 780 781 printf("#EOF\n"); // terminate the (possibly empty) pixels block 782 fflush(stdout); 783 784 fail: 785 SysFreeString(resultString); 786 // This will exit from our message loop. 787 PostQuitMessage(0); 788 done = true; 789 } 790 791 static bool shouldLogFrameLoadDelegates(const char* pathOrURL) 792 { 793 return strstr(pathOrURL, "/loading/") || strstr(pathOrURL, "\\loading\\"); 794 } 795 796 static bool shouldLogHistoryDelegates(const char* pathOrURL) 797 { 798 return strstr(pathOrURL, "/globalhistory/") || strstr(pathOrURL, "\\globalhistory\\"); 799 } 800 801 static bool shouldOpenWebInspector(const char* pathOrURL) 802 { 803 return strstr(pathOrURL, "/inspector/") || strstr(pathOrURL, "\\inspector\\"); 804 } 805 806 static bool shouldDumpAsText(const char* pathOrURL) 807 { 808 return strstr(pathOrURL, "/dumpAsText/") || strstr(pathOrURL, "\\dumpAsText\\"); 809 } 810 811 static bool shouldEnableDeveloperExtras(const char* pathOrURL) 812 { 813 return true; 814 } 815 816 static void resetDefaultsToConsistentValues(IWebPreferences* preferences) 817 { 818 #ifdef USE_MAC_FONTS 819 static BSTR standardFamily = SysAllocString(TEXT("Times")); 820 static BSTR fixedFamily = SysAllocString(TEXT("Courier")); 821 static BSTR sansSerifFamily = SysAllocString(TEXT("Helvetica")); 822 static BSTR cursiveFamily = SysAllocString(TEXT("Apple Chancery")); 823 static BSTR fantasyFamily = SysAllocString(TEXT("Papyrus")); 824 #else 825 static BSTR standardFamily = SysAllocString(TEXT("Times New Roman")); 826 static BSTR fixedFamily = SysAllocString(TEXT("Courier New")); 827 static BSTR sansSerifFamily = SysAllocString(TEXT("Arial")); 828 static BSTR cursiveFamily = SysAllocString(TEXT("Comic Sans MS")); // Not actually cursive, but it's what IE and Firefox use. 829 static BSTR fantasyFamily = SysAllocString(TEXT("Times New Roman")); 830 #endif 831 832 preferences->setStandardFontFamily(standardFamily); 833 preferences->setFixedFontFamily(fixedFamily); 834 preferences->setSerifFontFamily(standardFamily); 835 preferences->setSansSerifFontFamily(sansSerifFamily); 836 preferences->setCursiveFontFamily(cursiveFamily); 837 preferences->setFantasyFontFamily(fantasyFamily); 838 839 preferences->setAutosaves(FALSE); 840 preferences->setDefaultFontSize(16); 841 preferences->setDefaultFixedFontSize(13); 842 preferences->setMinimumFontSize(0); 843 preferences->setJavaEnabled(FALSE); 844 preferences->setPlugInsEnabled(TRUE); 845 preferences->setDOMPasteAllowed(TRUE); 846 preferences->setEditableLinkBehavior(WebKitEditableLinkOnlyLiveWithShiftKey); 847 preferences->setFontSmoothing(FontSmoothingTypeStandard); 848 preferences->setUsesPageCache(FALSE); 849 preferences->setPrivateBrowsingEnabled(FALSE); 850 preferences->setJavaScriptCanOpenWindowsAutomatically(TRUE); 851 preferences->setJavaScriptEnabled(TRUE); 852 preferences->setTabsToLinks(FALSE); 853 preferences->setShouldPrintBackgrounds(TRUE); 854 preferences->setLoadsImagesAutomatically(TRUE); 855 preferences->setEditingBehavior(WebKitEditingWinBehavior); 856 857 if (persistentUserStyleSheetLocation) { 858 Vector<wchar_t> urlCharacters(CFStringGetLength(persistentUserStyleSheetLocation.get())); 859 CFStringGetCharacters(persistentUserStyleSheetLocation.get(), CFRangeMake(0, CFStringGetLength(persistentUserStyleSheetLocation.get())), (UniChar *)urlCharacters.data()); 860 BSTR url = SysAllocStringLen(urlCharacters.data(), urlCharacters.size()); 861 preferences->setUserStyleSheetLocation(url); 862 SysFreeString(url); 863 preferences->setUserStyleSheetEnabled(TRUE); 864 } else 865 preferences->setUserStyleSheetEnabled(FALSE); 866 867 COMPtr<IWebPreferencesPrivate> prefsPrivate(Query, preferences); 868 if (prefsPrivate) { 869 prefsPrivate->setAllowUniversalAccessFromFileURLs(TRUE); 870 prefsPrivate->setAllowFileAccessFromFileURLs(TRUE); 871 prefsPrivate->setAuthorAndUserStylesEnabled(TRUE); 872 prefsPrivate->setDeveloperExtrasEnabled(FALSE); 873 prefsPrivate->setExperimentalNotificationsEnabled(TRUE); 874 prefsPrivate->setShouldPaintNativeControls(FALSE); // FIXME - need to make DRT pass with Windows native controls <http://bugs.webkit.org/show_bug.cgi?id=25592> 875 prefsPrivate->setJavaScriptCanAccessClipboard(TRUE); 876 prefsPrivate->setXSSAuditorEnabled(FALSE); 877 prefsPrivate->setFrameFlatteningEnabled(FALSE); 878 prefsPrivate->setOfflineWebApplicationCacheEnabled(TRUE); 879 prefsPrivate->setLoadsSiteIconsIgnoringImageLoadingPreference(FALSE); 880 } 881 setAlwaysAcceptCookies(false); 882 883 setlocale(LC_ALL, ""); 884 } 885 886 static void resetWebViewToConsistentStateBeforeTesting() 887 { 888 COMPtr<IWebView> webView; 889 if (FAILED(frame->webView(&webView))) 890 return; 891 892 webView->setPolicyDelegate(0); 893 policyDelegate->setPermissive(false); 894 policyDelegate->setControllerToNotifyDone(0); 895 896 COMPtr<IWebIBActions> webIBActions(Query, webView); 897 if (webIBActions) { 898 webIBActions->makeTextStandardSize(0); 899 webIBActions->resetPageZoom(0); 900 } 901 902 903 COMPtr<IWebPreferences> preferences; 904 if (SUCCEEDED(webView->preferences(&preferences))) 905 resetDefaultsToConsistentValues(preferences.get()); 906 907 COMPtr<IWebViewEditing> viewEditing; 908 if (SUCCEEDED(webView->QueryInterface(&viewEditing))) 909 viewEditing->setSmartInsertDeleteEnabled(TRUE); 910 911 COMPtr<IWebViewPrivate> webViewPrivate(Query, webView); 912 if (!webViewPrivate) 913 return; 914 915 double minimumInterval = 0; 916 if (SUCCEEDED(webViewPrivate->defaultMinimumTimerInterval(&minimumInterval))) 917 webViewPrivate->setMinimumTimerInterval(minimumInterval); 918 919 COMPtr<IWebInspector> inspector; 920 if (SUCCEEDED(webViewPrivate->inspector(&inspector))) 921 inspector->setJavaScriptProfilingEnabled(FALSE); 922 923 HWND viewWindow; 924 if (SUCCEEDED(webViewPrivate->viewWindow(reinterpret_cast<OLE_HANDLE*>(&viewWindow))) && viewWindow) 925 SetFocus(viewWindow); 926 927 webViewPrivate->clearMainFrameName(); 928 webViewPrivate->resetOriginAccessWhitelists(); 929 930 BSTR groupName; 931 if (SUCCEEDED(webView->groupName(&groupName))) { 932 webViewPrivate->removeAllUserContentFromGroup(groupName); 933 SysFreeString(groupName); 934 } 935 936 sharedUIDelegate->resetUndoManager(); 937 938 sharedFrameLoadDelegate->resetToConsistentState(); 939 940 COMPtr<IWebFramePrivate> framePrivate; 941 if (SUCCEEDED(frame->QueryInterface(&framePrivate))) 942 framePrivate->clearOpener(); 943 } 944 945 static void runTest(const string& testPathOrURL) 946 { 947 static BSTR methodBStr = SysAllocString(TEXT("GET")); 948 949 // Look for "'" as a separator between the path or URL, and the pixel dump hash that follows. 950 string pathOrURL(testPathOrURL); 951 string expectedPixelHash; 952 953 size_t separatorPos = pathOrURL.find("'"); 954 if (separatorPos != string::npos) { 955 pathOrURL = string(testPathOrURL, 0, separatorPos); 956 expectedPixelHash = string(testPathOrURL, separatorPos + 1); 957 } 958 959 BSTR urlBStr; 960 961 CFStringRef str = CFStringCreateWithCString(0, pathOrURL.c_str(), kCFStringEncodingWindowsLatin1); 962 CFURLRef url = CFURLCreateWithString(0, str, 0); 963 964 if (!url) 965 url = CFURLCreateWithFileSystemPath(0, str, kCFURLWindowsPathStyle, false); 966 967 CFRelease(str); 968 969 str = CFURLGetString(url); 970 971 CFIndex length = CFStringGetLength(str); 972 UniChar* buffer = new UniChar[length]; 973 974 CFStringGetCharacters(str, CFRangeMake(0, length), buffer); 975 urlBStr = SysAllocStringLen((OLECHAR*)buffer, length); 976 delete[] buffer; 977 978 CFRelease(url); 979 980 ::gLayoutTestController = LayoutTestController::create(pathOrURL, expectedPixelHash); 981 done = false; 982 topLoadingFrame = 0; 983 984 gLayoutTestController->setIconDatabaseEnabled(false); 985 986 if (shouldLogFrameLoadDelegates(pathOrURL.c_str())) 987 gLayoutTestController->setDumpFrameLoadCallbacks(true); 988 989 COMPtr<IWebView> webView; 990 if (SUCCEEDED(frame->webView(&webView))) { 991 COMPtr<IWebViewPrivate> viewPrivate; 992 if (SUCCEEDED(webView->QueryInterface(&viewPrivate))) { 993 if (shouldLogHistoryDelegates(pathOrURL.c_str())) { 994 gLayoutTestController->setDumpHistoryDelegateCallbacks(true); 995 viewPrivate->setHistoryDelegate(sharedHistoryDelegate.get()); 996 } else 997 viewPrivate->setHistoryDelegate(0); 998 } 999 } 1000 COMPtr<IWebHistory> history; 1001 if (SUCCEEDED(WebKitCreateInstance(CLSID_WebHistory, 0, __uuidof(history), reinterpret_cast<void**>(&history)))) 1002 history->setOptionalSharedHistory(0); 1003 1004 resetWebViewToConsistentStateBeforeTesting(); 1005 1006 if (shouldEnableDeveloperExtras(pathOrURL.c_str())) { 1007 gLayoutTestController->setDeveloperExtrasEnabled(true); 1008 if (shouldOpenWebInspector(pathOrURL.c_str())) 1009 gLayoutTestController->showWebInspector(); 1010 } 1011 if (shouldDumpAsText(pathOrURL.c_str())) { 1012 gLayoutTestController->setDumpAsText(true); 1013 gLayoutTestController->setGeneratePixelResults(false); 1014 } 1015 1016 prevTestBFItem = 0; 1017 if (webView) { 1018 COMPtr<IWebBackForwardList> bfList; 1019 if (SUCCEEDED(webView->backForwardList(&bfList))) 1020 bfList->currentItem(&prevTestBFItem); 1021 } 1022 1023 WorkQueue::shared()->clear(); 1024 WorkQueue::shared()->setFrozen(false); 1025 1026 HWND hostWindow; 1027 webView->hostWindow(reinterpret_cast<OLE_HANDLE*>(&hostWindow)); 1028 1029 COMPtr<IWebMutableURLRequest> request; 1030 HRESULT hr = WebKitCreateInstance(CLSID_WebMutableURLRequest, 0, IID_IWebMutableURLRequest, (void**)&request); 1031 if (FAILED(hr)) 1032 goto exit; 1033 1034 request->initWithURL(urlBStr, WebURLRequestUseProtocolCachePolicy, 60); 1035 1036 request->setHTTPMethod(methodBStr); 1037 frame->loadRequest(request.get()); 1038 1039 MSG msg; 1040 while (GetMessage(&msg, 0, 0, 0)) { 1041 // We get spurious WM_MOUSELEAVE events which make event handling machinery think that mouse button 1042 // is released during dragging (see e.g. fast\dynamic\layer-hit-test-crash.html). 1043 // Mouse can never leave WebView during normal DumpRenderTree operation, so we just ignore all such events. 1044 if (msg.message == WM_MOUSELEAVE) 1045 continue; 1046 TranslateMessage(&msg); 1047 DispatchMessage(&msg); 1048 } 1049 1050 if (shouldEnableDeveloperExtras(pathOrURL.c_str())) { 1051 gLayoutTestController->closeWebInspector(); 1052 gLayoutTestController->setDeveloperExtrasEnabled(false); 1053 } 1054 1055 resetWebViewToConsistentStateBeforeTesting(); 1056 1057 frame->stopLoading(); 1058 1059 if (::gLayoutTestController->closeRemainingWindowsWhenComplete()) { 1060 Vector<HWND> windows = openWindows(); 1061 unsigned size = windows.size(); 1062 for (unsigned i = 0; i < size; i++) { 1063 HWND window = windows[i]; 1064 1065 // Don't try to close the main window 1066 if (window == hostWindow) 1067 continue; 1068 1069 DestroyWindow(window); 1070 } 1071 } 1072 1073 exit: 1074 SysFreeString(urlBStr); 1075 ::gLayoutTestController.clear(); 1076 1077 return; 1078 } 1079 1080 static Boolean pthreadEqualCallback(const void* value1, const void* value2) 1081 { 1082 return (Boolean)pthread_equal(*(pthread_t*)value1, *(pthread_t*)value2); 1083 } 1084 1085 static CFDictionaryKeyCallBacks pthreadKeyCallbacks = { 0, 0, 0, 0, pthreadEqualCallback, 0 }; 1086 1087 static pthread_mutex_t javaScriptThreadsMutex = PTHREAD_MUTEX_INITIALIZER; 1088 static bool javaScriptThreadsShouldTerminate; 1089 1090 static const int javaScriptThreadsCount = 4; 1091 static CFMutableDictionaryRef javaScriptThreads() 1092 { 1093 assert(pthread_mutex_trylock(&javaScriptThreadsMutex) == EBUSY); 1094 static CFMutableDictionaryRef staticJavaScriptThreads; 1095 if (!staticJavaScriptThreads) 1096 staticJavaScriptThreads = CFDictionaryCreateMutable(0, 0, &pthreadKeyCallbacks, 0); 1097 return staticJavaScriptThreads; 1098 } 1099 1100 // Loops forever, running a script and randomly respawning, until 1101 // javaScriptThreadsShouldTerminate becomes true. 1102 void* runJavaScriptThread(void* arg) 1103 { 1104 const char* const script = 1105 " \ 1106 var array = []; \ 1107 for (var i = 0; i < 10; i++) { \ 1108 array.push(String(i)); \ 1109 } \ 1110 "; 1111 1112 while (true) { 1113 JSGlobalContextRef ctx = JSGlobalContextCreate(0); 1114 JSStringRef scriptRef = JSStringCreateWithUTF8CString(script); 1115 1116 JSValueRef exception = 0; 1117 JSEvaluateScript(ctx, scriptRef, 0, 0, 1, &exception); 1118 assert(!exception); 1119 1120 JSGlobalContextRelease(ctx); 1121 JSStringRelease(scriptRef); 1122 1123 JSGarbageCollect(ctx); 1124 1125 pthread_mutex_lock(&javaScriptThreadsMutex); 1126 1127 // Check for cancellation. 1128 if (javaScriptThreadsShouldTerminate) { 1129 pthread_mutex_unlock(&javaScriptThreadsMutex); 1130 return 0; 1131 } 1132 1133 // Respawn probabilistically. 1134 if (rand() % 5 == 0) { 1135 pthread_t pthread; 1136 pthread_create(&pthread, 0, &runJavaScriptThread, 0); 1137 pthread_detach(pthread); 1138 1139 pthread_t self = pthread_self(); 1140 CFDictionaryRemoveValue(javaScriptThreads(), self.p); 1141 CFDictionaryAddValue(javaScriptThreads(), pthread.p, 0); 1142 1143 pthread_mutex_unlock(&javaScriptThreadsMutex); 1144 return 0; 1145 } 1146 1147 pthread_mutex_unlock(&javaScriptThreadsMutex); 1148 } 1149 } 1150 1151 static void startJavaScriptThreads(void) 1152 { 1153 pthread_mutex_lock(&javaScriptThreadsMutex); 1154 1155 for (int i = 0; i < javaScriptThreadsCount; i++) { 1156 pthread_t pthread; 1157 pthread_create(&pthread, 0, &runJavaScriptThread, 0); 1158 pthread_detach(pthread); 1159 CFDictionaryAddValue(javaScriptThreads(), pthread.p, 0); 1160 } 1161 1162 pthread_mutex_unlock(&javaScriptThreadsMutex); 1163 } 1164 1165 static void stopJavaScriptThreads(void) 1166 { 1167 pthread_mutex_lock(&javaScriptThreadsMutex); 1168 1169 javaScriptThreadsShouldTerminate = true; 1170 1171 pthread_t* pthreads[javaScriptThreadsCount] = {0}; 1172 int threadDictCount = CFDictionaryGetCount(javaScriptThreads()); 1173 assert(threadDictCount == javaScriptThreadsCount); 1174 CFDictionaryGetKeysAndValues(javaScriptThreads(), (const void**)pthreads, 0); 1175 1176 pthread_mutex_unlock(&javaScriptThreadsMutex); 1177 1178 for (int i = 0; i < javaScriptThreadsCount; i++) { 1179 pthread_t* pthread = pthreads[i]; 1180 pthread_join(*pthread, 0); 1181 free(pthread); 1182 } 1183 } 1184 1185 Vector<HWND>& openWindows() 1186 { 1187 static Vector<HWND> vector; 1188 return vector; 1189 } 1190 1191 WindowToWebViewMap& windowToWebViewMap() 1192 { 1193 static WindowToWebViewMap map; 1194 return map; 1195 } 1196 1197 IWebView* createWebViewAndOffscreenWindow(HWND* webViewWindow) 1198 { 1199 unsigned maxViewWidth = LayoutTestController::maxViewWidth; 1200 unsigned maxViewHeight = LayoutTestController::maxViewHeight; 1201 HWND hostWindow = CreateWindowEx(WS_EX_TOOLWINDOW, kDumpRenderTreeClassName, TEXT("DumpRenderTree"), WS_POPUP, 1202 -maxViewWidth, -maxViewHeight, maxViewWidth, maxViewHeight, 0, 0, GetModuleHandle(0), 0); 1203 1204 IWebView* webView; 1205 1206 HRESULT hr = WebKitCreateInstance(CLSID_WebView, 0, IID_IWebView, (void**)&webView); 1207 if (FAILED(hr)) { 1208 fprintf(stderr, "Failed to create CLSID_WebView instance, error 0x%x\n", hr); 1209 return 0; 1210 } 1211 1212 if (FAILED(webView->setHostWindow((OLE_HANDLE)(ULONG64)hostWindow))) 1213 return 0; 1214 1215 RECT clientRect; 1216 clientRect.bottom = clientRect.left = clientRect.top = clientRect.right = 0; 1217 BSTR groupName = SysAllocString(L"org.webkit.DumpRenderTree"); 1218 bool failed = FAILED(webView->initWithFrame(clientRect, 0, groupName)); 1219 SysFreeString(groupName); 1220 if (failed) 1221 return 0; 1222 1223 COMPtr<IWebViewPrivate> viewPrivate; 1224 if (FAILED(webView->QueryInterface(&viewPrivate))) 1225 return 0; 1226 1227 viewPrivate->setShouldApplyMacFontAscentHack(TRUE); 1228 viewPrivate->setAlwaysUsesComplexTextCodePath(forceComplexText); 1229 1230 BSTR pluginPath = SysAllocStringLen(0, exePath().length() + _tcslen(TestPluginDir)); 1231 _tcscpy(pluginPath, exePath().c_str()); 1232 _tcscat(pluginPath, TestPluginDir); 1233 failed = FAILED(viewPrivate->addAdditionalPluginDirectory(pluginPath)); 1234 SysFreeString(pluginPath); 1235 if (failed) 1236 return 0; 1237 1238 HWND viewWindow; 1239 if (FAILED(viewPrivate->viewWindow(reinterpret_cast<OLE_HANDLE*>(&viewWindow)))) 1240 return 0; 1241 if (webViewWindow) 1242 *webViewWindow = viewWindow; 1243 1244 SetWindowPos(viewWindow, 0, 0, 0, maxViewWidth, maxViewHeight, 0); 1245 ShowWindow(hostWindow, SW_SHOW); 1246 1247 if (FAILED(webView->setFrameLoadDelegate(sharedFrameLoadDelegate.get()))) 1248 return 0; 1249 1250 if (FAILED(viewPrivate->setFrameLoadDelegatePrivate(sharedFrameLoadDelegate.get()))) 1251 return 0; 1252 1253 if (FAILED(webView->setUIDelegate(sharedUIDelegate.get()))) 1254 return 0; 1255 1256 COMPtr<IWebViewEditing> viewEditing; 1257 if (FAILED(webView->QueryInterface(&viewEditing))) 1258 return 0; 1259 1260 if (FAILED(viewEditing->setEditingDelegate(sharedEditingDelegate.get()))) 1261 return 0; 1262 1263 ResourceLoadDelegate* resourceLoadDelegate = new ResourceLoadDelegate(); 1264 HRESULT result = webView->setResourceLoadDelegate(resourceLoadDelegate); 1265 resourceLoadDelegate->Release(); // The delegate is owned by the WebView, so release our reference to it. 1266 if (FAILED(result)) 1267 return 0; 1268 1269 openWindows().append(hostWindow); 1270 windowToWebViewMap().set(hostWindow, webView); 1271 return webView; 1272 } 1273 1274 #if USE(CFNETWORK) 1275 RetainPtr<CFURLCacheRef> sharedCFURLCache() 1276 { 1277 #ifndef DEBUG_ALL 1278 HMODULE module = GetModuleHandle(TEXT("CFNetwork.dll")); 1279 #else 1280 HMODULE module = GetModuleHandle(TEXT("CFNetwork_debug.dll")); 1281 #endif 1282 if (!module) 1283 return 0; 1284 1285 typedef CFURLCacheRef (*CFURLCacheCopySharedURLCacheProcPtr)(void); 1286 if (CFURLCacheCopySharedURLCacheProcPtr copyCache = reinterpret_cast<CFURLCacheCopySharedURLCacheProcPtr>(GetProcAddress(module, "CFURLCacheCopySharedURLCache"))) 1287 return RetainPtr<CFURLCacheRef>(AdoptCF, copyCache()); 1288 1289 typedef CFURLCacheRef (*CFURLCacheSharedURLCacheProcPtr)(void); 1290 if (CFURLCacheSharedURLCacheProcPtr sharedCache = reinterpret_cast<CFURLCacheSharedURLCacheProcPtr>(GetProcAddress(module, "CFURLCacheSharedURLCache"))) 1291 return sharedCache(); 1292 1293 return 0; 1294 } 1295 #endif 1296 1297 static LONG WINAPI exceptionFilter(EXCEPTION_POINTERS*) 1298 { 1299 fputs("#CRASHED\n", stderr); 1300 fflush(stderr); 1301 return EXCEPTION_CONTINUE_SEARCH; 1302 } 1303 1304 int main(int argc, char* argv[]) 1305 { 1306 // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for 1307 // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the 1308 // error mode here to work around Cygwin's behavior. See <http://webkit.org/b/55222>. 1309 ::SetErrorMode(0); 1310 1311 ::SetUnhandledExceptionFilter(exceptionFilter); 1312 1313 leakChecking = false; 1314 1315 _setmode(1, _O_BINARY); 1316 _setmode(2, _O_BINARY); 1317 1318 initialize(); 1319 1320 Vector<const char*> tests; 1321 1322 for (int i = 1; i < argc; ++i) { 1323 if (!stricmp(argv[i], "--threaded")) { 1324 threaded = true; 1325 continue; 1326 } 1327 1328 if (!stricmp(argv[i], "--dump-all-pixels")) { 1329 dumpAllPixels = true; 1330 continue; 1331 } 1332 1333 if (!stricmp(argv[i], "--pixel-tests")) { 1334 dumpPixels = true; 1335 continue; 1336 } 1337 1338 if (!stricmp(argv[i], "--complex-text")) { 1339 forceComplexText = true; 1340 continue; 1341 } 1342 1343 if (!stricmp(argv[i], "--print-supported-features")) { 1344 printSupportedFeatures = true; 1345 continue; 1346 } 1347 1348 tests.append(argv[i]); 1349 } 1350 1351 policyDelegate = new PolicyDelegate(); 1352 sharedFrameLoadDelegate.adoptRef(new FrameLoadDelegate); 1353 sharedUIDelegate.adoptRef(new UIDelegate); 1354 sharedEditingDelegate.adoptRef(new EditingDelegate); 1355 sharedHistoryDelegate.adoptRef(new HistoryDelegate); 1356 1357 // FIXME - need to make DRT pass with Windows native controls <http://bugs.webkit.org/show_bug.cgi?id=25592> 1358 COMPtr<IWebPreferences> tmpPreferences; 1359 if (FAILED(WebKitCreateInstance(CLSID_WebPreferences, 0, IID_IWebPreferences, reinterpret_cast<void**>(&tmpPreferences)))) 1360 return -1; 1361 COMPtr<IWebPreferences> standardPreferences; 1362 if (FAILED(tmpPreferences->standardPreferences(&standardPreferences))) 1363 return -1; 1364 COMPtr<IWebPreferencesPrivate> standardPreferencesPrivate; 1365 if (FAILED(standardPreferences->QueryInterface(&standardPreferencesPrivate))) 1366 return -1; 1367 standardPreferencesPrivate->setShouldPaintNativeControls(FALSE); 1368 standardPreferences->setJavaScriptEnabled(TRUE); 1369 standardPreferences->setDefaultFontSize(16); 1370 standardPreferences->setAcceleratedCompositingEnabled(true); 1371 standardPreferences->setContinuousSpellCheckingEnabled(TRUE); 1372 1373 if (printSupportedFeatures) { 1374 BOOL acceleratedCompositingAvailable; 1375 standardPreferences->acceleratedCompositingEnabled(&acceleratedCompositingAvailable); 1376 1377 #if ENABLE(3D_RENDERING) 1378 // In theory, we could have a software-based 3D rendering implementation that we use when 1379 // hardware-acceleration is not available. But we don't have any such software 1380 // implementation, so 3D rendering is only available when hardware-acceleration is. 1381 BOOL threeDRenderingAvailable = acceleratedCompositingAvailable; 1382 #else 1383 BOOL threeDRenderingAvailable = FALSE; 1384 #endif 1385 1386 printf("SupportedFeatures:%s %s\n", acceleratedCompositingAvailable ? "AcceleratedCompositing" : "", threeDRenderingAvailable ? "3DRendering" : ""); 1387 return 0; 1388 } 1389 1390 COMPtr<IWebView> webView(AdoptCOM, createWebViewAndOffscreenWindow(&webViewWindow)); 1391 if (!webView) 1392 return -1; 1393 1394 COMPtr<IWebIconDatabase> iconDatabase; 1395 COMPtr<IWebIconDatabase> tmpIconDatabase; 1396 if (FAILED(WebKitCreateInstance(CLSID_WebIconDatabase, 0, IID_IWebIconDatabase, (void**)&tmpIconDatabase))) 1397 return -1; 1398 if (FAILED(tmpIconDatabase->sharedIconDatabase(&iconDatabase))) 1399 return -1; 1400 1401 if (FAILED(webView->mainFrame(&frame))) 1402 return -1; 1403 1404 #if USE(CFNETWORK) 1405 RetainPtr<CFURLCacheRef> urlCache = sharedCFURLCache(); 1406 CFURLCacheRemoveAllCachedResponses(urlCache.get()); 1407 #endif 1408 1409 #ifdef _DEBUG 1410 _CrtMemState entryToMainMemCheckpoint; 1411 if (leakChecking) 1412 _CrtMemCheckpoint(&entryToMainMemCheckpoint); 1413 #endif 1414 1415 if (threaded) 1416 startJavaScriptThreads(); 1417 1418 if (tests.size() == 1 && !strcmp(tests[0], "-")) { 1419 char filenameBuffer[2048]; 1420 printSeparators = true; 1421 while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) { 1422 char* newLineCharacter = strchr(filenameBuffer, '\n'); 1423 if (newLineCharacter) 1424 *newLineCharacter = '\0'; 1425 1426 if (strlen(filenameBuffer) == 0) 1427 continue; 1428 1429 runTest(filenameBuffer); 1430 } 1431 } else { 1432 printSeparators = tests.size() > 1; 1433 for (int i = 0; i < tests.size(); i++) 1434 runTest(tests[i]); 1435 } 1436 1437 if (threaded) 1438 stopJavaScriptThreads(); 1439 1440 delete policyDelegate; 1441 frame->Release(); 1442 1443 #ifdef _DEBUG 1444 if (leakChecking) { 1445 // dump leaks to stderr 1446 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); 1447 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); 1448 _CrtMemDumpAllObjectsSince(&entryToMainMemCheckpoint); 1449 } 1450 #endif 1451 1452 shutDownWebKit(); 1453 1454 return 0; 1455 } 1456