~ubuntu-branches/ubuntu/raring/qtwebkit-source/raring-proposed

« back to all changes in this revision

Viewing changes to Tools/DumpRenderTree/chromium/TestShell.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2013-02-18 14:24:18 UTC
  • Revision ID: package-import@ubuntu.com-20130218142418-eon0jmjg3nj438uy
Tags: upstream-2.3
ImportĀ upstreamĀ versionĀ 2.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2012 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 "DRTTestRunner.h"
 
37
#include "MockWebPrerenderingSupport.h"
 
38
#include "WebCache.h"
 
39
#include "WebDataSource.h"
 
40
#include "WebDocument.h"
 
41
#include "WebElement.h"
 
42
#include "WebFrame.h"
 
43
#include "WebHistoryItem.h"
 
44
#include "WebIDBFactory.h"
 
45
#include "WebTestingSupport.h"
 
46
#include "WebPermissions.h"
 
47
#include "WebRuntimeFeatures.h"
 
48
#include "WebScriptController.h"
 
49
#include "WebSettings.h"
 
50
#include "WebTestProxy.h"
 
51
#include "WebView.h"
 
52
#include "WebViewHost.h"
 
53
#include "platform/WebArrayBufferView.h"
 
54
#include "skia/ext/platform_canvas.h"
 
55
#include "webkit/support/webkit_support.h"
 
56
#include "webkit/support/webkit_support_gfx.h"
 
57
#include <public/Platform.h>
 
58
#include <public/WebCompositorSupport.h>
 
59
#include <public/WebPoint.h>
 
60
#include <public/WebSize.h>
 
61
#include <public/WebString.h>
 
62
#include <public/WebThread.h>
 
63
#include <public/WebURLRequest.h>
 
64
#include <public/WebURLResponse.h>
 
65
#include <algorithm>
 
66
#include <cctype>
 
67
#include <vector>
 
68
#include <wtf/MD5.h>
 
69
#include <wtf/OwnArrayPtr.h>
 
70
 
 
71
 
 
72
using namespace WebKit;
 
73
using namespace WebTestRunner;
 
74
using namespace std;
 
75
 
 
76
// Content area size for newly created windows.
 
77
static const int testWindowWidth = 800;
 
78
static const int testWindowHeight = 600;
 
79
 
 
80
// The W3C SVG layout tests use a different size than the other layout tests.
 
81
static const int SVGTestWindowWidth = 480;
 
82
static const int SVGTestWindowHeight = 360;
 
83
 
 
84
static const char layoutTestsPattern[] = "/LayoutTests/";
 
85
static const string::size_type layoutTestsPatternSize = sizeof(layoutTestsPattern) - 1;
 
86
static const char fileUrlPattern[] = "file:/";
 
87
static const char fileTestPrefix[] = "(file test):";
 
88
static const char dataUrlPattern[] = "data:";
 
89
static const string::size_type dataUrlPatternSize = sizeof(dataUrlPattern) - 1;
 
90
 
 
91
// FIXME: Move this to a common place so that it can be shared with
 
92
// WebCore::TransparencyWin::makeLayerOpaque().
 
93
static void makeCanvasOpaque(SkCanvas* canvas)
 
94
{
 
95
    const SkBitmap& bitmap = canvas->getTopDevice()->accessBitmap(true);
 
96
    ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config);
 
97
 
 
98
    SkAutoLockPixels lock(bitmap);
 
99
    for (int y = 0; y < bitmap.height(); y++) {
 
100
        uint32_t* row = bitmap.getAddr32(0, y);
 
101
        for (int x = 0; x < bitmap.width(); x++)
 
102
            row[x] |= 0xFF000000; // Set alpha bits to 1.
 
103
    }
 
104
}
 
105
 
 
106
TestShell::TestShell()
 
107
    : m_testIsPending(false)
 
108
    , m_testIsPreparing(false)
 
109
    , m_focusedWidget(0)
 
110
    , m_devTools(0)
 
111
    , m_dumpPixelsForCurrentTest(false)
 
112
    , m_allowExternalPages(false)
 
113
    , m_acceleratedCompositingForVideoEnabled(false)
 
114
    , m_acceleratedCompositingForFixedPositionEnabled(false)
 
115
    , m_softwareCompositingEnabled(false)
 
116
    , m_threadedCompositingEnabled(false)
 
117
    , m_forceCompositingMode(false)
 
118
    , m_accelerated2dCanvasEnabled(false)
 
119
    , m_deferred2dCanvasEnabled(false)
 
120
    , m_acceleratedPaintingEnabled(false)
 
121
    , m_perTilePaintingEnabled(false)
 
122
    , m_acceleratedAnimationEnabled(false)
 
123
    , m_deferredImageDecodingEnabled(false)
 
124
    , m_stressOpt(false)
 
125
    , m_stressDeopt(false)
 
126
    , m_dumpWhenFinished(true)
 
