Web Inspector: UI performance: introduce heap size tracking stats.
[WebKit-https.git] / Tools / DumpRenderTree / chromium / TestShell.cpp
1 /*
2  * Copyright (C) 2010 Google 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 are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "TestShell.h"
33
34 #include "DRTDevToolsAgent.h"
35 #include "DRTDevToolsClient.h"
36 #include "LayoutTestController.h"
37 #include "WebArrayBufferView.h"
38 #include "WebDataSource.h"
39 #include "WebDocument.h"
40 #include "WebElement.h"
41 #include "WebFrame.h"
42 #include "WebHistoryItem.h"
43 #include "WebIDBFactory.h"
44 #include "WebTestingSupport.h"
45 #include "WebKit.h"
46 #include "WebPermissions.h"
47 #include "WebPoint.h"
48 #include "WebRuntimeFeatures.h"
49 #include "WebScriptController.h"
50 #include "WebSettings.h"
51 #include "WebSize.h"
52 #include "WebSpeechInputControllerMock.h"
53 #include "WebString.h"
54 #include "WebURLRequest.h"
55 #include "WebURLResponse.h"
56 #include "WebView.h"
57 #include "WebViewHost.h"
58 #include "skia/ext/platform_canvas.h"
59 #include "webkit/support/webkit_support.h"
60 #include "webkit/support/webkit_support_gfx.h"
61 #include <algorithm>
62 #include <cctype>
63 #include <vector>
64 #include <wtf/MD5.h>
65
66 using namespace WebKit;
67 using namespace std;
68
69 // Content area size for newly created windows.
70 static const int testWindowWidth = 800;
71 static const int testWindowHeight = 600;
72
73 // The W3C SVG layout tests use a different size than the other layout tests.
74 static const int SVGTestWindowWidth = 480;
75 static const int SVGTestWindowHeight = 360;
76
77 static const char layoutTestsPattern[] = "/LayoutTests/";
78 static const string::size_type layoutTestsPatternSize = sizeof(layoutTestsPattern) - 1;
79 static const char fileUrlPattern[] = "file:/";
80 static const char fileTestPrefix[] = "(file test):";
81 static const char dataUrlPattern[] = "data:";
82 static const string::size_type dataUrlPatternSize = sizeof(dataUrlPattern) - 1;
83
84 // FIXME: Move this to a common place so that it can be shared with
85 // WebCore::TransparencyWin::makeLayerOpaque().
86 static void makeCanvasOpaque(SkCanvas* canvas)
87 {
88     const SkBitmap& bitmap = canvas->getTopDevice()->accessBitmap(true);
89     ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config);
90
91     SkAutoLockPixels lock(bitmap);
92     for (int y = 0; y < bitmap.height(); y++) {
93         uint32_t* row = bitmap.getAddr32(0, y);
94         for (int x = 0; x < bitmap.width(); x++)
95             row[x] |= 0xFF000000; // Set alpha bits to 1.
96     }
97 }
98
99 TestShell::TestShell(bool testShellMode)
100     : m_testIsPending(false)
101     , m_testIsPreparing(false)
102     , m_focusedWidget(0)
103     , m_testShellMode(testShellMode)
104     , m_devTools(0)
105     , m_allowExternalPages(false)
106     , m_acceleratedCompositingEnabled(false)
107     , m_compositeToTexture(false)
108     , m_forceCompositingMode(false)
109     , m_accelerated2dCanvasEnabled(false)
110     , m_legacyAccelerated2dCanvasEnabled(false)
111     , m_acceleratedDrawingEnabled(false)
112     , m_stressOpt(false)
113     , m_stressDeopt(false)
114     , m_dumpWhenFinished(true)
115 {
116     WebRuntimeFeatures::enableDataTransferItems(true);
117     WebRuntimeFeatures::enableGeolocation(true);
118     WebRuntimeFeatures::enableIndexedDatabase(true);
119     WebRuntimeFeatures::enableFileSystem(true);
120     WebRuntimeFeatures::enableJavaScriptI18NAPI(true);
121     WebRuntimeFeatures::enableMediaStream(true);
122     WebRuntimeFeatures::enableWebAudio(true); 
123
124     m_webPermissions = adoptPtr(new WebPermissions(this));
125     m_accessibilityController = adoptPtr(new AccessibilityController(this));
126     m_layoutTestController = adoptPtr(new LayoutTestController(this));
127     m_eventSender = adoptPtr(new EventSender(this));
128     m_plainTextController = adoptPtr(new PlainTextController());
129     m_textInputController = adoptPtr(new TextInputController(this));
130     m_notificationPresenter = adoptPtr(new NotificationPresenter(this));
131     m_printer = m_testShellMode ? TestEventPrinter::createTestShellPrinter() : TestEventPrinter::createDRTPrinter();
132
133     // 30 second is the same as the value in Mac DRT.
134     // If we use a value smaller than the timeout value of
135     // (new-)run-webkit-tests, (new-)run-webkit-tests misunderstands that a
136     // timed-out DRT process was crashed.
137     m_timeout = 30 * 1000;
138
139 #if ENABLE(INDEXED_DATABASE)
140     m_tempIndexedDBDirectory = adoptPtr(webkit_support::CreateScopedTempDirectory());
141     m_tempIndexedDBDirectory->CreateUniqueTempDir();
142     WebIDBFactory::setTemporaryDatabaseFolder(WebString::fromUTF8(m_tempIndexedDBDirectory->path().c_str()));
143 #endif
144
145     createMainWindow();
146 }
147
148 void TestShell::createMainWindow()
149 {
150     m_drtDevToolsAgent = adoptPtr(new DRTDevToolsAgent);
151     m_webViewHost = adoptPtr(createNewWindow(WebURL(), m_drtDevToolsAgent.get()));
152     m_webView = m_webViewHost->webView();
153     m_drtDevToolsAgent->setWebView(m_webView);
154 }
155
156 TestShell::~TestShell()
157 {
158     // Note: DevTools are closed together with all the other windows in the
159     // windows list.
160
161     // Destroy the WebView before its WebViewHost.
162     m_drtDevToolsAgent->setWebView(0);
163 }
164
165 void TestShell::createDRTDevToolsClient(DRTDevToolsAgent* agent)
166 {
167     m_drtDevToolsClient = adoptPtr(new DRTDevToolsClient(agent, m_devTools->webView()));
168 }
169
170 void TestShell::showDevTools()
171 {
172     if (!m_devTools) {
173         WebURL url = webkit_support::GetDevToolsPathAsURL();
174         if (!url.isValid()) {
175             ASSERT(false);
176             return;
177         }
178         m_devTools = createNewWindow(url);
179         m_devTools->webView()->settings()->setMemoryInfoEnabled(true);
180         ASSERT(m_devTools);
181         createDRTDevToolsClient(m_drtDevToolsAgent.get());
182     }
183     m_devTools->show(WebKit::WebNavigationPolicyNewWindow);
184 }
185
186 void TestShell::closeDevTools()
187 {
188     if (m_devTools) {
189         m_devTools->webView()->settings()->setMemoryInfoEnabled(false);
190         m_drtDevToolsAgent->reset();
191         m_drtDevToolsClient.clear();
192         closeWindow(m_devTools);
193         m_devTools = 0;
194     }
195 }
196
197 void TestShell::resetWebSettings(WebView& webView)
198 {
199     m_prefs.reset();
200     m_prefs.acceleratedCompositingEnabled = m_acceleratedCompositingEnabled;
201     m_prefs.compositeToTexture = m_compositeToTexture;
202     m_prefs.forceCompositingMode = m_forceCompositingMode;
203     m_prefs.accelerated2dCanvasEnabled = m_accelerated2dCanvasEnabled;
204     m_prefs.legacyAccelerated2dCanvasEnabled = m_legacyAccelerated2dCanvasEnabled;
205     m_prefs.acceleratedDrawingEnabled = m_acceleratedDrawingEnabled;
206     m_prefs.applyTo(&webView);
207 }
208
209 void TestShell::runFileTest(const TestParams& params)
210 {
211     ASSERT(params.testUrl.isValid());
212     m_testIsPreparing = true;
213     m_params = params;
214     string testUrl = m_params.testUrl.spec();
215
216     if (testUrl.find("loading/") != string::npos
217         || testUrl.find("loading\\") != string::npos)
218         m_layoutTestController->setShouldDumpFrameLoadCallbacks(true);
219
220     if (testUrl.find("/dumpAsText/") != string::npos
221         || testUrl.find("\\dumpAsText\\") != string::npos) {
222         m_layoutTestController->setShouldDumpAsText(true);
223         m_layoutTestController->setShouldGeneratePixelResults(false);
224     }
225
226     if (testUrl.find("/inspector/") != string::npos
227         || testUrl.find("\\inspector\\") != string::npos)
228         showDevTools();
229
230     if (m_params.debugLayerTree)
231         m_layoutTestController->setShowDebugLayerTree(true);
232
233     if (m_dumpWhenFinished)
234         m_printer->handleTestHeader(testUrl.c_str());
235     loadURL(m_params.testUrl);
236
237     m_testIsPreparing = false;
238     waitTestFinished();
239 }
240
241 static inline bool isSVGTestURL(const WebURL& url)
242 {
243     return url.isValid() && string(url.spec()).find("W3C-SVG-1.1") != string::npos;
244 }
245
246 void TestShell::resizeWindowForTest(WebViewHost* window, const WebURL& url)
247 {
248     int width, height;
249     if (isSVGTestURL(url)) {
250         width = SVGTestWindowWidth;
251         height = SVGTestWindowHeight;
252     } else {
253         width = testWindowWidth;
254         height = testWindowHeight;
255     }
256     window->setWindowRect(WebRect(0, 0, width + virtualWindowBorder * 2, height + virtualWindowBorder * 2));
257 }
258
259 void TestShell::resetTestController()
260 {
261     resetWebSettings(*webView());
262     m_webPermissions->reset();
263     m_accessibilityController->reset();
264     m_layoutTestController->reset();
265     m_eventSender->reset();
266     m_webViewHost->reset();
267     m_notificationPresenter->reset();
268     m_drtDevToolsAgent->reset();
269     if (m_drtDevToolsClient)
270         m_drtDevToolsClient->reset();
271     webView()->scalePage(1, WebPoint(0, 0));
272     webView()->mainFrame()->clearOpener();
273     WebTestingSupport::resetInternalsObject(webView()->mainFrame());
274 }
275
276 void TestShell::loadURL(const WebURL& url)
277 {
278     m_webViewHost->loadURLForFrame(url, WebString());
279 }
280
281 void TestShell::reload()
282 {
283     m_webViewHost->navigationController()->reload();
284 }
285
286 void TestShell::goToOffset(int offset)
287 {
288      m_webViewHost->navigationController()->goToOffset(offset);
289 }
290
291 int TestShell::navigationEntryCount() const
292 {
293     return m_webViewHost->navigationController()->entryCount();
294 }
295
296 void TestShell::callJSGC()
297 {
298     m_webView->mainFrame()->collectGarbage();
299 }
300
301 void TestShell::setFocus(WebWidget* widget, bool enable)
302 {
303     // Simulate the effects of InteractiveSetFocus(), which includes calling
304     // both setFocus() and setIsActive().
305     if (enable) {
306         if (m_focusedWidget != widget) {
307             if (m_focusedWidget)
308                 m_focusedWidget->setFocus(false);
309             webView()->setIsActive(enable);
310             widget->setFocus(enable);
311             m_focusedWidget = widget;
312         }
313     } else {
314         if (m_focusedWidget == widget) {
315             widget->setFocus(enable);
316             webView()->setIsActive(enable);
317             m_focusedWidget = 0;
318         }
319     }
320 }
321
322 void TestShell::testFinished()
323 {
324     if (!m_testIsPending)
325         return;
326     m_testIsPending = false;
327     if (m_dumpWhenFinished)
328         dump();
329     webkit_support::QuitMessageLoop();
330 }
331
332 void TestShell::testTimedOut()
333 {
334     m_printer->handleTimedOut();
335     testFinished();
336 }
337
338 static string dumpDocumentText(WebFrame* frame)
339 {
340     // We use the document element's text instead of the body text here because
341     // not all documents have a body, such as XML documents.
342     WebElement documentElement = frame->document().documentElement();
343     if (documentElement.isNull())
344         return string();
345     return documentElement.innerText().utf8();
346 }
347
348 static string dumpFramesAsText(WebFrame* frame, bool recursive)
349 {
350     string result;
351
352     // Add header for all but the main frame. Skip empty frames.
353     if (frame->parent() && !frame->document().documentElement().isNull()) {
354         result.append("\n--------\nFrame: '");
355         result.append(frame->name().utf8().data());
356         result.append("'\n--------\n");
357     }
358
359     result.append(dumpDocumentText(frame));
360     result.append("\n");
361
362     if (recursive) {
363         for (WebFrame* child = frame->firstChild(); child; child = child->nextSibling())
364             result.append(dumpFramesAsText(child, recursive));
365     }
366
367     return result;
368 }
369
370 static void dumpFrameScrollPosition(WebFrame* frame, bool recursive)
371 {
372     WebSize offset = frame->scrollOffset();
373     if (offset.width > 0 || offset.height > 0) {
374         if (frame->parent())
375             printf("frame '%s' ", frame->name().utf8().data());
376         printf("scrolled to %d,%d\n", offset.width, offset.height);
377     }
378
379     if (!recursive)
380         return;
381     for (WebFrame* child = frame->firstChild(); child; child = child->nextSibling())
382         dumpFrameScrollPosition(child, recursive);
383 }
384
385 struct ToLower {
386     char16 operator()(char16 c) { return tolower(c); }
387 };
388
389 // FIXME: Eliminate std::transform(), std::vector, and std::sort().
390
391 // Returns True if item1 < item2.
392 static bool HistoryItemCompareLess(const WebHistoryItem& item1, const WebHistoryItem& item2)
393 {
394     string16 target1 = item1.target();
395     string16 target2 = item2.target();
396     std::transform(target1.begin(), target1.end(), target1.begin(), ToLower());
397     std::transform(target2.begin(), target2.end(), target2.begin(), ToLower());
398     return target1 < target2;
399 }
400
401 static string normalizeLayoutTestURLInternal(const string& url)
402 {
403     string result = url;
404     size_t pos;
405     if (!url.find(fileUrlPattern) && ((pos = url.find(layoutTestsPattern)) != string::npos)) {
406         // adjust file URLs to match upstream results.
407         result.replace(0, pos + layoutTestsPatternSize, fileTestPrefix);
408     } else if (!url.find(dataUrlPattern)) {
409         // URL-escape data URLs to match results upstream.
410         string path = webkit_support::EscapePath(url.substr(dataUrlPatternSize));
411         result.replace(dataUrlPatternSize, url.length(), path);
412     }
413     return result;
414 }
415
416 static string dumpHistoryItem(const WebHistoryItem& item, int indent, bool isCurrent)
417 {
418     string result;
419
420     if (isCurrent) {
421         result.append("curr->");
422         result.append(indent - 6, ' '); // 6 == "curr->".length()
423     } else
424         result.append(indent, ' ');
425
426     string url = normalizeLayoutTestURLInternal(item.urlString().utf8());
427     result.append(url);
428     if (!item.target().isEmpty()) {
429         result.append(" (in frame \"");
430         result.append(item.target().utf8());
431         result.append("\")");
432     }
433     if (item.isTargetItem())
434         result.append("  **nav target**");
435     result.append("\n");
436
437     const WebVector<WebHistoryItem>& children = item.children();
438     if (!children.isEmpty()) {
439         // Must sort to eliminate arbitrary result ordering which defeats
440         // reproducible testing.
441         // FIXME: WebVector should probably just be a std::vector!!
442         std::vector<WebHistoryItem> sortedChildren;
443         for (size_t i = 0; i < children.size(); ++i)
444             sortedChildren.push_back(children[i]);
445         std::sort(sortedChildren.begin(), sortedChildren.end(), HistoryItemCompareLess);
446         for (size_t i = 0; i < sortedChildren.size(); ++i)
447             result += dumpHistoryItem(sortedChildren[i], indent + 4, false);
448     }
449
450     return result;
451 }
452
453 static void dumpBackForwardList(const TestNavigationController& navigationController, string& result)
454 {
455     result.append("\n============== Back Forward List ==============\n");
456     for (int index = 0; index < navigationController.entryCount(); ++index) {
457         int currentIndex = navigationController.lastCommittedEntryIndex();
458         WebHistoryItem historyItem = navigationController.entryAtIndex(index)->contentState();
459         if (historyItem.isNull()) {
460             historyItem.initialize();
461             historyItem.setURLString(navigationController.entryAtIndex(index)->URL().spec().utf16());
462         }
463         result.append(dumpHistoryItem(historyItem, 8, index == currentIndex));
464     }
465     result.append("===============================================\n");
466 }
467
468 string TestShell::dumpAllBackForwardLists()
469 {
470     string result;
471     for (unsigned i = 0; i < m_windowList.size(); ++i)
472         dumpBackForwardList(*m_windowList[i]->navigationController(), result);
473     return result;
474 }
475
476 void TestShell::dump()
477 {
478     WebScriptController::flushConsoleMessages();
479
480     // Dump the requested representation.
481     WebFrame* frame = m_webView->mainFrame();
482     if (!frame)
483         return;
484     bool shouldDumpAsText = m_layoutTestController->shouldDumpAsText();
485     bool shouldDumpAsAudio = m_layoutTestController->shouldDumpAsAudio();
486     bool shouldGeneratePixelResults = m_layoutTestController->shouldGeneratePixelResults();
487     bool dumpedAnything = false;
488
489     if (shouldDumpAsAudio) {
490         m_printer->handleAudioHeader();
491         
492         const WebKit::WebArrayBufferView& webArrayBufferView = m_layoutTestController->audioData();
493         printf("Content-Length: %d\n", webArrayBufferView.byteLength());
494         
495         if (fwrite(webArrayBufferView.baseAddress(), 1, webArrayBufferView.byteLength(), stdout) != webArrayBufferView.byteLength())
496             FATAL("Short write to stdout, disk full?\n");
497         printf("\n");
498         
499         m_printer->handleTestFooter(true);
500
501         fflush(stdout);
502         fflush(stderr);
503         return;
504     }
505
506     if (m_params.dumpTree) {
507         dumpedAnything = true;
508         m_printer->handleTextHeader();
509         // Text output: the test page can request different types of output
510         // which we handle here.
511         if (!shouldDumpAsText) {
512             // Plain text pages should be dumped as text
513             string mimeType = frame->dataSource()->response().mimeType().utf8();
514             if (mimeType == "text/plain") {
515                 shouldDumpAsText = true;
516                 shouldGeneratePixelResults = false;
517             }
518         }
519         if (shouldDumpAsText) {
520             bool recursive = m_layoutTestController->shouldDumpChildFramesAsText();
521             string dataUtf8 = dumpFramesAsText(frame, recursive);
522             if (fwrite(dataUtf8.c_str(), 1, dataUtf8.size(), stdout) != dataUtf8.size())
523                 FATAL("Short write to stdout, disk full?\n");
524         } else {
525             printf("%s", frame->renderTreeAsText(m_params.debugRenderTree).utf8().data());
526             bool recursive = m_layoutTestController->shouldDumpChildFrameScrollPositions();
527             dumpFrameScrollPosition(frame, recursive);
528         }
529         if (m_layoutTestController->shouldDumpBackForwardList())
530             printf("%s", dumpAllBackForwardLists().c_str());
531     }
532     if (dumpedAnything && m_params.printSeparators)
533         m_printer->handleTextFooter();
534
535     if (m_params.dumpPixels && shouldGeneratePixelResults) {
536         // Image output: we write the image data to the file given on the
537         // command line (for the dump pixels argument), and the MD5 sum to
538         // stdout.
539         dumpedAnything = true;
540         m_webView->layout();
541         if (m_layoutTestController->testRepaint()) {
542             WebSize viewSize = m_webView->size();
543             int width = viewSize.width;
544             int height = viewSize.height;
545             if (m_layoutTestController->sweepHorizontally()) {
546                 for (WebRect column(0, 0, 1, height); column.x < width; column.x++)
547                     m_webViewHost->paintRect(column);
548             } else {
549                 for (WebRect line(0, 0, width, 1); line.y < height; line.y++)
550                     m_webViewHost->paintRect(line);
551             }
552         } else
553             m_webViewHost->paintInvalidatedRegion();
554
555         // See if we need to draw the selection bounds rect. Selection bounds
556         // rect is the rect enclosing the (possibly transformed) selection.
557         // The rect should be drawn after everything is laid out and painted.
558         if (m_layoutTestController->shouldDumpSelectionRect()) {
559             // If there is a selection rect - draw a red 1px border enclosing rect
560             WebRect wr = frame->selectionBoundsRect();
561             if (!wr.isEmpty()) {
562                 // Render a red rectangle bounding selection rect
563                 SkPaint paint;
564                 paint.setColor(0xFFFF0000); // Fully opaque red
565                 paint.setStyle(SkPaint::kStroke_Style);
566                 paint.setFlags(SkPaint::kAntiAlias_Flag);
567                 paint.setStrokeWidth(1.0f);
568                 SkIRect rect; // Bounding rect
569                 rect.set(wr.x, wr.y, wr.x + wr.width, wr.y + wr.height);
570                 m_webViewHost->canvas()->drawIRect(rect, paint);
571             }
572         }
573
574         dumpImage(m_webViewHost->canvas());
575     }
576     m_printer->handleImageFooter();
577     m_printer->handleTestFooter(dumpedAnything);
578     fflush(stdout);
579     fflush(stderr);
580 }
581
582 void TestShell::dumpImage(SkCanvas* canvas) const
583 {
584     // Fix the alpha. The expected PNGs on Mac have an alpha channel, so we want
585     // to keep it. On Windows, the alpha channel is wrong since text/form control
586     // drawing may have erased it in a few places. So on Windows we force it to
587     // opaque and also don't write the alpha channel for the reference. Linux
588     // doesn't have the wrong alpha like Windows, but we match Windows.
589 #if OS(MAC_OS_X)
590     bool discardTransparency = false;
591 #else
592     bool discardTransparency = true;
593     makeCanvasOpaque(canvas);
594 #endif
595
596     const SkBitmap& sourceBitmap = canvas->getTopDevice()->accessBitmap(false);
597     SkAutoLockPixels sourceBitmapLock(sourceBitmap);
598
599     // Compute MD5 sum.
600     MD5 digester;
601     Vector<uint8_t, 16> digestValue;
602     digester.addBytes(reinterpret_cast<const uint8_t*>(sourceBitmap.getPixels()), sourceBitmap.getSize());
603     digester.checksum(digestValue);
604     string md5hash;
605     md5hash.reserve(16 * 2);
606     for (unsigned i = 0; i < 16; ++i) {
607         char hex[3];
608         // Use "x", not "X". The string must be lowercased.
609         sprintf(hex, "%02x", digestValue[i]);
610         md5hash.append(hex);
611     }
612
613     // Only encode and dump the png if the hashes don't match. Encoding the
614     // image is really expensive.
615     if (md5hash.compare(m_params.pixelHash)) {
616         std::vector<unsigned char> png;
617         webkit_support::EncodeBGRAPNGWithChecksum(reinterpret_cast<const unsigned char*>(sourceBitmap.getPixels()), sourceBitmap.width(),
618             sourceBitmap.height(), static_cast<int>(sourceBitmap.rowBytes()), discardTransparency, md5hash, &png);
619
620         m_printer->handleImage(md5hash.c_str(), m_params.pixelHash.c_str(), &png[0], png.size(), m_params.pixelFileName.c_str());
621     } else
622         m_printer->handleImage(md5hash.c_str(), m_params.pixelHash.c_str(), 0, 0, m_params.pixelFileName.c_str());
623 }
624
625 void TestShell::bindJSObjectsToWindow(WebFrame* frame)
626 {
627     WebTestingSupport::injectInternalsObject(frame);
628     m_accessibilityController->bindToJavascript(frame, WebString::fromUTF8("accessibilityController"));
629     m_layoutTestController->bindToJavascript(frame, WebString::fromUTF8("layoutTestController"));
630     m_eventSender->bindToJavascript(frame, WebString::fromUTF8("eventSender"));
631     m_plainTextController->bindToJavascript(frame, WebString::fromUTF8("plainText"));
632     m_textInputController->bindToJavascript(frame, WebString::fromUTF8("textInputController"));
633 }
634
635 WebViewHost* TestShell::createNewWindow(const WebKit::WebURL& url)
636 {
637     return createNewWindow(url, 0);
638 }
639
640 WebViewHost* TestShell::createNewWindow(const WebKit::WebURL& url, DRTDevToolsAgent* devToolsAgent)
641 {
642     WebViewHost* host = new WebViewHost(this);
643     WebView* view = WebView::create(host);
644     view->setPermissionClient(webPermissions());
645     view->setDevToolsAgentClient(devToolsAgent);
646     host->setWebWidget(view);
647     m_prefs.applyTo(view);
648     view->initializeMainFrame(host);
649     m_windowList.append(host);
650     host->loadURLForFrame(url, WebString());
651     return host;
652 }
653
654 void TestShell::closeWindow(WebViewHost* window)
655 {
656     size_t i = m_windowList.find(window);
657     if (i == notFound) {
658         ASSERT_NOT_REACHED();
659         return;
660     }
661     m_windowList.remove(i);
662     WebWidget* focusedWidget = m_focusedWidget;
663     if (window->webWidget() == m_focusedWidget)
664         focusedWidget = 0;
665
666     delete window;
667     // We set the focused widget after deleting the web view host because it
668     // can change the focus.
669     m_focusedWidget = focusedWidget;
670     if (m_focusedWidget) {
671         webView()->setIsActive(true);
672         m_focusedWidget->setFocus(true);
673     }
674 }
675
676 void TestShell::closeRemainingWindows()
677 {
678     // Just close devTools window manually because we have custom deinitialization code for it.
679     closeDevTools();
680
681     // Iterate through the window list and close everything except the main
682     // window. We don't want to delete elements as we're iterating, so we copy
683     // to a temp vector first.
684     Vector<WebViewHost*> windowsToDelete;
685     for (unsigned i = 0; i < m_windowList.size(); ++i) {
686         if (m_windowList[i] != webViewHost())
687             windowsToDelete.append(m_windowList[i]);
688     }
689     ASSERT(windowsToDelete.size() + 1 == m_windowList.size());
690     for (unsigned i = 0; i < windowsToDelete.size(); ++i)
691         closeWindow(windowsToDelete[i]);
692     ASSERT(m_windowList.size() == 1);
693 }
694
695 int TestShell::windowCount()
696 {
697     return m_windowList.size();
698 }
699
700 string TestShell::normalizeLayoutTestURL(const string& url)
701 {
702     return normalizeLayoutTestURLInternal(url);
703 }