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