127
    , m_isDisplayingModalDialog(false)
 
128
{
 
129
    WebRuntimeFeatures::enableDataTransferItems(true);
 
130
    WebRuntimeFeatures::enableDeviceMotion(false);
 
131
    WebRuntimeFeatures::enableGeolocation(true);
 
132
    WebRuntimeFeatures::enableIndexedDatabase(true);
 
133
    WebRuntimeFeatures::enableInputTypeDateTime(true);
 
134
    WebRuntimeFeatures::enableInputTypeDateTimeLocal(true);
 
135
    WebRuntimeFeatures::enableInputTypeMonth(true);
 
136
    WebRuntimeFeatures::enableInputTypeTime(true);
 
137
    WebRuntimeFeatures::enableInputTypeWeek(true);
 
138
    WebRuntimeFeatures::enableFileSystem(true);
 
139
    WebRuntimeFeatures::enableJavaScriptI18NAPI(true);
 
140
    WebRuntimeFeatures::enableMediaSource(true);
 
141
    WebRuntimeFeatures::enableEncryptedMedia(true);
 
142
    WebRuntimeFeatures::enableMediaStream(true);
 
143
    WebRuntimeFeatures::enablePeerConnection(true);
 
144
    WebRuntimeFeatures::enableWebAudio(true);
 
145
    WebRuntimeFeatures::enableVideoTrack(true);
 
146
    WebRuntimeFeatures::enableGamepad(true);
 
147
    WebRuntimeFeatures::enableShadowDOM(true);
 
148
    WebRuntimeFeatures::enableStyleScoped(true);
 
149
    WebRuntimeFeatures::enableScriptedSpeech(true);
 
150
    WebRuntimeFeatures::enableRequestAutocomplete(true);
 
151
 
 
152
    // 30 second is the same as the value in Mac DRT.
 
153
    // If we use a value smaller than the timeout value of
 
154
    // (new-)run-webkit-tests, (new-)run-webkit-tests misunderstands that a
 
155
    // timed-out DRT process was crashed.
 
156
    m_timeout = 30 * 1000;
 
157
}
 
158
 
 
159
void TestShell::initialize()
 
160
{
 
161
    m_webPermissions = adoptPtr(new WebPermissions(this));
 
162
    m_testInterfaces = adoptPtr(new WebTestInterfaces());
 
163
    m_testRunner = adoptPtr(new DRTTestRunner(this));
 
164
#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
 
165
    m_notificationPresenter = adoptPtr(new NotificationPresenter(this));
 
166
#endif
 
167
#if ENABLE(LINK_PRERENDER)
 
168
    m_prerenderingSupport = adoptPtr(new MockWebPrerenderingSupport());
 
169
#endif
 
170
 
 
171
    WTF::initializeThreading();
 
172
 
 
173
    if (m_threadedCompositingEnabled)
 
174
        m_webCompositorThread = adoptPtr(WebKit::Platform::current()->createThread("Compositor"));
 
175
    WebKit::Platform::current()->compositorSupport()->initialize(m_webCompositorThread.get());
 
176
 
 
177
    createMainWindow();
 
178
}
 
179
 
 
180
void TestShell::createMainWindow()
 
181
{
 
182
    m_drtDevToolsAgent = adoptPtr(new DRTDevToolsAgent);
 
183
    m_webViewHost = adoptPtr(createNewWindow(WebURL(), m_drtDevToolsAgent.get()));
 
184
    m_webView = m_webViewHost->webView();
 
185
    m_testInterfaces->setDelegate(m_webViewHost.get());
 
186
    m_testInterfaces->setWebView(m_webView);
 
187
    m_testRunner->setDelegate(m_webViewHost.get());
 
188
    m_testRunner->setWebView(m_webView);
 
189
    m_drtDevToolsAgent->setWebView(m_webView);
 
190
}
 
191
 
 
192
TestShell::~TestShell()
 
193
{
 
194
    m_testInterfaces->setDelegate(0);
 
195
    m_testInterfaces->setWebView(0);
 
196
    m_testRunner->setDelegate(0);
 
197
    m_testRunner->setWebView(0);
 
198
    m_drtDevToolsAgent->setWebView(0);
 
199
}
 
200
 
 
201
void TestShell::createDRTDevToolsClient(DRTDevToolsAgent* agent)
 
202
{
 
203
    m_drtDevToolsClient = adoptPtr(new DRTDevToolsClient(agent, m_devTools->webView()));
 
204
}
 
205
 
 
206
void TestShell::showDevTools()
 
207
{
 
208
    if (!m_devTools) {
 
209
        WebURL url = webkit_support::GetDevToolsPathAsURL();
 
210
        if (!url.isValid()) {
 
211
            ASSERT(false);
 
212
            return;
 
213
        }
 
214
        m_devTools = createNewWindow(url);
 
215
        m_devTools->webView()->settings()->setMemoryInfoEnabled(true);
 
216
        m_devTools->setLogConsoleOutput(false);
 
217
        ASSERT(m_devTools);
 
218
        createDRTDevToolsClient(m_drtDevToolsAgent.get());
 
219
    }
 
220
    m_devTools->show(WebKit::WebNavigationPolicyNewWindow);
 
221
}
 
