[chromium] Remove compositor settings that don't go through WebKit
[WebKit-https.git] / Tools / DumpRenderTree / chromium / TestShell.cpp
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 "MockPlatform.h"
37 #include "MockWebPrerenderingSupport.h"
38 #include "WebArrayBufferView.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 "WebSettings.h"
47 #include "WebTestProxy.h"
48 #include "WebTestRunner.h"
49 #include "WebView.h"
50 #include "WebViewHost.h"
51 #include "skia/ext/platform_canvas.h"
52 #include "webkit/support/webkit_support.h"
53 #include "webkit/support/webkit_support_gfx.h"
54 #include <public/Platform.h>
55 #include <public/WebCompositorSupport.h>
56 #include <public/WebPoint.h>
57 #include <public/WebSize.h>
58 #include <public/WebString.h>
59 #include <public/WebThread.h>
60 #include <public/WebURLRequest.h>
61 #include <public/WebURLResponse.h>
62 #include <algorithm>
63 #include <cctype>
64 #include <vector>
65 #include <wtf/MD5.h>
66 #include <wtf/OwnArrayPtr.h>
67
68
69 using namespace WebKit;
70 using namespace WebTestRunner;
71 using namespace std;
72
73 // Content area size for newly created windows.
74 static const int testWindowWidth = 800;
75 static const int testWindowHeight = 600;
76
77 // The W3C SVG layout tests use a different size than the other layout tests.
78 static const int SVGTestWindowWidth = 480;
79 static const int SVGTestWindowHeight = 360;
80
81 static const char layoutTestsPattern[] = "/LayoutTests/";
82 static const string::size_type layoutTestsPatternSize = sizeof(layoutTestsPattern) - 1;
83 static const char fileUrlPattern[] = "file:/";
84 static const char fileTestPrefix[] = "(file test):";
85 static const char dataUrlPattern[] = "data:";
86 static const string::size_type dataUrlPatternSize = sizeof(dataUrlPattern) - 1;
87
88 // FIXME: Move this to a common place so that it can be shared with
89 // WebCore::TransparencyWin::makeLayerOpaque().
90 static void makeCanvasOpaque(SkCanvas* canvas)
91 {
92     const SkBitmap& bitmap = canvas->getTopDevice()->accessBitmap(true);
93     ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config);
94
95     SkAutoLockPixels lock(bitmap);
96     for (int y = 0; y < bitmap.height(); y++) {
97         uint32_t* row = bitmap.getAddr32(0, y);
98         for (int x = 0; x < bitmap.width(); x++)
99             row[x] |= 0xFF000000; // Set alpha bits to 1.
100     }
101 }
102
103 TestShell::TestShell()
104     : m_testIsPending(false)
105     , m_testIsPreparing(false)
106     , m_focusedWidget(0)
107     , m_devTools(0)
108     , m_dumpPixelsForCurrentTest(false)
109     , m_allowExternalPages(false)
110     , m_acceleratedCompositingForVideoEnabled(false)
111     , m_acceleratedCompositingForFixedPositionEnabled(false)
112     , m_acceleratedCompositingForOverflowScrollEnabled(false)
113     , m_softwareCompositingEnabled(false)
114     , m_threadedCompositingEnabled(false)
115     , m_forceCompositingMode(false)
116     , m_threadedHTMLParser(true)
117     , m_accelerated2dCanvasEnabled(false)
118     , m_deferred2dCanvasEnabled(false)
119     , m_perTilePaintingEnabled(false)
120     , m_deferredImageDecodingEnabled(false)
121     , m_stressOpt(false)
122     , m_stressDeopt(false)
123     , m_dumpWhenFinished(true)
124     , m_isDisplayingModalDialog(false)
125 {
126     // 30 second is the same as the value in Mac DRT.
127     // If we use a value smaller than the timeout value of
128     // (new-)run-webkit-tests, (new-)run-webkit-tests misunderstands that a
129     // timed-out DRT process was crashed.
130     m_timeout = 30 * 1000;
131 }
132
133 void TestShell::initialize(MockPlatform* platformSupport)
134 {
135     m_testInterfaces = adoptPtr(new WebTestInterfaces());
136     platformSupport->setInterfaces(m_testInterfaces.get());
137     m_devToolsTestInterfaces = adoptPtr(new WebTestInterfaces());
138 #if ENABLE(LINK_PRERENDER)
139     m_prerenderingSupport = adoptPtr(new MockWebPrerenderingSupport());
140 #endif
141
142     WTF::initializeThreading();
143
144     if (m_threadedCompositingEnabled)
145         m_webCompositorThread = adoptPtr(WebKit::Platform::current()->createThread("Compositor"));
146     webkit_support::SetThreadedCompositorEnabled(m_threadedCompositingEnabled);
147
148     createMainWindow();
149 }
150
151 void TestShell::createMainWindow()
152 {
153     m_drtDevToolsAgent = adoptPtr(new DRTDevToolsAgent);
154     m_webViewHost = adoptPtr(createNewWindow(WebURL(), m_drtDevToolsAgent.get(), m_testInterfaces.get()));
155     m_webView = m_webViewHost->webView();
156     m_testInterfaces->setDelegate(m_webViewHost.get());
157     m_testInterfaces->setWebView(m_webView, m_webViewHost->proxy());
158     m_drtDevToolsAgent->setWebView(m_webView);
159 }
160
161 TestShell::~TestShell()
162 {
163     if (m_webViewHost)
164         m_webViewHost->shutdown();
165     m_testInterfaces->setDelegate(0);
166     m_testInterfaces->setWebView(0, 0);
167     m_devToolsTestInterfaces->setDelegate(0);
168     m_devToolsTestInterfaces->setWebView(0, 0);
169     m_drtDevToolsAgent->setWebView(0);
170 }
171
172 void TestShell::createDRTDevToolsClient(DRTDevToolsAgent* agent)
173 {
174     m_drtDevToolsClient = adoptPtr(new DRTDevToolsClient(agent, m_devTools->webView()));
175 }
176
177 void TestShell::showDevTools()
178 {
179     if (!m_devTools) {
180         WebURL url = webkit_support::GetDevToolsPathAsURL();
181         if (!url.isValid()) {
182             ASSERT(false);
183             return;
184         }
185         m_devTools = createNewWindow(url, 0, m_devToolsTestInterfaces.get());
186         m_devTools->webView()->settings()->setMemoryInfoEnabled(true);
187         m_devTools->proxy()->setLogConsoleOutput(false);
188         m_devToolsTestInterfaces->setDelegate(m_devTools);
189         m_devToolsTestInterfaces->setWebView(m_devTools->webView(), m_devTools->proxy());
190         ASSERT(m_devTools);
191         createDRTDevToolsClient(m_drtDevToolsAgent.get());
192     }
193     m_devTools->show(WebKit::WebNavigationPolicyNewWindow);
194 }
195
196 void TestShell::closeDevTools()
197 {
198     if (m_devTools) {
199         m_devTools->webView()->settings()->setMemoryInfoEnabled(false);
200         m_drtDevToolsAgent->reset();
201         m_drtDevToolsClient.clear();
202         m_devToolsTestInterfaces->setDelegate(0);
203         m_devToolsTestInterfaces->setWebView(0, 0);
204         closeWindow(m_devTools);
205         m_devTools = 0;
206     }
207 }
208
209 void TestShell::resetWebSettings(WebView& webView)
210 {
211     m_prefs.reset();
212     m_prefs.acceleratedCompositingEnabled = true;
213     m_prefs.acceleratedCompositingForVideoEnabled = m_acceleratedCompositingForVideoEnabled;
214     m_prefs.acceleratedCompositingForFixedPositionEnabled = m_acceleratedCompositingForFixedPositionEnabled;
215     m_prefs.acceleratedCompositingForOverflowScrollEnabled = m_acceleratedCompositingForOverflowScrollEnabled;
216     m_prefs.forceCompositingMode = m_forceCompositingMode;
217     m_prefs.accelerated2dCanvasEnabled = m_accelerated2dCanvasEnabled;
218     m_prefs.deferred2dCanvasEnabled = m_deferred2dCanvasEnabled;
219     m_prefs.perTilePaintingEnabled = m_perTilePaintingEnabled;
220     m_prefs.deferredImageDecodingEnabled = m_deferredImageDecodingEnabled;
221     m_prefs.threadedHTMLParser = m_threadedHTMLParser;
222     m_prefs.applyTo(&webView);
223 }
224
225 void TestShell::runFileTest(const TestParams& params, bool shouldDumpPixels)
226 {
227     ASSERT(params.testUrl.isValid());
228     m_dumpPixelsForCurrentTest = shouldDumpPixels;
229     m_testIsPreparing = true;
230     m_testInterfaces->setTestIsRunning(true);
231     m_params = params;
232     string testUrl = m_params.testUrl.spec();
233     m_testInterfaces->configureForTestWithURL(m_params.testUrl, shouldDumpPixels);
234
235     if (testUrl.find("compositing/") != string::npos || testUrl.find("compositing\\") != string::npos) {
236         if (!m_softwareCompositingEnabled)
237             m_prefs.accelerated2dCanvasEnabled = true;
238         m_prefs.acceleratedCompositingForVideoEnabled = true;
239         m_prefs.deferred2dCanvasEnabled = true;
240         m_prefs.mockScrollbarsEnabled = true;
241         m_prefs.applyTo(m_webView);
242     }
243
244     if (m_dumpWhenFinished)
245         m_printer.handleTestHeader(testUrl.c_str());
246     loadURL(m_params.testUrl);
247
248     if (m_devTools)
249         this->setFocus(m_devTools->webView(), true);
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_testInterfaces->resetAll();
277     m_devToolsTestInterfaces->resetAll();
278     m_webViewHost->reset();
279 #if OS(ANDROID)
280     webkit_support::ReleaseMediaResources();
281 #endif
282     m_drtDevToolsAgent->reset();
283     if (m_drtDevToolsClient)
284         m_drtDevToolsClient->reset();
285     webView()->setPageScaleFactor(1, WebPoint(0, 0));
286     webView()->enableFixedLayoutMode(false);
287     webView()->setFixedLayoutSize(WebSize(0, 0));
288     webView()->mainFrame()->clearOpener();
289     WebTestingSupport::resetInternalsObject(webView()->mainFrame());
290 }
291
292 void TestShell::loadURL(const WebURL& url)
293 {
294     m_webViewHost->loadURLForFrame(url, string());
295 }
296
297 void TestShell::reload()
298 {
299     m_webViewHost->navigationController()->reload();
300 }
301
302 void TestShell::goToOffset(int offset)
303 {
304      m_webViewHost->navigationController()->goToOffset(offset);
305 }
306
307 int TestShell::navigationEntryCount() const
308 {
309     return m_webViewHost->navigationController()->entryCount();
310 }
311
312 void TestShell::callJSGC()
313 {
314     m_webView->mainFrame()->collectGarbage();
315 }
316
317 void TestShell::setFocus(WebWidget* widget, bool enable)
318 {
319     // Simulate the effects of InteractiveSetFocus(), which includes calling
320     // both setFocus() and setIsActive().
321     if (enable) {
322         if (m_focusedWidget != widget) {
323             if (m_focusedWidget)
324                 m_focusedWidget->setFocus(false);
325             webView()->setIsActive(enable);
326             widget->setFocus(enable);
327             m_focusedWidget = widget;
328         }
329     } else {
330         if (m_focusedWidget == widget) {
331             widget->setFocus(enable);
332             webView()->setIsActive(enable);
333             m_focusedWidget = 0;
334         }
335     }
336 }
337
338 void TestShell::testFinished(WebViewHost* host)
339 {
340     if (host == m_devTools)
341         return;
342
343     if (!m_testIsPending)
344         return;
345     m_testIsPending = false;
346     m_testInterfaces->setTestIsRunning(false);
347     if (m_dumpWhenFinished)
348         dump();
349     webkit_support::QuitMessageLoop();
350 }
351
352 void TestShell::testTimedOut()
353 {
354     m_printer.handleTimedOut();
355     testFinished(webViewHost());
356 }
357
358 void TestShell::dump()
359 {
360     // Dump the requested representation.
361     WebFrame* frame = m_webView->mainFrame();
362     if (!frame)
363         return;
364     bool shouldDumpAsAudio = m_testInterfaces->testRunner()->shouldDumpAsAudio();
365     bool shouldGeneratePixelResults = m_testInterfaces->testRunner()->shouldGeneratePixelResults();
366     bool dumpedAnything = false;
367
368     if (shouldDumpAsAudio) {
369         const WebKit::WebArrayBufferView* webArrayBufferView = m_testInterfaces->testRunner()->audioData();
370         m_printer.handleAudio(webArrayBufferView->baseAddress(), webArrayBufferView->byteLength());
371         m_printer.handleAudioFooter();
372         m_printer.handleTestFooter(true);
373
374         fflush(stdout);
375         fflush(stderr);
376         return;
377     }
378
379     if (m_params.dumpTree) {
380         dumpedAnything = true;
381         m_printer.handleTextHeader();
382         string dataUtf8 = m_webViewHost->proxy()->captureTree(m_params.debugRenderTree);
383         if (fwrite(dataUtf8.c_str(), 1, dataUtf8.size(), stdout) != dataUtf8.size())
384             FATAL("Short write to stdout, disk full?\n");
385     }
386     if (dumpedAnything && m_params.printSeparators)
387         m_printer.handleTextFooter();
388
389     if (m_dumpPixelsForCurrentTest && shouldGeneratePixelResults) {
390         // Image output: we write the image data to the file given on the
391         // command line (for the dump pixels argument), and the MD5 sum to
392         // stdout.
393         dumpedAnything = true;
394         dumpImage(m_webViewHost->proxy()->capturePixels());
395     }
396     m_printer.handleTestFooter(dumpedAnything);
397     fflush(stdout);
398     fflush(stderr);
399 }
400
401 void TestShell::dumpImage(SkCanvas* canvas) const
402 {
403     // Fix the alpha. The expected PNGs on Mac have an alpha channel, so we want
404     // to keep it. On Windows, the alpha channel is wrong since text/form control
405     // drawing may have erased it in a few places. So on Windows we force it to
406     // opaque and also don't write the alpha channel for the reference. Linux
407     // doesn't have the wrong alpha like Windows, but we match Windows.
408 #if OS(MAC_OS_X)
409     bool discardTransparency = false;
410 #else
411     bool discardTransparency = true;
412     makeCanvasOpaque(canvas);
413 #endif
414
415     const SkBitmap& sourceBitmap = canvas->getTopDevice()->accessBitmap(false);
416     SkAutoLockPixels sourceBitmapLock(sourceBitmap);
417
418     // Compute MD5 sum.
419     MD5 digester;
420     Vector<uint8_t, 16> digestValue;
421 #if OS(ANDROID)
422     // On Android, pixel layout is RGBA (see third_party/skia/include/core/SkColorPriv.h);
423     // however, other Chrome platforms use BGRA (see skia/config/SkUserConfig.h).
424     // To match the checksum of other Chrome platforms, we need to reorder the layout of pixels.
425     // NOTE: The following code assumes we use SkBitmap::kARGB_8888_Config,
426     // which has been checked in device.makeOpaque() (see above).
427     const uint8_t* rawPixels = reinterpret_cast<const uint8_t*>(sourceBitmap.getPixels());
428     size_t bitmapSize = sourceBitmap.getSize();
429     OwnArrayPtr<uint8_t> reorderedPixels = adoptArrayPtr(new uint8_t[bitmapSize]);
430     for (size_t i = 0; i < bitmapSize; i += 4) {
431         reorderedPixels[i] = rawPixels[i + 2]; // R
432         reorderedPixels[i + 1] = rawPixels[i + 1]; // G
433         reorderedPixels[i + 2] = rawPixels[i]; // B
434         reorderedPixels[i + 3] = rawPixels[i + 3]; // A
435     }
436     digester.addBytes(reorderedPixels.get(), bitmapSize);
437     reorderedPixels.clear();
438 #else
439     digester.addBytes(reinterpret_cast<const uint8_t*>(sourceBitmap.getPixels()), sourceBitmap.getSize());
440 #endif
441     digester.checksum(digestValue);
442     string md5hash;
443     md5hash.reserve(16 * 2);
444     for (unsigned i = 0; i < 16; ++i) {
445         char hex[3];
446         // Use "x", not "X". The string must be lowercased.
447         sprintf(hex, "%02x", digestValue[i]);
448         md5hash.append(hex);
449     }
450
451     // Only encode and dump the png if the hashes don't match. Encoding the
452     // image is really expensive.
453     if (md5hash.compare(m_params.pixelHash)) {
454         std::vector<unsigned char> png;
455 #if OS(ANDROID)
456         webkit_support::EncodeRGBAPNGWithChecksum(reinterpret_cast<const unsigned char*>(sourceBitmap.getPixels()), sourceBitmap.width(),
457             sourceBitmap.height(), static_cast<int>(sourceBitmap.rowBytes()), discardTransparency, md5hash, &png);
458 #else
459         webkit_support::EncodeBGRAPNGWithChecksum(reinterpret_cast<const unsigned char*>(sourceBitmap.getPixels()), sourceBitmap.width(),
460             sourceBitmap.height(), static_cast<int>(sourceBitmap.rowBytes()), discardTransparency, md5hash, &png);
461 #endif
462
463         m_printer.handleImage(md5hash.c_str(), m_params.pixelHash.c_str(), &png[0], png.size());
464     } else
465         m_printer.handleImage(md5hash.c_str(), m_params.pixelHash.c_str(), 0, 0);
466 }
467
468 void TestShell::bindJSObjectsToWindow(WebFrame* frame)
469 {
470     WebTestingSupport::injectInternalsObject(frame);
471     if (m_devTools && m_devTools->webView() == frame->view())
472         m_devToolsTestInterfaces->bindTo(frame);
473     else
474         m_testInterfaces->bindTo(frame);
475 }
476
477 WebViewHost* TestShell::createNewWindow(const WebKit::WebURL& url)
478 {
479     return createNewWindow(url, 0, m_testInterfaces.get());
480 }
481
482 WebViewHost* TestShell::createNewWindow(const WebKit::WebURL& url, DRTDevToolsAgent* devToolsAgent, WebTestInterfaces *testInterfaces)
483 {
484     WebTestProxy<WebViewHost, TestShell*>* host = new WebTestProxy<WebViewHost, TestShell*>(this);
485     host->setInterfaces(testInterfaces);
486     if (m_webViewHost)
487         host->setDelegate(m_webViewHost.get());
488     else
489         host->setDelegate(host);
490     host->setProxy(host);
491     WebView* view = WebView::create(host);
492     view->setPermissionClient(testInterfaces->testRunner()->webPermissions());
493     view->setDevToolsAgentClient(devToolsAgent);
494     host->setWebWidget(view);
495     m_prefs.applyTo(view);
496     view->initializeMainFrame(host);
497     m_windowList.append(host);
498     host->loadURLForFrame(url, string());
499     return host;
500 }
501
502 void TestShell::closeWindow(WebViewHost* window)
503 {
504     size_t i = m_windowList.find(window);
505     if (i == notFound) {
506         ASSERT_NOT_REACHED();
507         return;
508     }
509     m_windowList.remove(i);
510     WebWidget* focusedWidget = m_focusedWidget;
511     if (window->webWidget() == m_focusedWidget)
512         focusedWidget = 0;
513
514     window->shutdown();
515     delete window;
516     // We set the focused widget after deleting the web view host because it
517     // can change the focus.
518     m_focusedWidget = focusedWidget;
519     if (m_focusedWidget) {
520         webView()->setIsActive(true);
521         m_focusedWidget->setFocus(true);
522     }
523 }
524
525 void TestShell::closeRemainingWindows()
526 {
527     // Just close devTools window manually because we have custom deinitialization code for it.
528     closeDevTools();
529
530     // Iterate through the window list and close everything except the main
531     // window. We don't want to delete elements as we're iterating, so we copy
532     // to a temp vector first.
533     Vector<WebViewHost*> windowsToDelete;
534     for (unsigned i = 0; i < m_windowList.size(); ++i) {
535         if (m_windowList[i] != webViewHost())
536             windowsToDelete.append(m_windowList[i]);
537     }
538     ASSERT(windowsToDelete.size() + 1 == m_windowList.size());
539     for (unsigned i = 0; i < windowsToDelete.size(); ++i)
540         closeWindow(windowsToDelete[i]);
541     ASSERT(m_windowList.size() == 1);
542 }
543
544 int TestShell::windowCount()
545 {
546     return m_windowList.size();
547 }
548
549 void TestShell::captureHistoryForWindow(size_t windowIndex, WebVector<WebHistoryItem>* history, size_t* currentEntryIndex)
550 {
551     ASSERT(history);
552     ASSERT(currentEntryIndex);
553     if (windowIndex >= m_windowList.size())
554         return;
555     TestNavigationController& navigationController = *m_windowList[windowIndex]->navigationController();
556     size_t entryCount = navigationController.entryCount();
557     WebVector<WebHistoryItem> result(entryCount);
558     *currentEntryIndex = navigationController.lastCommittedEntryIndex();
559     for (size_t index = 0; index < entryCount; ++index) {
560         WebHistoryItem historyItem = navigationController.entryAtIndex(index)->contentState();
561         if (historyItem.isNull()) {
562             historyItem.initialize();
563             historyItem.setURLString(navigationController.entryAtIndex(index)->URL().spec().utf16());
564         }
565         result[index] = historyItem;
566     }
567     history->swap(result);
568 }