WebKitTools/DumpRenderTree/chromium/TestShell.cpp
changeset 2 303757a437d3
parent 0 4f2f89ce4247
equal deleted inserted replaced
0:4f2f89ce4247 2:303757a437d3
     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 "WebViewHost.h"
       
    38 #include "base/md5.h" // FIXME: Wrap by webkit_support.
       
    39 #include "base/string16.h"
       
    40 #include "gfx/codec/png_codec.h" // FIXME: Remove dependecy. WebCore/platform/image-encoder is better?
       
    41 #include "net/base/escape.h" // FIXME: Remove dependency.
       
    42 #include "public/WebDataSource.h"
       
    43 #include "public/WebDocument.h"
       
    44 #include "public/WebElement.h"
       
    45 #include "public/WebFrame.h"
       
    46 #include "public/WebHistoryItem.h"
       
    47 #include "public/WebRuntimeFeatures.h"
       
    48 #include "public/WebScriptController.h"
       
    49 #include "public/WebSettings.h"
       
    50 #include "public/WebSize.h"
       
    51 #include "public/WebString.h"
       
    52 #include "public/WebURLRequest.h"
       
    53 #include "public/WebURLResponse.h"
       
    54 #include "public/WebView.h"
       
    55 #include "skia/ext/bitmap_platform_device.h"
       
    56 #include "skia/ext/platform_canvas.h"
       
    57 #include "webkit/support/webkit_support.h"
       
    58 #include <algorithm>
       
    59 #include <cctype>
       
    60 #include <vector>
       
    61 
       
    62 using namespace WebKit;
       
    63 using namespace std;
       
    64 
       
    65 // Content area size for newly created windows.
       
    66 static const int testWindowWidth = 800;
       
    67 static const int testWindowHeight = 600;
       
    68 
       
    69 // The W3C SVG layout tests use a different size than the other layout tests.
       
    70 static const int SVGTestWindowWidth = 480;
       
    71 static const int SVGTestWindowHeight = 360;
       
    72 
       
    73 static const char layoutTestsPattern[] = "/LayoutTests/";
       
    74 static const string::size_type layoutTestsPatternSize = sizeof(layoutTestsPattern) - 1;
       
    75 static const char fileUrlPattern[] = "file:/";
       
    76 static const char fileTestPrefix[] = "(file test):";
       
    77 static const char dataUrlPattern[] = "data:";
       
    78 static const string::size_type dataUrlPatternSize = sizeof(dataUrlPattern) - 1;
       
    79 
       
    80 TestShell::TestShell(bool testShellMode)
       
    81     : m_testIsPending(false)
       
    82     , m_testIsPreparing(false)
       
    83     , m_focusedWidget(0)
       
    84     , m_testShellMode(testShellMode)
       
    85     , m_allowExternalPages(false)
       
    86     , m_devTools(0)
       
    87 {
       
    88     WebRuntimeFeatures::enableGeolocation(true);
       
    89     m_accessibilityController.set(new AccessibilityController(this));
       
    90     m_layoutTestController.set(new LayoutTestController(this));
       
    91     m_eventSender.set(new EventSender(this));
       
    92     m_plainTextController.set(new PlainTextController());
       
    93     m_textInputController.set(new TextInputController(this));
       
    94     m_notificationPresenter.set(new NotificationPresenter(this));
       
    95     m_printer.set(m_testShellMode ? TestEventPrinter::createTestShellPrinter() : TestEventPrinter::createDRTPrinter());
       
    96 
       
    97     // 30 second is the same as the value in Mac DRT.
       
    98     // If we use a value smaller than the timeout value of
       
    99     // (new-)run-webkit-tests, (new-)run-webkit-tests misunderstands that a
       
   100     // timed-out DRT process was crashed.
       
   101     m_timeout = 30 * 1000;
       
   102 
       
   103     m_drtDevToolsAgent.set(new DRTDevToolsAgent);
       
   104     m_webViewHost = createWebView();
       
   105     m_webView = m_webViewHost->webView();
       
   106     m_drtDevToolsAgent->setWebView(m_webView);
       
   107 }
       
   108 
       
   109 TestShell::~TestShell()
       
   110 {
       
   111     // Note: DevTools are closed together with all the other windows in the
       
   112     // windows list.
       
   113 
       
   114     loadURL(GURL("about:blank"));
       
   115     // Call GC twice to clean up garbage.
       
   116     callJSGC();
       
   117     callJSGC();
       
   118 
       
   119     // Destroy the WebView before its WebViewHost.
       
   120     m_webView->close();
       
   121 }
       
   122 
       
   123 void TestShell::createDRTDevToolsClient(DRTDevToolsAgent* agent)
       
   124 {
       
   125     m_drtDevToolsClient.set(new DRTDevToolsClient(agent, m_devTools->webView()));
       
   126 }
       
   127 
       
   128 void TestShell::showDevTools()
       
   129 {
       
   130     if (!m_devTools) {
       
   131         WebURL url = webkit_support::GetDevToolsPathAsURL();
       
   132         if (!url.isValid()) {
       
   133             ASSERT(false);
       
   134             return;
       
   135         }
       
   136         m_devTools = createNewWindow(url);
       
   137         ASSERT(m_devTools);
       
   138         createDRTDevToolsClient(m_drtDevToolsAgent.get());
       
   139     }
       
   140     m_devTools->show(WebKit::WebNavigationPolicyNewWindow);
       
   141 }
       
   142 
       
   143 void TestShell::closeDevTools()
       
   144 {
       
   145     if (m_devTools) {
       
   146         closeWindow(m_devTools);
       
   147         m_devTools = 0;
       
   148     }
       
   149 }
       
   150 
       
   151 void TestShell::resetWebSettings(WebView& webView)
       
   152 {
       
   153     // Match the settings used by Mac DumpRenderTree, with the exception of
       
   154     // fonts.
       
   155     WebSettings* settings = webView.settings();
       
   156 #if OS(MAC_OS_X)
       
   157     WebString serif = WebString::fromUTF8("Times");
       
   158     settings->setCursiveFontFamily(WebString::fromUTF8("Apple Chancery"));
       
   159     settings->setFantasyFontFamily(WebString::fromUTF8("Papyrus"));
       
   160 #else
       
   161     // NOTE: case matters here, this must be 'times new roman', else
       
   162     // some layout tests fail.
       
   163     WebString serif = WebString::fromUTF8("times new roman");
       
   164 
       
   165     // These two fonts are picked from the intersection of
       
   166     // Win XP font list and Vista font list :
       
   167     //   http://www.microsoft.com/typography/fonts/winxp.htm
       
   168     //   http://blogs.msdn.com/michkap/archive/2006/04/04/567881.aspx
       
   169     // Some of them are installed only with CJK and complex script
       
   170     // support enabled on Windows XP and are out of consideration here.
       
   171     // (although we enabled both on our buildbots.)
       
   172     // They (especially Impact for fantasy) are not typical cursive
       
   173     // and fantasy fonts, but it should not matter for layout tests
       
   174     // as long as they're available.
       
   175     settings->setCursiveFontFamily(WebString::fromUTF8("Comic Sans MS"));
       
   176     settings->setFantasyFontFamily(WebString::fromUTF8("Impact"));
       
   177 #endif
       
   178     settings->setSerifFontFamily(serif);
       
   179     settings->setStandardFontFamily(serif);
       
   180     settings->setFixedFontFamily(WebString::fromUTF8("Courier"));
       
   181     settings->setSansSerifFontFamily(WebString::fromUTF8("Helvetica"));
       
   182 
       
   183     settings->setDefaultTextEncodingName(WebString::fromUTF8("ISO-8859-1"));
       
   184     settings->setDefaultFontSize(16);
       
   185     settings->setDefaultFixedFontSize(13);
       
   186     settings->setMinimumFontSize(1);
       
   187     settings->setMinimumLogicalFontSize(9);
       
   188     settings->setJavaScriptCanOpenWindowsAutomatically(true);
       
   189     settings->setJavaScriptCanAccessClipboard(true);
       
   190     settings->setDOMPasteAllowed(true);
       
   191     settings->setDeveloperExtrasEnabled(false);
       
   192     settings->setNeedsSiteSpecificQuirks(true);
       
   193     settings->setShrinksStandaloneImagesToFit(false);
       
   194     settings->setUsesEncodingDetector(false);
       
   195     settings->setTextAreasAreResizable(false);
       
   196     settings->setJavaEnabled(false);
       
   197     settings->setAllowScriptsToCloseWindows(false);
       
   198     settings->setXSSAuditorEnabled(false);
       
   199     settings->setDownloadableBinaryFontsEnabled(true);
       
   200     settings->setLocalStorageEnabled(true);
       
   201     settings->setOfflineWebApplicationCacheEnabled(true);
       
   202     settings->setAllowFileAccessFromFileURLs(true);
       
   203 
       
   204     // LayoutTests were written with Safari Mac in mind which does not allow
       
   205     // tabbing to links by default.
       
   206     webView.setTabsToLinks(false);
       
   207 
       
   208     // Allow those layout tests running as local files, i.e. under
       
   209     // LayoutTests/http/tests/local, to access http server.
       
   210     settings->setAllowUniversalAccessFromFileURLs(true);
       
   211 
       
   212     settings->setJavaScriptEnabled(true);
       
   213     settings->setPluginsEnabled(true);
       
   214     settings->setWebSecurityEnabled(true);
       
   215     settings->setEditableLinkBehaviorNeverLive();
       
   216     settings->setFontRenderingModeNormal();
       
   217     settings->setShouldPaintCustomScrollbars(true);
       
   218     settings->setTextDirectionSubmenuInclusionBehaviorNeverIncluded();
       
   219 
       
   220     settings->setLoadsImagesAutomatically(true);
       
   221     settings->setImagesEnabled(true);
       
   222 
       
   223 #if OS(DARWIN)
       
   224     settings->setEditingBehavior(WebSettings::EditingBehaviorMac);
       
   225 #else
       
   226     settings->setEditingBehavior(WebSettings::EditingBehaviorWin);
       
   227 #endif
       
   228 }
       
   229 
       
   230 void TestShell::runFileTest(const TestParams& params)
       
   231 {
       
   232     ASSERT(params.testUrl.isValid());
       
   233     m_testIsPreparing = true;
       
   234     m_params = params;
       
   235     string testUrl = m_params.testUrl.spec();
       
   236 
       
   237     bool inspectorTestMode = testUrl.find("/inspector/") != string::npos
       
   238         || testUrl.find("\\inspector\\") != string::npos;
       
   239     m_webView->settings()->setDeveloperExtrasEnabled(inspectorTestMode);
       
   240 
       
   241     if (testUrl.find("loading/") != string::npos
       
   242         || testUrl.find("loading\\") != string::npos)
       
   243         m_layoutTestController->setShouldDumpFrameLoadCallbacks(true);
       
   244 
       
   245     if (inspectorTestMode)
       
   246         showDevTools();
       
   247 
       
   248     m_printer->handleTestHeader(testUrl.c_str());
       
   249     loadURL(m_params.testUrl);
       
   250 
       
   251     m_testIsPreparing = false;
       
   252     waitTestFinished();
       
   253 }
       
   254 
       
   255 static inline bool isSVGTestURL(const WebURL& url)
       
   256 {
       
   257     return url.isValid() && string(url.spec()).find("W3C-SVG-1.1") != string::npos;
       
   258 }
       
   259 
       
   260 void TestShell::resizeWindowForTest(WebViewHost* window, const WebURL& url)
       
   261 {
       
   262     int width, height;
       
   263     if (isSVGTestURL(url)) {
       
   264         width = SVGTestWindowWidth;
       
   265         height = SVGTestWindowHeight;
       
   266     } else {
       
   267         width = testWindowWidth;
       
   268         height = testWindowHeight;
       
   269     }
       
   270     window->setWindowRect(WebRect(0, 0, width + virtualWindowBorder * 2, height + virtualWindowBorder * 2));
       
   271 }
       
   272 
       
   273 void TestShell::resetTestController()
       
   274 {
       
   275     resetWebSettings(*webView());
       
   276     m_accessibilityController->reset();
       
   277     m_layoutTestController->reset();
       
   278     m_eventSender->reset();
       
   279     m_webViewHost->reset();
       
   280     m_notificationPresenter->reset();
       
   281 }
       
   282 
       
   283 void TestShell::loadURL(const WebURL& url)
       
   284 {
       
   285     m_webViewHost->loadURLForFrame(url, WebString());
       
   286 }
       
   287 
       
   288 void TestShell::reload()
       
   289 {
       
   290     m_webViewHost->navigationController()->reload();
       
   291 }
       
   292 
       
   293 void TestShell::goToOffset(int offset)
       
   294 {
       
   295      m_webViewHost->navigationController()->goToOffset(offset);
       
   296 }
       
   297 
       
   298 int TestShell::navigationEntryCount() const
       
   299 {
       
   300     return m_webViewHost->navigationController()->entryCount();
       
   301 }
       
   302 
       
   303 void TestShell::callJSGC()
       
   304 {
       
   305     m_webView->mainFrame()->collectGarbage();
       
   306 }
       
   307 
       
   308 void TestShell::setFocus(WebWidget* widget, bool enable)
       
   309 {
       
   310     // Simulate the effects of InteractiveSetFocus(), which includes calling
       
   311     // both setFocus() and setIsActive().
       
   312     if (enable) {
       
   313         if (m_focusedWidget != widget) {
       
   314             if (m_focusedWidget)
       
   315                 m_focusedWidget->setFocus(false);
       
   316             webView()->setIsActive(enable);
       
   317             widget->setFocus(enable);
       
   318             m_focusedWidget = widget;
       
   319         }
       
   320     } else {
       
   321         if (m_focusedWidget == widget) {
       
   322             widget->setFocus(enable);
       
   323             webView()->setIsActive(enable);
       
   324             m_focusedWidget = 0;
       
   325         }
       
   326     }
       
   327 }
       
   328 
       
   329 void TestShell::testFinished()
       
   330 {
       
   331     if (!m_testIsPending)
       
   332         return;
       
   333     m_testIsPending = false;
       
   334     dump();
       
   335     webkit_support::QuitMessageLoop();
       
   336 }
       
   337 
       
   338 void TestShell::testTimedOut()
       
   339 {
       
   340     m_printer->handleTimedOut();
       
   341     testFinished();
       
   342 }
       
   343 
       
   344 static string dumpDocumentText(WebFrame* frame)
       
   345 {
       
   346     // We use the document element's text instead of the body text here because
       
   347     // not all documents have a body, such as XML documents.
       
   348     WebElement documentElement = frame->document().documentElement();
       
   349     if (documentElement.isNull())
       
   350         return string();
       
   351     return documentElement.innerText().utf8();
       
   352 }
       
   353 
       
   354 static string dumpFramesAsText(WebFrame* frame, bool recursive)
       
   355 {
       
   356     string result;
       
   357 
       
   358     // Add header for all but the main frame. Skip empty frames.
       
   359     if (frame->parent() && !frame->document().documentElement().isNull()) {
       
   360         result.append("\n--------\nFrame: '");
       
   361         result.append(frame->name().utf8().data());
       
   362         result.append("'\n--------\n");
       
   363     }
       
   364 
       
   365     result.append(dumpDocumentText(frame));
       
   366     result.append("\n");
       
   367 
       
   368     if (recursive) {
       
   369         for (WebFrame* child = frame->firstChild(); child; child = child->nextSibling())
       
   370             result.append(dumpFramesAsText(child, recursive));
       
   371     }
       
   372 
       
   373     return result;
       
   374 }
       
   375 
       
   376 static void dumpFrameScrollPosition(WebFrame* frame, bool recursive)
       
   377 {
       
   378     WebSize offset = frame->scrollOffset();
       
   379     if (offset.width > 0 || offset.height > 0) {
       
   380         if (frame->parent())
       
   381             printf("frame '%s' ", frame->name().utf8().data());
       
   382         printf("scrolled to %d,%d\n", offset.width, offset.height);
       
   383     }
       
   384 
       
   385     if (!recursive)
       
   386         return;
       
   387     for (WebFrame* child = frame->firstChild(); child; child = child->nextSibling())
       
   388         dumpFrameScrollPosition(child, recursive);
       
   389 }
       
   390 
       
   391 struct ToLower {
       
   392     char16 operator()(char16 c) { return tolower(c); }
       
   393 };
       
   394 
       
   395 // FIXME: Eliminate std::transform(), std::vector, and std::sort().
       
   396 
       
   397 // Returns True if item1 < item2.
       
   398 static bool HistoryItemCompareLess(const WebHistoryItem& item1, const WebHistoryItem& item2)
       
   399 {
       
   400     string16 target1 = item1.target();
       
   401     string16 target2 = item2.target();
       
   402     std::transform(target1.begin(), target1.end(), target1.begin(), ToLower());
       
   403     std::transform(target2.begin(), target2.end(), target2.begin(), ToLower());
       
   404     return target1 < target2;
       
   405 }
       
   406 
       
   407 static string dumpHistoryItem(const WebHistoryItem& item, int indent, bool isCurrent)
       
   408 {
       
   409     string result;
       
   410 
       
   411     if (isCurrent) {
       
   412         result.append("curr->");
       
   413         result.append(indent - 6, ' '); // 6 == "curr->".length()
       
   414     } else {
       
   415         result.append(indent, ' ');
       
   416     }
       
   417 
       
   418     string url = item.urlString().utf8();
       
   419     size_t pos;
       
   420     if (!url.find(fileUrlPattern) && ((pos = url.find(layoutTestsPattern)) != string::npos)) {
       
   421         // adjust file URLs to match upstream results.
       
   422         url.replace(0, pos + layoutTestsPatternSize, fileTestPrefix);
       
   423     } else if (!url.find(dataUrlPattern)) {
       
   424         // URL-escape data URLs to match results upstream.
       
   425         string path = EscapePath(url.substr(dataUrlPatternSize));
       
   426         url.replace(dataUrlPatternSize, url.length(), path);
       
   427     }
       
   428 
       
   429     result.append(url);
       
   430     if (!item.target().isEmpty()) {
       
   431         result.append(" (in frame \"");
       
   432         result.append(item.target().utf8());
       
   433         result.append("\")");
       
   434     }
       
   435     if (item.isTargetItem())
       
   436         result.append("  **nav target**");
       
   437     result.append("\n");
       
   438 
       
   439     const WebVector<WebHistoryItem>& children = item.children();
       
   440     if (!children.isEmpty()) {
       
   441         // Must sort to eliminate arbitrary result ordering which defeats
       
   442         // reproducible testing.
       
   443         // FIXME: WebVector should probably just be a std::vector!!
       
   444         std::vector<WebHistoryItem> sortedChildren;
       
   445         for (size_t i = 0; i < children.size(); ++i)
       
   446             sortedChildren.push_back(children[i]);
       
   447         std::sort(sortedChildren.begin(), sortedChildren.end(), HistoryItemCompareLess);
       
   448         for (size_t i = 0; i < sortedChildren.size(); ++i)
       
   449             result += dumpHistoryItem(sortedChildren[i], indent + 4, false);
       
   450     }
       
   451 
       
   452     return result;
       
   453 }
       
   454 
       
   455 static void dumpBackForwardList(const TestNavigationController& navigationController, string& result)
       
   456 {
       
   457     result.append("\n============== Back Forward List ==============\n");
       
   458     for (int index = 0; index < navigationController.entryCount(); ++index) {
       
   459         int currentIndex = navigationController.lastCommittedEntryIndex();
       
   460         WebHistoryItem historyItem = navigationController.entryAtIndex(index)->contentState();
       
   461         if (historyItem.isNull()) {
       
   462             historyItem.initialize();
       
   463             historyItem.setURLString(navigationController.entryAtIndex(index)->URL().spec().utf16());
       
   464         }
       
   465         result.append(dumpHistoryItem(historyItem, 8, index == currentIndex));
       
   466     }
       
   467     result.append("===============================================\n");
       
   468 }
       
   469 
       
   470 string TestShell::dumpAllBackForwardLists()
       
   471 {
       
   472     string result;
       
   473     for (unsigned i = 0; i < m_windowList.size(); ++i)
       
   474         dumpBackForwardList(*m_windowList[i]->navigationController(), result);
       
   475     return result;
       
   476 }
       
   477 
       
   478 void TestShell::dump()
       
   479 {
       
   480     WebScriptController::flushConsoleMessages();
       
   481 
       
   482     // Dump the requested representation.
       
   483     WebFrame* frame = m_webView->mainFrame();
       
   484     if (!frame)
       
   485         return;
       
   486     bool shouldDumpAsText = m_layoutTestController->shouldDumpAsText();
       
   487     bool dumpedAnything = false;
       
   488     if (m_params.dumpTree) {
       
   489         dumpedAnything = true;
       
   490         m_printer->handleTextHeader();
       
   491         // Text output: the test page can request different types of output
       
   492         // which we handle here.
       
   493         if (!shouldDumpAsText) {
       
   494             // Plain text pages should be dumped as text
       
   495             string mimeType = frame->dataSource()->response().mimeType().utf8();
       
   496             shouldDumpAsText = mimeType == "text/plain";
       
   497         }
       
   498         if (shouldDumpAsText) {
       
   499             bool recursive = m_layoutTestController->shouldDumpChildFramesAsText();
       
   500             string dataUtf8 = dumpFramesAsText(frame, recursive);
       
   501             if (fwrite(dataUtf8.c_str(), 1, dataUtf8.size(), stdout) != dataUtf8.size())
       
   502                 FATAL("Short write to stdout, disk full?\n");
       
   503         } else {
       
   504             printf("%s", frame->renderTreeAsText().utf8().data());
       
   505             bool recursive = m_layoutTestController->shouldDumpChildFrameScrollPositions();
       
   506             dumpFrameScrollPosition(frame, recursive);
       
   507         }
       
   508         if (m_layoutTestController->shouldDumpBackForwardList())
       
   509             printf("%s", dumpAllBackForwardLists().c_str());
       
   510     }
       
   511     if (dumpedAnything && m_params.printSeparators)
       
   512         m_printer->handleTextFooter();
       
   513 
       
   514     if (m_params.dumpPixels && m_layoutTestController->shouldGeneratePixelResults()) {
       
   515         // Image output: we write the image data to the file given on the
       
   516         // command line (for the dump pixels argument), and the MD5 sum to
       
   517         // stdout.
       
   518         dumpedAnything = true;
       
   519         m_webView->layout();
       
   520         if (m_layoutTestController->testRepaint()) {
       
   521             WebSize viewSize = m_webView->size();
       
   522             int width = viewSize.width;
       
   523             int height = viewSize.height;
       
   524             if (m_layoutTestController->sweepHorizontally()) {
       
   525                 for (WebRect column(0, 0, 1, height); column.x < width; column.x++)
       
   526                     m_webViewHost->paintRect(column);
       
   527             } else {
       
   528                 for (WebRect line(0, 0, width, 1); line.y < height; line.y++)
       
   529                     m_webViewHost->paintRect(line);
       
   530             }
       
   531         } else
       
   532             m_webViewHost->paintInvalidatedRegion();
       
   533 
       
   534         // See if we need to draw the selection bounds rect. Selection bounds
       
   535         // rect is the rect enclosing the (possibly transformed) selection.
       
   536         // The rect should be drawn after everything is laid out and painted.
       
   537         if (m_layoutTestController->shouldDumpSelectionRect()) {
       
   538             // If there is a selection rect - draw a red 1px border enclosing rect
       
   539             WebRect wr = frame->selectionBoundsRect();
       
   540             if (!wr.isEmpty()) {
       
   541                 // Render a red rectangle bounding selection rect
       
   542                 SkPaint paint;
       
   543                 paint.setColor(0xFFFF0000); // Fully opaque red
       
   544                 paint.setStyle(SkPaint::kStroke_Style);
       
   545                 paint.setFlags(SkPaint::kAntiAlias_Flag);
       
   546                 paint.setStrokeWidth(1.0f);
       
   547                 SkIRect rect; // Bounding rect
       
   548                 rect.set(wr.x, wr.y, wr.x + wr.width, wr.y + wr.height);
       
   549                 m_webViewHost->canvas()->drawIRect(rect, paint);
       
   550             }
       
   551         }
       
   552 
       
   553         dumpImage(m_webViewHost->canvas());
       
   554     }
       
   555     m_printer->handleImageFooter();
       
   556     m_printer->handleTestFooter(dumpedAnything);
       
   557     fflush(stdout);
       
   558     fflush(stderr);
       
   559 }
       
   560 
       
   561 void TestShell::dumpImage(skia::PlatformCanvas* canvas) const
       
   562 {
       
   563     skia::BitmapPlatformDevice& device =
       
   564         static_cast<skia::BitmapPlatformDevice&>(canvas->getTopPlatformDevice());
       
   565     const SkBitmap& sourceBitmap = device.accessBitmap(false);
       
   566 
       
   567     SkAutoLockPixels sourceBitmapLock(sourceBitmap);
       
   568 
       
   569     // Fix the alpha. The expected PNGs on Mac have an alpha channel, so we want
       
   570     // to keep it. On Windows, the alpha channel is wrong since text/form control
       
   571     // drawing may have erased it in a few places. So on Windows we force it to
       
   572     // opaque and also don't write the alpha channel for the reference. Linux
       
   573     // doesn't have the wrong alpha like Windows, but we ignore it anyway.
       
   574 #if OS(WINDOWS)
       
   575     bool discardTransparency = true;
       
   576     device.makeOpaque(0, 0, sourceBitmap.width(), sourceBitmap.height());
       
   577 #elif OS(MAC_OS_X)
       
   578     bool discardTransparency = false;
       
   579 #elif OS(UNIX)
       
   580     bool discardTransparency = true;
       
   581 #endif
       
   582 
       
   583     // Compute MD5 sum.  We should have done this before calling
       
   584     // device.makeOpaque on Windows.  Because we do it after the call, there are
       
   585     // some images that are the pixel identical on windows and other platforms
       
   586     // but have different MD5 sums.  At this point, rebaselining all the windows
       
   587     // tests is too much of a pain, so we just check in different baselines.
       
   588     MD5Context ctx;
       
   589     MD5Init(&ctx);
       
   590     MD5Update(&ctx, sourceBitmap.getPixels(), sourceBitmap.getSize());
       
   591 
       
   592     MD5Digest digest;
       
   593     MD5Final(&digest, &ctx);
       
   594     string md5hash = MD5DigestToBase16(digest);
       
   595 
       
   596     // Only encode and dump the png if the hashes don't match. Encoding the image
       
   597     // is really expensive.
       
   598     if (md5hash.compare(m_params.pixelHash)) {
       
   599         std::vector<unsigned char> png;
       
   600         gfx::PNGCodec::ColorFormat colorFormat = gfx::PNGCodec::FORMAT_BGRA;
       
   601         gfx::PNGCodec::Encode(
       
   602             reinterpret_cast<const unsigned char*>(sourceBitmap.getPixels()),
       
   603             colorFormat, sourceBitmap.width(), sourceBitmap.height(),
       
   604             static_cast<int>(sourceBitmap.rowBytes()), discardTransparency, &png);
       
   605 
       
   606         m_printer->handleImage(md5hash.c_str(), m_params.pixelHash.c_str(), &png[0], png.size(), m_params.pixelFileName.c_str());
       
   607     } else
       
   608         m_printer->handleImage(md5hash.c_str(), m_params.pixelHash.c_str(), 0, 0, m_params.pixelFileName.c_str());
       
   609 }
       
   610 
       
   611 void TestShell::bindJSObjectsToWindow(WebFrame* frame)
       
   612 {
       
   613     m_accessibilityController->bindToJavascript(frame, WebString::fromUTF8("accessibilityController"));
       
   614     m_layoutTestController->bindToJavascript(frame, WebString::fromUTF8("layoutTestController"));
       
   615     m_eventSender->bindToJavascript(frame, WebString::fromUTF8("eventSender"));
       
   616     m_plainTextController->bindToJavascript(frame, WebString::fromUTF8("plainText"));
       
   617     m_textInputController->bindToJavascript(frame, WebString::fromUTF8("textInputController"));
       
   618 }
       
   619 
       
   620 WebViewHost* TestShell::createWebView()
       
   621 {
       
   622     return createNewWindow(WebURL());
       
   623 }
       
   624 
       
   625 WebViewHost* TestShell::createNewWindow(const WebURL& url)
       
   626 {
       
   627     WebViewHost* host = new WebViewHost(this);
       
   628     WebView* view = WebView::create(host, m_drtDevToolsAgent.get());
       
   629     host->setWebWidget(view);
       
   630     resetWebSettings(*view);
       
   631     view->initializeMainFrame(host);
       
   632     m_windowList.append(host);
       
   633     host->loadURLForFrame(url, WebString());
       
   634     return host;
       
   635 }
       
   636 
       
   637 void TestShell::closeWindow(WebViewHost* window)
       
   638 {
       
   639     size_t i = m_windowList.find(window);
       
   640     if (i == notFound) {
       
   641         ASSERT_NOT_REACHED();
       
   642         return;
       
   643     }
       
   644     m_windowList.remove(i);
       
   645     if (window->webWidget() == m_focusedWidget)
       
   646         m_focusedWidget = 0;
       
   647     window->webWidget()->close();
       
   648     delete window;
       
   649 }
       
   650 
       
   651 void TestShell::closeRemainingWindows()
       
   652 {
       
   653     // Iterate through the window list and close everything except the main
       
   654     // ihwindow. We don't want to delete elements as we're iterating, so we copy
       
   655     // to a temp vector first.
       
   656     Vector<WebViewHost*> windowsToDelete;
       
   657     for (unsigned i = 0; i < m_windowList.size(); ++i) {
       
   658         if (m_windowList[i] != webViewHost())
       
   659             windowsToDelete.append(m_windowList[i]);
       
   660     }
       
   661     ASSERT(windowsToDelete.size() + 1 == m_windowList.size());
       
   662     for (unsigned i = 0; i < windowsToDelete.size(); ++i)
       
   663         closeWindow(windowsToDelete[i]);
       
   664     ASSERT(m_windowList.size() == 1);
       
   665 }
       
   666 
       
   667 int TestShell::windowCount()
       
   668 {
       
   669     return m_windowList.size();
       
   670 }