222
 
 
223
void TestShell::closeDevTools()
 
224
{
 
225
    if (m_devTools) {
 
226
        m_devTools->webView()->settings()->setMemoryInfoEnabled(false);
 
227
        m_drtDevToolsAgent->reset();
 
228
        m_drtDevToolsClient.clear();
 
229
        closeWindow(m_devTools);
 
230
        m_devTools = 0;
 
231
    }
 
232
}
 
233
 
 
234
void TestShell::resetWebSettings(WebView& webView)
 
235
{
 
236
    m_prefs.reset();
 
237
    m_prefs.acceleratedCompositingEnabled = true;
 
238
    m_prefs.acceleratedCompositingForVideoEnabled = m_acceleratedCompositingForVideoEnabled;
 
239
    m_prefs.acceleratedCompositingForFixedPositionEnabled = m_acceleratedCompositingForFixedPositionEnabled;
 
240
    m_prefs.forceCompositingMode = m_forceCompositingMode;
 
241
    m_prefs.accelerated2dCanvasEnabled = m_accelerated2dCanvasEnabled;
 
242
    m_prefs.deferred2dCanvasEnabled = m_deferred2dCanvasEnabled;
 
243
    m_prefs.acceleratedPaintingEnabled = m_acceleratedPaintingEnabled;
 
244
    m_prefs.perTilePaintingEnabled = m_perTilePaintingEnabled;
 
245
    m_prefs.acceleratedAnimationEnabled = m_acceleratedAnimationEnabled;
 
246
    m_prefs.deferredImageDecodingEnabled = m_deferredImageDecodingEnabled;
 
247
    m_prefs.applyTo(&webView);
 
248
}
 
249
 
 
250
void TestShell::runFileTest(const TestParams& params, bool shouldDumpPixels)
 
251
{
 
252
    ASSERT(params.testUrl.isValid());
 
253
    m_dumpPixelsForCurrentTest = shouldDumpPixels;
 
254
    m_testIsPreparing = true;
 
255
    m_params = params;
 
256
    string testUrl = m_params.testUrl.spec();
 
257
 
 
258
    m_testRunner->setShouldGeneratePixelResults(shouldDumpPixels);
 
259
 
 
260
    if (testUrl.find("loading/") != string::npos
 
261
        || testUrl.find("loading\\") != string::npos)
 
262
        m_testRunner->setShouldDumpFrameLoadCallbacks(true);
 
263
 
 
264
    if (testUrl.find("compositing/") != string::npos || testUrl.find("compositing\\") != string::npos) {
 
265
        if (!m_softwareCompositingEnabled)
 
266
            m_prefs.accelerated2dCanvasEnabled = true;
 
267
        m_prefs.acceleratedCompositingForVideoEnabled = true;
 
268
        m_prefs.deferred2dCanvasEnabled = true;
 
269
        m_prefs.mockScrollbarsEnabled = true;
 
270
        m_prefs.applyTo(m_webView);
 
271
    }
 
272
 
 
273
    if (testUrl.find("/dumpAsText/") != string::npos
 
274
        || testUrl.find("\\dumpAsText\\") != string::npos) {
 
275
        m_testRunner->setShouldDumpAsText(true);
 
276
        m_testRunner->setShouldGeneratePixelResults(false);
 
277
    }
 
278
 
 
279
    if (testUrl.find("/inspector/") != string::npos
 
280
        || testUrl.find("\\inspector\\") != string::npos)
 
281
        showDevTools();
 
282
 
 
283
    if (m_params.debugLayerTree)
 
284
        m_testRunner->setShowDebugLayerTree(true);
 
285
 
 
286
    if (m_dumpWhenFinished)
 
287
        m_printer.handleTestHeader(testUrl.c_str());
 
288
    loadURL(m_params.testUrl);
 
289
 
 
290
    m_testIsPreparing = false;
 
291
    waitTestFinished();
 
292
}
 
293
 
 
294
static inline bool isSVGTestURL(const WebURL& url)
 
295
{
 
296
    return url.isValid() && string(url.spec()).find("W3C-SVG-1.1") != string::npos;
 
297
}
 
298
 
 
299
void TestShell::resizeWindowForTest(WebViewHost* window, const WebURL& url)
 
300
{
 
301
    int width, height;
 
302
    if (isSVGTestURL(url)) {
 
303
        width = SVGTestWindowWidth;
 
304
        height = SVGTestWindowHeight;
 
305
    } else {
 
306
        width = testWindowWidth;
 
307
        height = testWindowHeight;
 
308
    }
 
309
    window->setWindowRect(WebRect(0, 0, width + virtualWindowBorder * 2, height + virtualWindowBorder * 2));
 
310
}
 
