|
1 /* |
|
2 * Copyright (C) 2005, 2006, 2007 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 "DumpRenderTree.h" |
|
30 |
|
31 #include "EditingDelegate.h" |
|
32 #include "FrameLoaderDelegate.h" |
|
33 #include "LayoutTestController.h" |
|
34 #include "UIDelegate.h" |
|
35 #include "WorkQueueItem.h" |
|
36 #include "WorkQueue.h" |
|
37 #include <wtf/Vector.h> |
|
38 #include <WebCore/COMPtr.h> |
|
39 #include <CoreFoundation/CoreFoundation.h> |
|
40 #include <JavaScriptCore/JavaScriptCore.h> |
|
41 #include <math.h> |
|
42 #include <pthread.h> |
|
43 #include <string> |
|
44 #include <tchar.h> |
|
45 #include <WebKit/DOMPrivate.h> |
|
46 #include <WebKit/IWebFramePrivate.h> |
|
47 #include <WebKit/IWebHistoryItem.h> |
|
48 #include <WebKit/IWebHistoryItemPrivate.h> |
|
49 #include <WebKit/IWebURLResponse.h> |
|
50 #include <WebKit/IWebViewPrivate.h> |
|
51 #include <WebKit/WebKit.h> |
|
52 #include <windows.h> |
|
53 #include <stdio.h> |
|
54 |
|
55 using std::wstring; |
|
56 |
|
57 #ifdef _DEBUG |
|
58 const LPWSTR TestPluginDir = L"TestNetscapePlugin_Debug"; |
|
59 #else |
|
60 const LPWSTR TestPluginDir = L"TestNetscapePlugin"; |
|
61 #endif |
|
62 |
|
63 #define USE_MAC_FONTS |
|
64 |
|
65 const LPCWSTR kDumpRenderTreeClassName = L"DumpRenderTreeWindow"; |
|
66 |
|
67 static bool dumpTree = true; |
|
68 static bool printSeparators; |
|
69 static bool leakChecking = false; |
|
70 static bool timedOut = false; |
|
71 static bool threaded = false; |
|
72 |
|
73 static const char* currentTest; |
|
74 |
|
75 volatile bool done; |
|
76 // This is the topmost frame that is loading, during a given load, or nil when no load is |
|
77 // in progress. Usually this is the same as the main frame, but not always. In the case |
|
78 // where a frameset is loaded, and then new content is loaded into one of the child frames, |
|
79 // that child frame is the "topmost frame that is loading". |
|
80 IWebFrame* topLoadingFrame; // !nil iff a load is in progress |
|
81 static COMPtr<IWebHistoryItem> prevTestBFItem; // current b/f item at the end of the previous test |
|
82 |
|
83 IWebFrame* frame; |
|
84 HWND webViewWindow; |
|
85 static HWND hostWindow; |
|
86 |
|
87 LayoutTestController* layoutTestController = 0; |
|
88 CFRunLoopTimerRef waitToDumpWatchdog = 0; |
|
89 |
|
90 static const unsigned timeoutValue = 60000; |
|
91 static const unsigned timeoutId = 10; |
|
92 |
|
93 const unsigned maxViewWidth = 800; |
|
94 const unsigned maxViewHeight = 600; |
|
95 |
|
96 static LRESULT CALLBACK DumpRenderTreeWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) |
|
97 { |
|
98 switch (msg) { |
|
99 case WM_TIMER: |
|
100 // The test ran long enough to time out |
|
101 timedOut = true; |
|
102 PostQuitMessage(0); |
|
103 return 0; |
|
104 break; |
|
105 default: |
|
106 return DefWindowProc(hWnd, msg, wParam, lParam); |
|
107 } |
|
108 } |
|
109 |
|
110 extern "C" BOOL InitializeCoreGraphics(); |
|
111 |
|
112 #ifdef DEBUG_WEBKIT_HAS_SUFFIX |
|
113 #define WEBKITDLL TEXT("WebKit_debug.dll") |
|
114 #else |
|
115 #define WEBKITDLL TEXT("WebKit.dll") |
|
116 #endif |
|
117 |
|
118 static wstring initialize(HMODULE hModule) |
|
119 { |
|
120 if (HMODULE webKitModule = LoadLibrary(WEBKITDLL)) |
|
121 if (FARPROC dllRegisterServer = GetProcAddress(webKitModule, "DllRegisterServer")) |
|
122 dllRegisterServer(); |
|
123 |
|
124 static LPCTSTR fontsToInstall[] = { |
|
125 TEXT("AHEM____.ttf"), |
|
126 TEXT("Apple Chancery.ttf"), |
|
127 TEXT("Courier Bold.ttf"), |
|
128 TEXT("Courier.ttf"), |
|
129 TEXT("Helvetica Bold.ttf"), |
|
130 TEXT("Helvetica.ttf"), |
|
131 TEXT("Helvetica Neue Bold Italic.ttf"), |
|
132 TEXT("Helvetica Neue Bold.ttf"), |
|
133 TEXT("Helvetica Neue Condensed Black.ttf"), |
|
134 TEXT("Helvetica Neue Condensed Bold.ttf"), |
|
135 TEXT("Helvetica Neue Italic.ttf"), |
|
136 TEXT("Helvetica Neue Light Italic.ttf"), |
|
137 TEXT("Helvetica Neue Light.ttf"), |
|
138 TEXT("Helvetica Neue UltraLight Italic.ttf"), |
|
139 TEXT("Helvetica Neue UltraLight.ttf"), |
|
140 TEXT("Helvetica Neue.ttf"), |
|
141 TEXT("Lucida Grande.ttf"), |
|
142 TEXT("Lucida Grande Bold.ttf"), |
|
143 TEXT("Monaco.ttf"), |
|
144 TEXT("Papyrus.ttf"), |
|
145 TEXT("Times Bold Italic.ttf"), |
|
146 TEXT("Times Bold.ttf"), |
|
147 TEXT("Times Italic.ttf"), |
|
148 TEXT("Times Roman.ttf") |
|
149 }; |
|
150 |
|
151 TCHAR buffer[MAX_PATH]; |
|
152 GetModuleFileName(hModule, buffer, ARRAYSIZE(buffer)); |
|
153 wstring exePath(buffer); |
|
154 int lastSlash = exePath.rfind('\\'); |
|
155 if (lastSlash != -1 && lastSlash + 1< exePath.length()) |
|
156 exePath = exePath.substr(0, lastSlash + 1); |
|
157 |
|
158 wstring resourcesPath(exePath + TEXT("DumpRenderTree.resources\\")); |
|
159 |
|
160 for (int i = 0; i < ARRAYSIZE(fontsToInstall); ++i) |
|
161 AddFontResourceEx(wstring(resourcesPath + fontsToInstall[i]).c_str(), FR_PRIVATE, 0); |
|
162 |
|
163 // Init COM |
|
164 OleInitialize(0); |
|
165 |
|
166 // Initialize CG |
|
167 InitializeCoreGraphics(); |
|
168 |
|
169 // Register a host window |
|
170 WNDCLASSEX wcex; |
|
171 |
|
172 wcex.cbSize = sizeof(WNDCLASSEX); |
|
173 |
|
174 wcex.style = CS_HREDRAW | CS_VREDRAW; |
|
175 wcex.lpfnWndProc = DumpRenderTreeWndProc; |
|
176 wcex.cbClsExtra = 0; |
|
177 wcex.cbWndExtra = 0; |
|
178 wcex.hInstance = hModule; |
|
179 wcex.hIcon = 0; |
|
180 wcex.hCursor = LoadCursor(0, IDC_ARROW); |
|
181 wcex.hbrBackground = 0; |
|
182 wcex.lpszMenuName = 0; |
|
183 wcex.lpszClassName = kDumpRenderTreeClassName; |
|
184 wcex.hIconSm = 0; |
|
185 |
|
186 RegisterClassEx(&wcex); |
|
187 |
|
188 hostWindow = CreateWindowEx(WS_EX_TOOLWINDOW, kDumpRenderTreeClassName, TEXT("DumpRenderTree"), WS_POPUP, |
|
189 -maxViewWidth, -maxViewHeight, maxViewWidth, maxViewHeight, 0, 0, hModule, 0); |
|
190 |
|
191 return exePath; |
|
192 } |
|
193 |
|
194 void displayWebView() |
|
195 { |
|
196 ::InvalidateRect(webViewWindow, 0, TRUE); |
|
197 ::UpdateWindow(webViewWindow); |
|
198 } |
|
199 |
|
200 void dumpFrameScrollPosition(IWebFrame* frame) |
|
201 { |
|
202 if (!frame) |
|
203 return; |
|
204 |
|
205 COMPtr<IWebFramePrivate> framePrivate; |
|
206 if (FAILED(frame->QueryInterface(&framePrivate))) |
|
207 return; |
|
208 |
|
209 SIZE scrollPosition; |
|
210 if (FAILED(framePrivate->scrollOffset(&scrollPosition))) |
|
211 return; |
|
212 |
|
213 if (abs(scrollPosition.cx) > 0.00000001 || abs(scrollPosition.cy) > 0.00000001) { |
|
214 COMPtr<IWebFrame> parent; |
|
215 if (FAILED(frame->parentFrame(&parent))) |
|
216 return; |
|
217 if (parent) { |
|
218 BSTR name; |
|
219 if (FAILED(frame->name(&name))) |
|
220 return; |
|
221 printf("frame '%S' ", name ? name : L""); |
|
222 SysFreeString(name); |
|
223 } |
|
224 printf("scrolled to %.f,%.f\n", (double)scrollPosition.cx, (double)scrollPosition.cy); |
|
225 } |
|
226 |
|
227 if (::layoutTestController->dumpChildFrameScrollPositions()) { |
|
228 COMPtr<IEnumVARIANT> enumKids; |
|
229 if (FAILED(frame->childFrames(&enumKids))) |
|
230 return; |
|
231 VARIANT var; |
|
232 VariantInit(&var); |
|
233 while (enumKids->Next(1, &var, 0) == S_OK) { |
|
234 ASSERT(V_VT(&var) == VT_UNKNOWN); |
|
235 COMPtr<IWebFrame> framePtr; |
|
236 V_UNKNOWN(&var)->QueryInterface(IID_IWebFrame, (void**)&framePtr); |
|
237 dumpFrameScrollPosition(framePtr.get()); |
|
238 VariantClear(&var); |
|
239 } |
|
240 } |
|
241 } |
|
242 |
|
243 static wstring dumpFramesAsText(IWebFrame* frame) |
|
244 { |
|
245 if (!frame) |
|
246 return L""; |
|
247 |
|
248 COMPtr<IDOMDocument> document; |
|
249 if (FAILED(frame->DOMDocument(&document))) |
|
250 return L""; |
|
251 |
|
252 COMPtr<IDOMElement> documentElement; |
|
253 if (FAILED(document->documentElement(&documentElement))) |
|
254 return L""; |
|
255 |
|
256 wstring result; |
|
257 |
|
258 // Add header for all but the main frame. |
|
259 COMPtr<IWebFrame> parent; |
|
260 if (FAILED(frame->parentFrame(&parent))) |
|
261 return L""; |
|
262 if (parent) { |
|
263 BSTR name = L""; |
|
264 if (FAILED(frame->name(&name))) |
|
265 return L""; |
|
266 |
|
267 result.append(L"\n--------\nFrame: '"); |
|
268 result.append(name ? name : L""); |
|
269 result.append(L"'\n--------\n"); |
|
270 |
|
271 SysFreeString(name); |
|
272 } |
|
273 |
|
274 BSTR innerText = 0; |
|
275 COMPtr<IDOMElementPrivate> docPrivate; |
|
276 if (SUCCEEDED(documentElement->QueryInterface(&docPrivate))) |
|
277 docPrivate->innerText(&innerText); |
|
278 |
|
279 result.append(innerText ? innerText : L""); |
|
280 result.append(L"\n"); |
|
281 |
|
282 SysFreeString(innerText); |
|
283 |
|
284 if (::layoutTestController->dumpChildFramesAsText()) { |
|
285 COMPtr<IEnumVARIANT> enumKids; |
|
286 if (FAILED(frame->childFrames(&enumKids))) |
|
287 return L""; |
|
288 VARIANT var; |
|
289 VariantInit(&var); |
|
290 while (enumKids->Next(1, &var, 0) == S_OK) { |
|
291 ASSERT(V_VT(&var) == VT_UNKNOWN); |
|
292 COMPtr<IWebFrame> framePtr; |
|
293 V_UNKNOWN(&var)->QueryInterface(IID_IWebFrame, (void**)&framePtr); |
|
294 result.append(dumpFramesAsText(framePtr.get())); |
|
295 VariantClear(&var); |
|
296 } |
|
297 } |
|
298 |
|
299 return result; |
|
300 } |
|
301 |
|
302 static int compareHistoryItems(const void* item1, const void* item2) |
|
303 { |
|
304 COMPtr<IWebHistoryItemPrivate> itemA; |
|
305 if (FAILED((*(COMPtr<IUnknown>*)item1)->QueryInterface(&itemA))) |
|
306 return 0; |
|
307 |
|
308 COMPtr<IWebHistoryItemPrivate> itemB; |
|
309 if (FAILED((*(COMPtr<IUnknown>*)item2)->QueryInterface(&itemB))) |
|
310 return 0; |
|
311 |
|
312 BSTR targetA; |
|
313 if (FAILED(itemA->target(&targetA))) |
|
314 return 0; |
|
315 |
|
316 BSTR targetB; |
|
317 if (FAILED(itemB->target(&targetB))) { |
|
318 SysFreeString(targetA); |
|
319 return 0; |
|
320 } |
|
321 |
|
322 int result = wcsicmp(wstring(targetA, SysStringLen(targetA)).c_str(), wstring(targetB, SysStringLen(targetB)).c_str()); |
|
323 SysFreeString(targetA); |
|
324 SysFreeString(targetB); |
|
325 return result; |
|
326 } |
|
327 |
|
328 static void dumpHistoryItem(IWebHistoryItem* item, int indent, bool current) |
|
329 { |
|
330 assert(item); |
|
331 |
|
332 int start = 0; |
|
333 if (current) { |
|
334 printf("curr->"); |
|
335 start = 6; |
|
336 } |
|
337 for (int i = start; i < indent; i++) |
|
338 putchar(' '); |
|
339 |
|
340 BSTR url; |
|
341 if (FAILED(item->URLString(&url))) |
|
342 return; |
|
343 printf("%S", url ? url : L""); |
|
344 SysFreeString(url); |
|
345 |
|
346 COMPtr<IWebHistoryItemPrivate> itemPrivate; |
|
347 if (FAILED(item->QueryInterface(&itemPrivate))) |
|
348 return; |
|
349 |
|
350 BSTR target; |
|
351 if (FAILED(itemPrivate->target(&target))) |
|
352 return; |
|
353 if (SysStringLen(target)) |
|
354 printf(" (in frame \"%S\")", target); |
|
355 SysFreeString(target); |
|
356 BOOL isTargetItem = FALSE; |
|
357 if (FAILED(itemPrivate->isTargetItem(&isTargetItem))) |
|
358 return; |
|
359 if (isTargetItem) |
|
360 printf(" **nav target**"); |
|
361 putchar('\n'); |
|
362 |
|
363 unsigned kidsCount; |
|
364 SAFEARRAY* arrPtr; |
|
365 if (FAILED(itemPrivate->children(&kidsCount, &arrPtr)) || !kidsCount) |
|
366 return; |
|
367 |
|
368 Vector<COMPtr<IUnknown> > kidsVector; |
|
369 |
|
370 LONG lowerBound; |
|
371 if (FAILED(::SafeArrayGetLBound(arrPtr, 1, &lowerBound))) |
|
372 goto exit; |
|
373 |
|
374 LONG upperBound; |
|
375 if (FAILED(::SafeArrayGetUBound(arrPtr, 1, &upperBound))) |
|
376 goto exit; |
|
377 |
|
378 LONG length = upperBound - lowerBound + 1; |
|
379 if (!length) |
|
380 goto exit; |
|
381 ASSERT(length == kidsCount); |
|
382 |
|
383 IUnknown** safeArrayData; |
|
384 if (FAILED(::SafeArrayAccessData(arrPtr, (void**)&safeArrayData))) |
|
385 goto exit; |
|
386 |
|
387 for (int i = 0; i < length; ++i) |
|
388 kidsVector.append(safeArrayData[i]); |
|
389 ::SafeArrayUnaccessData(arrPtr); |
|
390 |
|
391 // must sort to eliminate arbitrary result ordering which defeats reproducible testing |
|
392 qsort(kidsVector.data(), kidsCount, sizeof(kidsVector[0]), compareHistoryItems); |
|
393 |
|
394 for (unsigned i = 0; i < kidsCount; ++i) { |
|
395 COMPtr<IWebHistoryItem> item; |
|
396 kidsVector[i]->QueryInterface(&item); |
|
397 dumpHistoryItem(item.get(), indent + 4, false); |
|
398 } |
|
399 |
|
400 exit: |
|
401 if (arrPtr && SUCCEEDED(::SafeArrayUnlock(arrPtr))) |
|
402 ::SafeArrayDestroy(arrPtr); |
|
403 } |
|
404 |
|
405 static void dumpBackForwardList(IWebFrame* frame) |
|
406 { |
|
407 assert(frame); |
|
408 |
|
409 printf("\n============== Back Forward List ==============\n"); |
|
410 COMPtr<IWebView> webView; |
|
411 if (FAILED(frame->webView(&webView))) |
|
412 return; |
|
413 |
|
414 COMPtr<IWebBackForwardList> bfList; |
|
415 if (FAILED(webView->backForwardList(&bfList))) |
|
416 return; |
|
417 |
|
418 // Print out all items in the list after prevTestBFItem, which was from the previous test |
|
419 // Gather items from the end of the list, the print them out from oldest to newest |
|
420 |
|
421 Vector<COMPtr<IUnknown> > itemsToPrint; |
|
422 |
|
423 int forwardListCount; |
|
424 if (FAILED(bfList->forwardListCount(&forwardListCount))) |
|
425 return; |
|
426 |
|
427 for (int i = forwardListCount; i > 0; --i) { |
|
428 COMPtr<IWebHistoryItem> item; |
|
429 if (FAILED(bfList->itemAtIndex(i, &item))) |
|
430 return; |
|
431 // something is wrong if the item from the last test is in the forward part of the b/f list |
|
432 assert(item != prevTestBFItem); |
|
433 COMPtr<IUnknown> itemUnknown; |
|
434 item->QueryInterface(&itemUnknown); |
|
435 itemsToPrint.append(itemUnknown); |
|
436 } |
|
437 |
|
438 COMPtr<IWebHistoryItem> currentItem; |
|
439 if (FAILED(bfList->currentItem(¤tItem))) |
|
440 return; |
|
441 |
|
442 assert(currentItem != prevTestBFItem); |
|
443 COMPtr<IUnknown> currentItemUnknown; |
|
444 currentItem->QueryInterface(¤tItemUnknown); |
|
445 itemsToPrint.append(currentItemUnknown); |
|
446 int currentItemIndex = itemsToPrint.size() - 1; |
|
447 |
|
448 int backListCount; |
|
449 if (FAILED(bfList->backListCount(&backListCount))) |
|
450 return; |
|
451 |
|
452 for (int i = -1; i >= -backListCount; --i) { |
|
453 COMPtr<IWebHistoryItem> item; |
|
454 if (FAILED(bfList->itemAtIndex(i, &item))) |
|
455 return; |
|
456 if (item == prevTestBFItem) |
|
457 break; |
|
458 COMPtr<IUnknown> itemUnknown; |
|
459 item->QueryInterface(&itemUnknown); |
|
460 itemsToPrint.append(itemUnknown); |
|
461 } |
|
462 |
|
463 for (int i = itemsToPrint.size() - 1; i >= 0; --i) { |
|
464 COMPtr<IWebHistoryItem> historyItemToPrint; |
|
465 itemsToPrint[i]->QueryInterface(&historyItemToPrint); |
|
466 dumpHistoryItem(historyItemToPrint.get(), 8, i == currentItemIndex); |
|
467 } |
|
468 |
|
469 printf("===============================================\n"); |
|
470 } |
|
471 |
|
472 void dump() |
|
473 { |
|
474 COMPtr<IWebDataSource> dataSource; |
|
475 if (SUCCEEDED(frame->dataSource(&dataSource))) { |
|
476 COMPtr<IWebURLResponse> response; |
|
477 if (SUCCEEDED(dataSource->response(&response)) && response) { |
|
478 BSTR mimeType; |
|
479 if (SUCCEEDED(response->MIMEType(&mimeType))) |
|
480 ::layoutTestController->setDumpAsText(::layoutTestController->dumpAsText() | !_tcscmp(mimeType, TEXT("text/plain"))); |
|
481 SysFreeString(mimeType); |
|
482 } |
|
483 } |
|
484 |
|
485 BSTR resultString = 0; |
|
486 |
|
487 if (dumpTree) { |
|
488 if (::layoutTestController->dumpAsText()) { |
|
489 ::InvalidateRect(webViewWindow, 0, TRUE); |
|
490 ::SendMessage(webViewWindow, WM_PAINT, 0, 0); |
|
491 wstring result = dumpFramesAsText(frame); |
|
492 resultString = SysAllocStringLen(result.data(), result.size()); |
|
493 } else { |
|
494 bool isSVGW3CTest = strstr(currentTest, "svg\\W3C-SVG-1.1"); |
|
495 unsigned width; |
|
496 unsigned height; |
|
497 if (isSVGW3CTest) { |
|
498 width = 480; |
|
499 height = 360; |
|
500 } else { |
|
501 width = maxViewWidth; |
|
502 height = maxViewHeight; |
|
503 } |
|
504 |
|
505 ::SetWindowPos(webViewWindow, 0, 0, 0, width, height, SWP_NOMOVE); |
|
506 ::InvalidateRect(webViewWindow, 0, TRUE); |
|
507 ::SendMessage(webViewWindow, WM_PAINT, 0, 0); |
|
508 |
|
509 COMPtr<IWebFramePrivate> framePrivate; |
|
510 if (FAILED(frame->QueryInterface(&framePrivate))) |
|
511 goto fail; |
|
512 framePrivate->renderTreeAsExternalRepresentation(&resultString); |
|
513 } |
|
514 |
|
515 if (!resultString) |
|
516 printf("ERROR: nil result from %s", ::layoutTestController->dumpAsText() ? "IDOMElement::innerText" : "IFrameViewPrivate::renderTreeAsExternalRepresentation"); |
|
517 else { |
|
518 unsigned stringLength = SysStringLen(resultString); |
|
519 int bufferSize = ::WideCharToMultiByte(CP_UTF8, 0, resultString, stringLength, 0, 0, 0, 0); |
|
520 char* buffer = (char*)malloc(bufferSize + 1); |
|
521 int result = ::WideCharToMultiByte(CP_UTF8, 0, resultString, stringLength, buffer, bufferSize + 1, 0, 0); |
|
522 buffer[bufferSize] = '\0'; |
|
523 printf("%s", buffer); |
|
524 free(buffer); |
|
525 if (!::layoutTestController->dumpAsText()) |
|
526 dumpFrameScrollPosition(frame); |
|
527 } |
|
528 if (::layoutTestController->dumpBackForwardList()) |
|
529 dumpBackForwardList(frame); |
|
530 } |
|
531 |
|
532 if (printSeparators) |
|
533 puts("#EOF"); |
|
534 fail: |
|
535 SysFreeString(resultString); |
|
536 // This will exit from our message loop |
|
537 PostQuitMessage(0); |
|
538 done = true; |
|
539 } |
|
540 |
|
541 static void runTest(const char* pathOrURL) |
|
542 { |
|
543 static BSTR methodBStr = SysAllocString(TEXT("GET")); |
|
544 |
|
545 BSTR urlBStr; |
|
546 |
|
547 CFStringRef str = CFStringCreateWithCString(0, pathOrURL, kCFStringEncodingWindowsLatin1); |
|
548 CFURLRef url = CFURLCreateWithString(0, str, 0); |
|
549 |
|
550 if (!url) |
|
551 url = CFURLCreateWithFileSystemPath(0, str, kCFURLWindowsPathStyle, false); |
|
552 |
|
553 CFRelease(str); |
|
554 |
|
555 str = CFURLGetString(url); |
|
556 |
|
557 CFIndex length = CFStringGetLength(str); |
|
558 UniChar* buffer = new UniChar[length]; |
|
559 |
|
560 CFStringGetCharacters(str, CFRangeMake(0, length), buffer); |
|
561 urlBStr = SysAllocStringLen((OLECHAR*)buffer, length); |
|
562 delete[] buffer; |
|
563 |
|
564 CFRelease(url); |
|
565 |
|
566 currentTest = pathOrURL; |
|
567 |
|
568 ::layoutTestController = new LayoutTestController(false, false); |
|
569 done = false; |
|
570 topLoadingFrame = 0; |
|
571 timedOut = false; |
|
572 |
|
573 prevTestBFItem = 0; |
|
574 COMPtr<IWebView> webView; |
|
575 if (SUCCEEDED(frame->webView(&webView))) { |
|
576 COMPtr<IWebBackForwardList> bfList; |
|
577 if (SUCCEEDED(webView->backForwardList(&bfList))) |
|
578 bfList->currentItem(&prevTestBFItem); |
|
579 |
|
580 COMPtr<IWebIBActions> webIBActions; |
|
581 if (SUCCEEDED(webView->QueryInterface(IID_IWebIBActions, (void**)&webIBActions))) |
|
582 webIBActions->makeTextStandardSize(0); |
|
583 } |
|
584 |
|
585 WorkQueue::shared()->clear(); |
|
586 WorkQueue::shared()->setFrozen(false); |
|
587 |
|
588 // Set the test timeout timer |
|
589 SetTimer(hostWindow, timeoutId, timeoutValue, 0); |
|
590 |
|
591 COMPtr<IWebMutableURLRequest> request; |
|
592 HRESULT hr = CoCreateInstance(CLSID_WebMutableURLRequest, 0, CLSCTX_ALL, IID_IWebMutableURLRequest, (void**)&request); |
|
593 if (FAILED(hr)) |
|
594 goto exit; |
|
595 |
|
596 request->initWithURL(urlBStr, WebURLRequestUseProtocolCachePolicy, 0); |
|
597 |
|
598 request->setHTTPMethod(methodBStr); |
|
599 frame->loadRequest(request.get()); |
|
600 |
|
601 MSG msg; |
|
602 while (GetMessage(&msg, 0, 0, 0)) { |
|
603 TranslateMessage(&msg); |
|
604 DispatchMessage(&msg); |
|
605 } |
|
606 KillTimer(hostWindow, timeoutId); |
|
607 |
|
608 if (timedOut) { |
|
609 fprintf(stderr, "ERROR: Timed out running %s\n", pathOrURL); |
|
610 printf("ERROR: Timed out loading page\n"); |
|
611 |
|
612 if (printSeparators) |
|
613 puts("#EOF"); |
|
614 } |
|
615 exit: |
|
616 SysFreeString(urlBStr); |
|
617 return; |
|
618 } |
|
619 |
|
620 static void initializePreferences(IWebPreferences* preferences) |
|
621 { |
|
622 #ifdef USE_MAC_FONTS |
|
623 BSTR standardFamily = SysAllocString(TEXT("Times")); |
|
624 BSTR fixedFamily = SysAllocString(TEXT("Courier")); |
|
625 BSTR sansSerifFamily = SysAllocString(TEXT("Helvetica")); |
|
626 BSTR cursiveFamily = SysAllocString(TEXT("Apple Chancery")); |
|
627 BSTR fantasyFamily = SysAllocString(TEXT("Papyrus")); |
|
628 #else |
|
629 BSTR standardFamily = SysAllocString(TEXT("Times New Roman")); |
|
630 BSTR fixedFamily = SysAllocString(TEXT("Courier New")); |
|
631 BSTR sansSerifFamily = SysAllocString(TEXT("Arial")); |
|
632 BSTR cursiveFamily = SysAllocString(TEXT("Comic Sans MS")); // Not actually cursive, but it's what IE and Firefox use. |
|
633 BSTR fantasyFamily = SysAllocString(TEXT("Times New Roman")); |
|
634 #endif |
|
635 |
|
636 preferences->setStandardFontFamily(standardFamily); |
|
637 preferences->setFixedFontFamily(fixedFamily); |
|
638 preferences->setSerifFontFamily(standardFamily); |
|
639 preferences->setSansSerifFontFamily(sansSerifFamily); |
|
640 preferences->setCursiveFontFamily(cursiveFamily); |
|
641 preferences->setFantasyFontFamily(fantasyFamily); |
|
642 |
|
643 preferences->setAutosaves(FALSE); |
|
644 preferences->setJavaEnabled(FALSE); |
|
645 preferences->setPlugInsEnabled(TRUE); |
|
646 preferences->setDOMPasteAllowed(TRUE); |
|
647 preferences->setEditableLinkBehavior(WebKitEditableLinkOnlyLiveWithShiftKey); |
|
648 |
|
649 SysFreeString(standardFamily); |
|
650 SysFreeString(fixedFamily); |
|
651 SysFreeString(sansSerifFamily); |
|
652 SysFreeString(cursiveFamily); |
|
653 SysFreeString(fantasyFamily); |
|
654 } |
|
655 |
|
656 static Boolean pthreadEqualCallback(const void* value1, const void* value2) |
|
657 { |
|
658 return (Boolean)pthread_equal(*(pthread_t*)value1, *(pthread_t*)value2); |
|
659 } |
|
660 |
|
661 static CFDictionaryKeyCallBacks pthreadKeyCallbacks = { 0, 0, 0, 0, pthreadEqualCallback, 0 }; |
|
662 |
|
663 static pthread_mutex_t javaScriptThreadsMutex = PTHREAD_MUTEX_INITIALIZER; |
|
664 static bool javaScriptThreadsShouldTerminate; |
|
665 |
|
666 static const int javaScriptThreadsCount = 4; |
|
667 static CFMutableDictionaryRef javaScriptThreads() |
|
668 { |
|
669 assert(pthread_mutex_trylock(&javaScriptThreadsMutex) == EBUSY); |
|
670 static CFMutableDictionaryRef staticJavaScriptThreads; |
|
671 if (!staticJavaScriptThreads) |
|
672 staticJavaScriptThreads = CFDictionaryCreateMutable(0, 0, &pthreadKeyCallbacks, 0); |
|
673 return staticJavaScriptThreads; |
|
674 } |
|
675 |
|
676 // Loops forever, running a script and randomly respawning, until |
|
677 // javaScriptThreadsShouldTerminate becomes true. |
|
678 void* runJavaScriptThread(void* arg) |
|
679 { |
|
680 const char* const script = |
|
681 " \ |
|
682 var array = []; \ |
|
683 for (var i = 0; i < 10; i++) { \ |
|
684 array.push(String(i)); \ |
|
685 } \ |
|
686 "; |
|
687 |
|
688 while (true) { |
|
689 JSGlobalContextRef ctx = JSGlobalContextCreate(0); |
|
690 JSStringRef scriptRef = JSStringCreateWithUTF8CString(script); |
|
691 |
|
692 JSValueRef exception = 0; |
|
693 JSEvaluateScript(ctx, scriptRef, 0, 0, 0, &exception); |
|
694 assert(!exception); |
|
695 |
|
696 JSGlobalContextRelease(ctx); |
|
697 JSStringRelease(scriptRef); |
|
698 |
|
699 JSGarbageCollect(ctx); |
|
700 |
|
701 pthread_mutex_lock(&javaScriptThreadsMutex); |
|
702 |
|
703 // Check for cancellation. |
|
704 if (javaScriptThreadsShouldTerminate) { |
|
705 pthread_mutex_unlock(&javaScriptThreadsMutex); |
|
706 return 0; |
|
707 } |
|
708 |
|
709 // Respawn probabilistically. |
|
710 if (rand() % 5 == 0) { |
|
711 pthread_t pthread; |
|
712 pthread_create(&pthread, 0, &runJavaScriptThread, 0); |
|
713 pthread_detach(pthread); |
|
714 |
|
715 pthread_t self = pthread_self(); |
|
716 CFDictionaryRemoveValue(javaScriptThreads(), self.p); |
|
717 CFDictionaryAddValue(javaScriptThreads(), pthread.p, 0); |
|
718 |
|
719 pthread_mutex_unlock(&javaScriptThreadsMutex); |
|
720 return 0; |
|
721 } |
|
722 |
|
723 pthread_mutex_unlock(&javaScriptThreadsMutex); |
|
724 } |
|
725 } |
|
726 |
|
727 static void startJavaScriptThreads(void) |
|
728 { |
|
729 pthread_mutex_lock(&javaScriptThreadsMutex); |
|
730 |
|
731 for (int i = 0; i < javaScriptThreadsCount; i++) { |
|
732 pthread_t pthread; |
|
733 pthread_create(&pthread, 0, &runJavaScriptThread, 0); |
|
734 pthread_detach(pthread); |
|
735 CFDictionaryAddValue(javaScriptThreads(), pthread.p, 0); |
|
736 } |
|
737 |
|
738 pthread_mutex_unlock(&javaScriptThreadsMutex); |
|
739 } |
|
740 |
|
741 static void stopJavaScriptThreads(void) |
|
742 { |
|
743 pthread_mutex_lock(&javaScriptThreadsMutex); |
|
744 |
|
745 javaScriptThreadsShouldTerminate = true; |
|
746 |
|
747 pthread_t* pthreads[javaScriptThreadsCount] = {0}; |
|
748 int threadDictCount = CFDictionaryGetCount(javaScriptThreads()); |
|
749 assert(threadDictCount == javaScriptThreadsCount); |
|
750 CFDictionaryGetKeysAndValues(javaScriptThreads(), (const void**)pthreads, 0); |
|
751 |
|
752 pthread_mutex_unlock(&javaScriptThreadsMutex); |
|
753 |
|
754 for (int i = 0; i < javaScriptThreadsCount; i++) { |
|
755 pthread_t* pthread = pthreads[i]; |
|
756 pthread_join(*pthread, 0); |
|
757 free(pthread); |
|
758 } |
|
759 } |
|
760 |
|
761 int main(int argc, char* argv[]) |
|
762 { |
|
763 leakChecking = false; |
|
764 |
|
765 wstring exePath = initialize(GetModuleHandle(0)); |
|
766 |
|
767 // FIXME: options |
|
768 |
|
769 COMPtr<IWebView> webView; |
|
770 HRESULT hr = CoCreateInstance(CLSID_WebView, 0, CLSCTX_ALL, IID_IWebView, (void**)&webView); |
|
771 if (FAILED(hr)) { |
|
772 fprintf(stderr, "Failed to create CLSID_WebView instance, error 0x%x\n", hr); |
|
773 return -1; |
|
774 } |
|
775 |
|
776 if (FAILED(webView->setHostWindow((OLE_HANDLE)(ULONG64)hostWindow))) |
|
777 return -1; |
|
778 |
|
779 RECT clientRect; |
|
780 clientRect.bottom = clientRect.left = clientRect.top = clientRect.right = 0; |
|
781 BSTR groupName = SysAllocString(L"org.webkit.DumpRenderTree"); |
|
782 bool failed = FAILED(webView->initWithFrame(clientRect, 0, groupName)); |
|
783 SysFreeString(groupName); |
|
784 if (failed) |
|
785 return -1; |
|
786 |
|
787 COMPtr<IWebViewPrivate> viewPrivate; |
|
788 if (FAILED(webView->QueryInterface(&viewPrivate))) |
|
789 return -1; |
|
790 webView->Release(); |
|
791 |
|
792 |
|
793 BSTR pluginPath = SysAllocStringLen(0, exePath.length() + _tcslen(TestPluginDir)); |
|
794 _tcscpy(pluginPath, exePath.c_str()); |
|
795 _tcscat(pluginPath, TestPluginDir); |
|
796 failed = FAILED(viewPrivate->addAdditionalPluginPath(pluginPath)); |
|
797 SysFreeString(pluginPath); |
|
798 if (failed) |
|
799 return -1; |
|
800 |
|
801 if (FAILED(viewPrivate->viewWindow((OLE_HANDLE*)&webViewWindow))) |
|
802 return -1; |
|
803 |
|
804 SetWindowPos(webViewWindow, 0, 0, 0, maxViewWidth, maxViewHeight, 0); |
|
805 ShowWindow(hostWindow, SW_SHOW); |
|
806 |
|
807 COMPtr<FrameLoadDelegate> frameLoadDelegate; |
|
808 frameLoadDelegate.adoptRef(new FrameLoadDelegate); |
|
809 if (FAILED(webView->setFrameLoadDelegate(frameLoadDelegate.get()))) |
|
810 return -1; |
|
811 |
|
812 COMPtr<UIDelegate> uiDelegate; |
|
813 uiDelegate.adoptRef(new UIDelegate); |
|
814 if (FAILED(webView->setUIDelegate(uiDelegate.get()))) |
|
815 return -1; |
|
816 |
|
817 COMPtr<IWebViewEditing> viewEditing; |
|
818 if (FAILED(webView->QueryInterface(&viewEditing))) |
|
819 return -1; |
|
820 webView->Release(); |
|
821 |
|
822 COMPtr<EditingDelegate> editingDelegate; |
|
823 editingDelegate.adoptRef(new EditingDelegate); |
|
824 if (FAILED(viewEditing->setEditingDelegate(editingDelegate.get()))) |
|
825 return -1; |
|
826 |
|
827 COMPtr<IWebPreferences> preferences; |
|
828 if (FAILED(webView->preferences(&preferences))) |
|
829 return -1; |
|
830 |
|
831 initializePreferences(preferences.get()); |
|
832 |
|
833 COMPtr<IWebIconDatabase> iconDatabase; |
|
834 COMPtr<IWebIconDatabase> tmpIconDatabase; |
|
835 if (FAILED(CoCreateInstance(CLSID_WebIconDatabase, 0, CLSCTX_ALL, IID_IWebIconDatabase, (void**)&tmpIconDatabase))) |
|
836 return -1; |
|
837 if (FAILED(tmpIconDatabase->sharedIconDatabase(&iconDatabase))) |
|
838 return -1; |
|
839 |
|
840 if (FAILED(webView->mainFrame(&frame))) |
|
841 return -1; |
|
842 |
|
843 _CrtMemState entryToMainMemCheckpoint; |
|
844 if (leakChecking) |
|
845 _CrtMemCheckpoint(&entryToMainMemCheckpoint); |
|
846 |
|
847 for (int i = 0; i < argc; ++i) |
|
848 if (!stricmp(argv[i], "--threaded")) { |
|
849 argv[i] = argv[argc - 1]; |
|
850 argc--; |
|
851 threaded = true; |
|
852 break; |
|
853 } |
|
854 |
|
855 if (threaded) |
|
856 startJavaScriptThreads(); |
|
857 |
|
858 if (argc == 2 && strcmp(argv[1], "-") == 0) { |
|
859 char filenameBuffer[2048]; |
|
860 printSeparators = true; |
|
861 while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) { |
|
862 char* newLineCharacter = strchr(filenameBuffer, '\n'); |
|
863 if (newLineCharacter) |
|
864 *newLineCharacter = '\0'; |
|
865 |
|
866 if (strlen(filenameBuffer) == 0) |
|
867 continue; |
|
868 |
|
869 runTest(filenameBuffer); |
|
870 fflush(stdout); |
|
871 } |
|
872 } else { |
|
873 printSeparators = argc > 2; |
|
874 for (int i = 1; i != argc; i++) |
|
875 runTest(argv[i]); |
|
876 } |
|
877 |
|
878 if (threaded) |
|
879 stopJavaScriptThreads(); |
|
880 |
|
881 frame->Release(); |
|
882 |
|
883 if (leakChecking) { |
|
884 // dump leaks to stderr |
|
885 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); |
|
886 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); |
|
887 _CrtMemDumpAllObjectsSince(&entryToMainMemCheckpoint); |
|
888 } |
|
889 |
|
890 return 0; |
|
891 } |