311
 
 
312
void TestShell::resetTestController()
 
313
{
 
314
    resetWebSettings(*webView());
 
315
    m_webPermissions->reset();
 
316
    m_testInterfaces->resetAll();
 
317
    m_testRunner->reset();
 
318
    m_webViewHost->reset();
 
319
#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
 
320
    m_notificationPresenter->reset();
 
321
#endif
 
322
#if OS(ANDROID)
 
323
    webkit_support::ReleaseMediaResources();
 
324
#endif
 
325
    m_drtDevToolsAgent->reset();
 
326
    if (m_drtDevToolsClient)
 
327
        m_drtDevToolsClient->reset();
 
328
    webView()->setPageScaleFactor(1, WebPoint(0, 0));
 
329
    webView()->enableFixedLayoutMode(false);
 
330
    webView()->setFixedLayoutSize(WebSize(0, 0));
 
331
    webView()->mainFrame()->clearOpener();
 
332
    WebTestingSupport::resetInternalsObject(webView()->mainFrame());
 
333
    WebCache::clear();
 
334
}
 
335
 
 
336
void TestShell::loadURL(const WebURL& url)
 
337
{
 
338
    m_webViewHost->loadURLForFrame(url, WebString());
 
339
}
 
340
 
 
341
void TestShell::reload()
 
342
{
 
343
    m_webViewHost->navigationController()->reload();
 
344
}
 
345
 
 
346
void TestShell::goToOffset(int offset)
 
347
{
 
348
     m_webViewHost->navigationController()->goToOffset(offset);
 
349
}
 
350
 
 
351
int TestShell::navigationEntryCount() const
 
352
{
 
353
    return m_webViewHost->navigationController()->entryCount();
 
354
}
 
355
 
 
356
void TestShell::callJSGC()
 
357
{
 
358
    m_webView->mainFrame()->collectGarbage();
 
359
}
 
360
 
 
361
void TestShell::setFocus(WebWidget* widget, bool enable)
 
362
{
 
363
    // Simulate the effects of InteractiveSetFocus(), which includes calling
 
364
    // both setFocus() and setIsActive().
 
365
    if (enable) {
 
366
        if (m_focusedWidget != widget) {
 
367
            if (m_focusedWidget)
 
368
                m_focusedWidget->setFocus(false);
 
369
            webView()->setIsActive(enable);
 
370
            widget->setFocus(enable);
 
371
            m_focusedWidget = widget;
 
372
        }
 
373
    } else {
 
374
        if (m_focusedWidget == widget) {
 
375
            widget->setFocus(enable);
 
376
            webView()->setIsActive(enable);
 
377
            m_focusedWidget = 0;
 
378
        }
 
379
    }
 
380
}
 
381
 
 
382
void TestShell::testFinished()
 
383
{
 
384
    if (!m_testIsPending)
 
385
        return;
 
386
    m_testIsPending = false;
 
387
    if (m_dumpWhenFinished)
 
388
        dump();
 
389
    webkit_support::QuitMessageLoop();
 
390
}
 
391
 
 
392
void TestShell::testTimedOut()
 
393
{
 
394
    m_printer.handleTimedOut();
 
395
    testFinished();
 
396
}
 
397
 
 
398
static string dumpDocumentText(WebFrame* frame)
 
399
{
 
400
    // We use the document element's text instead of the body text here because
 
401
    // not all documents have a body, such as XML documents.
 
402
    WebElement documentElement = frame->document().documentElement();
 
403
    if (documentElement.isNull())
 
404
        return string();
 
405
    return documentElement.innerText().utf8();
 
406
}
 
407
 
 
408
static string dumpFramesAsText(WebFrame* frame, bool recursive)
 
409
{
 
410
    string result;
 
411
 
 
412
    // Add header for all but the main frame. Skip empty frames.
 
413
    if (frame->parent() && !frame->document().documentElement().isNull()) {
 
414
        result.append("\n--------\nFrame: '");
 
415
        result.append(frame->uniqueName().utf8().data());
 
416
        result.append("'\n--------\n");
 
417
    }
 
418
 
 
419
    result.append(dumpDocumentText(frame));
 
420
    result.append("\n");
 
421
 
 
422
    if (recursive) {
 
423
        for (WebFrame* child = frame->firstChild(); child; child = child->nextSibling())
 
424
            result.append(dumpFramesAsText(child, recursive));
 
425
    }
 
426
 
 
427
    return result;
 
428
}
 
429
 
 
430
static string dumpFramesAsPrintedText(WebFrame* frame, bool recursive)
 
431
{
 
432
    string result;
 
433
 
 
434
    // Cannot do printed format for anything other than HTML
 
435
    if (!frame->document().isHTMLDocument())
 
436
        return string();
 
437
 
 
438
    // Add header for all but the main frame. Skip empty frames.
 
439
    if (frame->parent() && !frame->document().documentElement().isNull()) {
 
440
        result.append("\n--------\nFrame: '");
 
441
        result.append(frame->uniqueName().utf8().data());
 
442
        result.append("'\n--------\n");
 
443
    }
 
444
 
 
445
    result.append(frame->renderTreeAsText(WebFrame::RenderAsTextPrinting).utf8());
 
446
    result.append("\n");
 
447
 
 
448
    if (recursive) {
 
449
        for (WebFrame* child = frame->firstChild(); child; child = child->nextSibling())
 
450
            result.append(dumpFramesAsPrintedText(child, recursive));
 
451
    }
 
452
 
 
453
    return result;
 
454
}
 
455
 
 
456
static void dumpFrameScrollPosition(WebFrame* frame, bool recursive)
 
457
{
 
458
    WebSize offset = frame->scrollOffset();
 
459
    if (offset.width > 0 || offset.height > 0) {
 
460
        if (frame->parent())
 
461
            printf("frame '%s' ", frame->uniqueName().utf8().data());
 
462
        printf("scrolled to %d,%d\n", offset.width, offset.height);
 
463
    }
 
464
 
 
465
    if (!recursive)
 
466
        return;
 
467
    for (WebFrame* child = frame->firstChild(); child; child = child->nextSibling())
 
468
        dumpFrameScrollPosition(child, recursive);
 
469
}
 
470
 
 
471
struct ToLower {
 
472
    char16 operator()(char16 c) { return tolower(c); }
 
473
};
 
474
 
 
475
// FIXME: Eliminate std::transform(), std::vector, and std::sort().
 
476
 
 
477
// Returns True if item1 < item2.
 
478
static bool HistoryItemCompareLess(const WebHistoryItem& item1, const WebHistoryItem& item2)
 
479
{
 
480
    string16 target1 = item1.target();
 
481
    string16 target2 = item2.target();
 
482
    std::transform(target1.begin(), target1.end(), target1.begin(), ToLower());
 
483
    std::transform(target2.begin(), target2.end(), target2.begin(), ToLower());
 
484
    return target1 < target2;
 
485
}
 
486
 
 
487
static string normalizeLayoutTestURLInternal(const string& url)
 
488
{
 
489
    string result = url;
 
490
    size_t pos;
 
491
    if (!url.find(fileUrlPattern) && ((pos = url.find(layoutTestsPattern)) != string::npos)) {
 
492
        // adjust file URLs to match upstream results.
 
493
        result.replace(0, pos + layoutTestsPatternSize, fileTestPrefix);
 
494
    } else if (!url.find(dataUrlPattern)) {
 
495
        // URL-escape data URLs to match results upstream.
 
496
        string path = url.substr(dataUrlPatternSize);
 
497
        result.replace(dataUrlPatternSize, url.length(), path);
 
498
    }
 
499
    return result;
 
500
}
 
501
 
 
502
static string dumpHistoryItem(const WebHistoryItem& item, int indent, bool isCurrent)
 
503
{
 
504
    string result;
 
505
 
 
506
    if (isCurrent) {
 
507
        result.append("curr->");
 
508
        result.append(indent - 6, ' '); // 6 == "curr->".length()
 
509
    } else
 
510
        result.append(indent, ' ');
 
511
 
 
512
    string url = normalizeLayoutTestURLInternal(item.urlString().utf8());
 
513
    result.append(url);
 
514
    if (!item.target().isEmpty()) {
 
515
        result.append(" (in frame \"");
 
516
        result.append(item.target().utf8());
 
517
        result.append("\")");
 
518
    }
 
519
    if (item.isTargetItem())
 
520
        result.append("  **nav target**");
 
521
    result.append("\n");
 
522
 
 
523
    const WebVector<WebHistoryItem>& children = item.children();
 
524
    if (!children.isEmpty()) {
 
525
        // Must sort to eliminate arbitrary result ordering which defeats
 
526
        // reproducible testing.
 
527
        // FIXME: WebVector should probably just be a std::vector!!
 
528
        std::vector<WebHistoryItem> sortedChildren;
 
529
        for (size_t i = 0; i < children.size(); ++i)
 
530
            sortedChildren.push_back(children[i]);
 
531
        std::sort(sortedChildren.begin(), sortedChildren.end(), HistoryItemCompareLess);
 
532
        for (size_t i = 0; i < sortedChildren.size(); ++i)
 
533
            result += dumpHistoryItem(sortedChildren[i], indent + 4, false);
 
534
    }
 
535
 
 
536
    return result;
 
537
}
 
538
 
 
539
static void dumpBackForwardList(const TestNavigationController& navigationController, string& result)
 
540
{
 
541
    result.append("\n============== Back Forward List ==============\n");
 
542
    for (int index = 0; index < navigationController.entryCount(); ++index) {
 
543
        int currentIndex = navigationController.lastCommittedEntryIndex();
 
544
        WebHistoryItem historyItem = navigationController.entryAtIndex(index)->contentState();
 
545
        if (historyItem.isNull()) {
 
546
            historyItem.initialize();
 
547
            historyItem.setURLString(navigationController.entryAtIndex(index)->URL().spec().utf16());
 
548
        }
 
549
        result.append(dumpHistoryItem(historyItem, 8, index == currentIndex));
 
550
    }
 
551
    result.append("===============================================\n");
 
552
}
 
553
 
 
554
string TestShell::dumpAllBackForwardLists()
 
555
{
 
556
    string result;
 
557
    for (unsigned i = 0; i < m_windowList.size(); ++i)
 
558
        dumpBackForwardList(*m_windowList[i]->navigationController(), result);
 
559
    return result;
 
560
}
 
561
 
 
562
void TestShell::dump()
 
563
{
 
564
    WebScriptController::flushConsoleMessages();
 
565
 
 
566
    // Dump the requested representation.
 
567
    WebFrame* frame = m_webView->mainFrame();
 
568
    if (!frame)
 
569
        return;
 
570
    bool shouldDumpAsText = m_testRunner->shouldDumpAsText();
 
571
    bool shouldDumpAsAudio = m_testRunner->shouldDumpAsAudio();
 
572
    bool shouldGeneratePixelResults = m_testRunner->shouldGeneratePixelResults();
 
573
    bool shouldDumpAsPrinted = m_testRunner->isPrinting();
 
574
    bool dumpedAnything = false;
 
575
 
 
576
    if (shouldDumpAsAudio) {
 
577
        const WebKit::WebArrayBufferView& webArrayBufferView = m_testRunner->audioData();
 
578
        m_printer.handleAudio(webArrayBufferView.baseAddress(), webArrayBufferView.byteLength());
 
579
        m_printer.handleAudioFooter();
 
580
        m_printer.handleTestFooter(true);
 
581
 
 
582
        fflush(stdout);
 
583
        fflush(stderr);
 
584
        return;
 
585
    }
 
586
 
 
587
    if (m_params.dumpTree) {
 
588
        dumpedAnything = true;
 
589
        m_printer.handleTextHeader();
 
590
        // Text output: the test page can request different types of output
 
591
        // which we handle here.
 
592
        if (!shouldDumpAsText) {
 
593
            // Plain text pages should be dumped as text
 
594
            string mimeType = frame->dataSource()->response().mimeType().utf8();
 
595
            if (mimeType == "text/plain") {
 
596
                shouldDumpAsText = true;
 
597
                shouldGeneratePixelResults = false;
 
598
            }
 
599
        }
 
600
        if (shouldDumpAsText) {
 
601
            bool recursive = m_testRunner->shouldDumpChildFramesAsText();
 
602
            string dataUtf8 = shouldDumpAsPrinted ? dumpFramesAsPrintedText(frame, recursive) : dumpFramesAsText(frame, recursive);
 
603
            if (fwrite(dataUtf8.c_str(), 1, dataUtf8.size(), stdout) != dataUtf8.size())
 
604
                FATAL("Short write to stdout, disk full?\n");
 
605
        } else {
 
606
          WebFrame::RenderAsTextControls renderTextBehavior = WebFrame::RenderAsTextNormal;
 
607
            if (shouldDumpAsPrinted)
 
608
                renderTextBehavior |= WebFrame::RenderAsTextPrinting;
 
609
            if (m_params.debugRenderTree)
 
610
                renderTextBehavior |= WebFrame::RenderAsTextDebug;
 
611
            printf("%s", frame->renderTreeAsText(renderTextBehavior).utf8().data());
 
612
            bool recursive = m_testRunner->shouldDumpChildFrameScrollPositions();
 
613
            dumpFrameScrollPosition(frame, recursive);
 
614
        }
 
615
        if (m_testRunner->shouldDumpBackForwardList())
 
616
            printf("%s", dumpAllBackForwardLists().c_str());
 
617
    }
 
618
    if (dumpedAnything && m_params.printSeparators)
 
619
        m_printer.handleTextFooter();
 
620
 
 
621
    if (m_dumpPixelsForCurrentTest && shouldGeneratePixelResults) {
 
622
        // Image output: we write the image data to the file given on the
 
623
        // command line (for the dump pixels argument), and the MD5 sum to
 
624
        // stdout.
 
625
        dumpedAnything = true;
 
626
        m_webView->layout();
 
627
        if (m_testRunner->testRepaint()) {
 
628
            WebSize viewSize = m_webView->size();
 
629
            int width = viewSize.width;
 
630
            int height = viewSize.height;
 
631
            if (m_testRunner->sweepHorizontally()) {
 
632
                for (WebRect column(0, 0, 1, height); column.x < width; column.x++)
 
633
                    m_webViewHost->paintRect(column);
 
634
            } else {
 
635
                for (WebRect line(0, 0, width, 1); line.y < height; line.y++)
 
636
                    m_webViewHost->paintRect(line);
 
637
            }
 
638
        } else if (m_testRunner->isPrinting())
 
639
            m_webViewHost->paintPagesWithBoundaries();
 
640
        else
 
641
            m_webViewHost->paintInvalidatedRegion();
 
642
 
 
643
        // See if we need to draw the selection bounds rect. Selection bounds
 
644
        // rect is the rect enclosing the (possibly transformed) selection.
 
645
        // The rect should be drawn after everything is laid out and painted.
 
646
        if (m_testRunner->shouldDumpSelectionRect()) {
 
647
            // If there is a selection rect - draw a red 1px border enclosing rect
 
648
            WebRect wr = frame->selectionBoundsRect();
 
649
            if (!wr.isEmpty()) {
 
650
                // Render a red rectangle bounding selection rect
 
651
                SkPaint paint;
 
652
                paint.setColor(0xFFFF0000); // Fully opaque red
 
653
                paint.setStyle(SkPaint::kStroke_Style);
 
654
                paint.setFlags(SkPaint::kAntiAlias_Flag);
 
655
                paint.setStrokeWidth(1.0f);
 
656
                SkIRect rect; // Bounding rect
 
657
                rect.set(wr.x, wr.y, wr.x + wr.width, wr.y + wr.height);
 
658
                m_webViewHost->canvas()->drawIRect(rect, paint);
 
659
            }
 
660
        }
 
661
 
 
662
        dumpImage(m_webViewHost->canvas());
 
663
    }
 
664
    m_printer.handleTestFooter(dumpedAnything);
 
665
    fflush(stdout);
 
666
    fflush(stderr);
 
667
}
 
668
 
 
669
void TestShell::dumpImage(SkCanvas* canvas) const
 
670
{
 
671
    // Fix the alpha. The expected PNGs on Mac have an alpha channel, so we want
 
672
    // to keep it. On Windows, the alpha channel is wrong since text/form control
 
673
    // drawing may have erased it in a few places. So on Windows we force it to
 
674
    // opaque and also don't write the alpha channel for the reference. Linux
 
675
    // doesn't have the wrong alpha like Windows, but we match Windows.
 
676
#if OS(MAC_OS_X)
 
677
    bool discardTransparency = false;
 
678
#else
 
679
    bool discardTransparency = true;
 
680
    makeCanvasOpaque(canvas);
 
681
#endif
 
682
 
 
683
    const SkBitmap& sourceBitmap = canvas->getTopDevice()->accessBitmap(false);
 
684
    SkAutoLockPixels sourceBitmapLock(sourceBitmap);
 
685
 
 
686
    // Compute MD5 sum.
 
687
    MD5 digester;
 
688
    Vector<uint8_t, 16> digestValue;
 
689
#if OS(ANDROID)
 
690
    // On Android, pixel layout is RGBA (see third_party/skia/include/core/SkColorPriv.h);
 
691
    // however, other Chrome platforms use BGRA (see skia/config/SkUserConfig.h).
 
692
    // To match the checksum of other Chrome platforms, we need to reorder the layout of pixels.
 
693
    // NOTE: The following code assumes we use SkBitmap::kARGB_8888_Config,
 
694
    // which has been checked in device.makeOpaque() (see above).
 
695
    const uint8_t* rawPixels = reinterpret_cast<const uint8_t*>(sourceBitmap.getPixels());
 
696
    size_t bitmapSize = sourceBitmap.getSize();
 
697
    OwnArrayPtr<uint8_t> reorderedPixels = adoptArrayPtr(new uint8_t[bitmapSize]);
 
698
    for (size_t i = 0; i < bitmapSize; i += 4) {
 
699
        reorderedPixels[i] = rawPixels[i + 2]; // R
 
700
        reorderedPixels[i + 1] = rawPixels[i + 1]; // G
 
701
        reorderedPixels[i + 2] = rawPixels[i]; // B
 
702
        reorderedPixels[i + 3] = rawPixels[i + 3]; // A
 
703
    }
 
704
    digester.addBytes(reorderedPixels.get(), bitmapSize);
 
705
    reorderedPixels.clear();
 
706
#else
 
707
    digester.addBytes(reinterpret_cast<const uint8_t*>(sourceBitmap.getPixels()), sourceBitmap.getSize());
 
708
#endif
 
709
    digester.checksum(digestValue);
 
710
    string md5hash;
 
711
    md5hash.reserve(16 * 2);
 
712
    for (unsigned i = 0; i < 16; ++i) {
 
713
        char hex[3];
 
714
        // Use "x", not "X". The string must be lowercased.
 
715
        sprintf(hex, "%02x", digestValue[i]);
 
716
        md5hash.append(hex);
 
717
    }
 
718
 
 
719
    // Only encode and dump the png if the hashes don't match. Encoding the
 
720
    // image is really expensive.
 
721
    if (md5hash.compare(m_params.pixelHash)) {
 
722
        std::vector<unsigned char> png;
 
723
#if OS(ANDROID)
 
724
        webkit_support::EncodeRGBAPNGWithChecksum(reinterpret_cast<const unsigned char*>(sourceBitmap.getPixels()), sourceBitmap.width(),
 
725
            sourceBitmap.height(), static_cast<int>(sourceBitmap.rowBytes()), discardTransparency, md5hash, &png);
 
726
#else
 
727
        webkit_support::EncodeBGRAPNGWithChecksum(reinterpret_cast<const unsigned char*>(sourceBitmap.getPixels()), sourceBitmap.width(),
 
728
            sourceBitmap.height(), static_cast<int>(sourceBitmap.rowBytes()), discardTransparency, md5hash, &png);
 
729
#endif
 
730
 
 
731
        m_printer.handleImage(md5hash.c_str(), m_params.pixelHash.c_str(), &png[0], png.size());
 
732
    } else
 
733
        m_printer.handleImage(md5hash.c_str(), m_params.pixelHash.c_str(), 0, 0);
 
734
}
 
735
 
 
736
void TestShell::bindJSObjectsToWindow(WebFrame* frame)
 
737
{
 
738
    WebTestingSupport::injectInternalsObject(frame);
 
739
    m_testInterfaces->bindTo(frame);
 
740
    m_testRunner->bindToJavascript(frame, WebString::fromUTF8("testRunner"));
 
741
    m_testRunner->bindToJavascript(frame, WebString::fromUTF8("layoutTestController"));
 
742
}
 
743
 
 
744
WebViewHost* TestShell::createNewWindow(const WebKit::WebURL& url)
 
745
{
 
746
    return createNewWindow(url, 0);
 
747
}
 
748
 
 
749
WebViewHost* TestShell::createNewWindow(const WebKit::WebURL& url, DRTDevToolsAgent* devToolsAgent)
 
750
{
 
751
    WebTestRunner::WebTestProxy<WebViewHost, TestShell*>* host = new WebTestRunner::WebTestProxy<WebViewHost, TestShell*>(this);
 
752
    host->setInterfaces(m_testInterfaces.get());
 
753
    if (m_webViewHost)
 
754
        host->setDelegate(m_webViewHost.get());
 
755
    else
 
756
        host->setDelegate(host);
 
757
    host->setProxy(host);
 
758
    WebView* view = WebView::create(host);
 
759
    view->setPermissionClient(webPermissions());
 
760
    view->setDevToolsAgentClient(devToolsAgent);
 
761
    host->setWebWidget(view);
 
762
    m_prefs.applyTo(view);
 
763
    view->initializeMainFrame(host);
 
764
    m_windowList.append(host);
 
765
    host->loadURLForFrame(url, WebString());
 
766
    return host;
 
767
}
 
768
 
 
769
void TestShell::closeWindow(WebViewHost* window)
 
770
{
 
771
    size_t i = m_windowList.find(window);
 
772
    if (i == notFound) {
 
773
        ASSERT_NOT_REACHED();
 
774
        return;
 
775
    }
 
776
    m_windowList.remove(i);
 
777
    WebWidget* focusedWidget = m_focusedWidget;
 
778
    if (window->webWidget() == m_focusedWidget)
 
779
        focusedWidget = 0;
 
780
 
 
781
    delete window;
 
782
    // We set the focused widget after deleting the web view host because it
 
783
    // can change the focus.
 
784
    m_focusedWidget = focusedWidget;
 
785
    if (m_focusedWidget) {
 
786
        webView()->setIsActive(true);
 
787
        m_focusedWidget->setFocus(true);
 
788
    }
 
789
}
 
790
 
 
791
void TestShell::closeRemainingWindows()
 
792
{
 
793
    // Just close devTools window manually because we have custom deinitialization code for it.
 
794
    closeDevTools();
 
795
 
 
796
    // Iterate through the window list and close everything except the main
 
797
    // window. We don't want to delete elements as we're iterating, so we copy
 
798
    // to a temp vector first.
 
799
    Vector<WebViewHost*> windowsToDelete;
 
800
    for (unsigned i = 0; i < m_windowList.size(); ++i) {
 
801
        if (m_windowList[i] != webViewHost())
 
802
            windowsToDelete.append(m_windowList[i]);
 
803
    }
 
804
    ASSERT(windowsToDelete.size() + 1 == m_windowList.size());
 
805
    for (unsigned i = 0; i < windowsToDelete.size(); ++i)
 
806
        closeWindow(windowsToDelete[i]);
 
807
    ASSERT(m_windowList.size() == 1);
 
808
}
 
809
 
 
810
int TestShell::windowCount()
 
811
{
 
812
    return m_windowList.size();
 
813
}
 
814
 
 
815
string TestShell::normalizeLayoutTestURL(const string& url)
 
816
{
 
817
    return normalizeLayoutTestURLInternal(url);
 
818
}