[Qt] Remove the fix in QWebPage::javaScriptConsoleMessage introduced by (r61433)
[WebKit-https.git] / Tools / DumpRenderTree / qt / DumpRenderTreeQt.cpp
1 /*
2  * Copyright (C) 2005, 2006 Apple Computer, Inc.  All rights reserved.
3  * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
4  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
5  * Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1.  Redistributions of source code must retain the above copyright
12  *     notice, this list of conditions and the following disclaimer.
13  * 2.  Redistributions in binary form must reproduce the above copyright
14  *     notice, this list of conditions and the following disclaimer in the
15  *     documentation and/or other materials provided with the distribution.
16  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
17  *     its contributors may be used to endorse or promote products derived
18  *     from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
21  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
24  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include "config.h"
33 #include "DumpRenderTree.h"
34
35 #include "DumpRenderTreeQt.h"
36 #include "DumpRenderTreeSupportQt.h"
37 #include "EventSenderQt.h"
38 #include "GCController.h"
39 #include "InitWebCoreQt.h"
40 #include "InitWebKitQt.h"
41 #include "JSStringRefQt.h"
42 #include "QtTestSupport.h"
43 #include "TestRunner.h"
44 #include "TestRunnerQt.h"
45 #include "TextInputControllerQt.h"
46 #include "testplugin.h"
47 #include "WorkQueue.h"
48
49 #include <QApplication>
50 #include <QBuffer>
51 #include <QCryptographicHash>
52 #include <QDir>
53 #include <QFile>
54 #include <QFileInfo>
55 #include <QFocusEvent>
56 #include <QLabel>
57 #include <QLocale>
58 #include <QNetworkAccessManager>
59 #include <QNetworkReply>
60 #include <QNetworkRequest>
61 #include <QPaintDevice>
62 #include <QPaintEngine>
63 #if !defined(QT_NO_PRINTER) && HAVE(QTPRINTSUPPORT)
64 #include <QPrinter>
65 #endif
66 #include <QProgressBar>
67 #include <QUndoStack>
68 #include <QUrl>
69 #include <limits.h>
70 #include <locale.h>
71 #include <qwebsecurityorigin.h>
72 #include <qwebsettings.h>
73 #ifndef Q_OS_WIN
74 #include <unistd.h>
75 #endif
76
77 using namespace WebCore;
78
79 const int databaseDefaultQuota = 5 * 1024 * 1024;
80
81 NetworkAccessManager::NetworkAccessManager(QObject* parent)
82     : QNetworkAccessManager(parent)
83 {
84 #ifndef QT_NO_OPENSSL
85     connect(this, SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError>&)),
86             this, SLOT(sslErrorsEncountered(QNetworkReply*, const QList<QSslError>&)));
87 #endif
88 }
89
90 #ifndef QT_NO_OPENSSL
91 void NetworkAccessManager::sslErrorsEncountered(QNetworkReply* reply, const QList<QSslError>& errors)
92 {
93     if (reply->url().host() == "127.0.0.1" || reply->url().host() == "localhost") {
94         bool ignore = true;
95
96         // Accept any HTTPS certificate.
97         foreach (const QSslError& error, errors) {
98             if (error.error() < QSslError::UnableToGetIssuerCertificate || error.error() > QSslError::HostNameMismatch) {
99                 ignore = false;
100                 break;
101             }
102         }
103
104         if (ignore)
105             reply->ignoreSslErrors();
106     }
107 }
108 #endif
109
110
111 #if !defined(QT_NO_PRINTER) && HAVE(QTPRINTSUPPORT)
112 class NullPrinter : public QPrinter {
113 public:
114     class NullPaintEngine : public QPaintEngine {
115     public:
116         virtual bool begin(QPaintDevice*) { return true; }
117         virtual bool end() { return true; }
118         virtual QPaintEngine::Type type() const { return QPaintEngine::User; }
119         virtual void drawPixmap(const QRectF& r, const QPixmap& pm, const QRectF& sr) { }
120         virtual void updateState(const QPaintEngineState& state) { }
121     };
122
123     virtual QPaintEngine* paintEngine() const { return const_cast<NullPaintEngine*>(&m_engine); }
124
125     NullPaintEngine m_engine;
126 };
127 #endif
128
129 WebPage::WebPage(QObject* parent, DumpRenderTree* drt)
130     : QWebPage(parent)
131     , m_webInspector(0)
132     , m_drt(drt)
133 {
134     QWebSettings* globalSettings = QWebSettings::globalSettings();
135
136     globalSettings->setFontSize(QWebSettings::MinimumFontSize, 0);
137     globalSettings->setFontSize(QWebSettings::MinimumLogicalFontSize, 5);
138     globalSettings->setFontSize(QWebSettings::DefaultFontSize, 16);
139     globalSettings->setFontSize(QWebSettings::DefaultFixedFontSize, 13);
140
141     globalSettings->setAttribute(QWebSettings::JavascriptCanOpenWindows, true);
142     globalSettings->setAttribute(QWebSettings::JavascriptCanAccessClipboard, true);
143     globalSettings->setAttribute(QWebSettings::LinksIncludedInFocusChain, false);
144     globalSettings->setAttribute(QWebSettings::PluginsEnabled, true);
145     globalSettings->setAttribute(QWebSettings::LocalContentCanAccessRemoteUrls, true);
146     globalSettings->setAttribute(QWebSettings::JavascriptEnabled, true);
147     globalSettings->setAttribute(QWebSettings::PrivateBrowsingEnabled, false);
148     globalSettings->setAttribute(QWebSettings::SpatialNavigationEnabled, false);
149
150     connect(this, SIGNAL(geometryChangeRequested(const QRect &)),
151             this, SLOT(setViewGeometry(const QRect & )));
152
153     setNetworkAccessManager(m_drt->networkAccessManager());
154     setPluginFactory(new TestPlugin(this));
155
156     connect(this, SIGNAL(featurePermissionRequested(QWebFrame*, QWebPage::Feature)), this, SLOT(requestPermission(QWebFrame*, QWebPage::Feature)));
157     connect(this, SIGNAL(featurePermissionRequestCanceled(QWebFrame*, QWebPage::Feature)), this, SLOT(cancelPermission(QWebFrame*, QWebPage::Feature)));
158 }
159
160 WebPage::~WebPage()
161 {
162     // Load an empty url to send the onunload event to the running page before
163     // deleting this instance.
164     // Prior to this fix the onunload event would be triggered from '~QWebPage', but
165     // it may call virtual functions (e.g. calling a window.alert from window.onunload)
166     // of 'QWebPage' as the 'WebPage' part of the vtable has already been unwinded.
167     // When in '~WebPage' the vtable of 'QWebPage' points to the derived
168     // class 'WebPage' and it's possible to receive 'QWebPage' virtual calls
169     // like javaScriptAlert, javaScriptConsoleMessage, ...etc.
170     mainFrame()->load(QUrl());
171     delete m_webInspector;
172 }
173
174 QWebInspector* WebPage::webInspector()
175 {
176     if (!m_webInspector) {
177         m_webInspector = new QWebInspector;
178         m_webInspector->setPage(this);
179     }
180     return m_webInspector;
181 }
182
183 void WebPage::resetSettings()
184 {
185     // After each layout test, reset the settings that may have been changed by
186     // testRunner.overridePreference() or similar.
187     settings()->resetFontSize(QWebSettings::DefaultFontSize);
188     settings()->resetAttribute(QWebSettings::JavascriptCanOpenWindows);
189     settings()->resetAttribute(QWebSettings::JavascriptEnabled);
190     settings()->resetAttribute(QWebSettings::PrivateBrowsingEnabled);
191     settings()->resetAttribute(QWebSettings::SpatialNavigationEnabled);
192     settings()->resetAttribute(QWebSettings::LinksIncludedInFocusChain);
193     settings()->resetAttribute(QWebSettings::OfflineWebApplicationCacheEnabled);
194     settings()->resetAttribute(QWebSettings::LocalContentCanAccessRemoteUrls);
195     settings()->resetAttribute(QWebSettings::LocalContentCanAccessFileUrls);
196     settings()->resetAttribute(QWebSettings::PluginsEnabled);
197     settings()->resetAttribute(QWebSettings::JavascriptCanAccessClipboard);
198     settings()->resetAttribute(QWebSettings::AutoLoadImages);
199     settings()->resetAttribute(QWebSettings::ZoomTextOnly);
200     settings()->resetAttribute(QWebSettings::CSSRegionsEnabled);
201     settings()->resetAttribute(QWebSettings::CSSGridLayoutEnabled);
202     settings()->resetAttribute(QWebSettings::AcceleratedCompositingEnabled);
203
204     m_drt->testRunner()->setCaretBrowsingEnabled(false);
205     m_drt->testRunner()->setAuthorAndUserStylesEnabled(true);
206     m_drt->jscTestRunner()->setDefersLoading(false);
207
208     // globalSettings must be reset explicitly.
209     m_drt->testRunner()->setXSSAuditorEnabled(false);
210
211     QWebSettings::setMaximumPagesInCache(0); // reset to default
212     settings()->setUserStyleSheetUrl(QUrl()); // reset to default
213
214     DumpRenderTreeSupportQt::setSeamlessIFramesEnabled(true);
215
216     DumpRenderTreeSupportQt::resetInternalsObject(mainFrame()->handle());
217
218     m_pendingGeolocationRequests.clear();
219 }
220
221 QWebPage* WebPage::createWindow(QWebPage::WebWindowType)
222 {
223     return m_drt->createWindow();
224 }
225
226 void WebPage::javaScriptAlert(QWebFrame*, const QString& message)
227 {
228     if (!isTextOutputEnabled())
229         return;
230
231     fprintf(stdout, "ALERT: %s\n", message.toUtf8().constData());
232     fflush(stdout);
233 }
234
235 void WebPage::requestPermission(QWebFrame* frame, QWebPage::Feature feature)
236 {
237     switch (feature) {
238     case Notifications:
239         if (!m_drt->testRunner()->ignoreReqestForPermission())
240             setFeaturePermission(frame, feature, PermissionGrantedByUser);
241         break;
242     case Geolocation:
243         if (m_drt->testRunner()->isGeolocationPermissionSet())
244             if (m_drt->testRunner()->geolocationPermission())
245                 setFeaturePermission(frame, feature, PermissionGrantedByUser);
246             else
247                 setFeaturePermission(frame, feature, PermissionDeniedByUser);
248         else
249             m_pendingGeolocationRequests.append(frame);
250         break;
251     default:
252         break;
253     }
254 }
255
256 void WebPage::cancelPermission(QWebFrame* frame, QWebPage::Feature feature)
257 {
258     switch (feature) {
259     case Geolocation:
260         m_pendingGeolocationRequests.removeOne(frame);
261         break;
262     default:
263         break;
264     }
265 }
266
267 void WebPage::permissionSet(QWebPage::Feature feature)
268 {
269     switch (feature) {
270     case Geolocation:
271         {
272         Q_ASSERT(m_drt->testRunner()->isGeolocationPermissionSet());
273         foreach (QWebFrame* frame, m_pendingGeolocationRequests)
274             if (m_drt->testRunner()->geolocationPermission())
275                 setFeaturePermission(frame, feature, PermissionGrantedByUser);
276             else
277                 setFeaturePermission(frame, feature, PermissionDeniedByUser);
278
279         m_pendingGeolocationRequests.clear();
280         break;
281         }
282     default:
283         break;
284     }
285 }
286
287 // FIXME (119591): Make this match other platforms better.
288 static QString urlSuitableForTestResult(const QString& url)
289 {
290     if (url.isEmpty() || !url.startsWith(QLatin1String("file://")))
291         return url;
292
293     return QFileInfo(url).fileName();
294 }
295
296 void WebPage::javaScriptConsoleMessage(const QString& message, int lineNumber, const QString&)
297 {
298     if (!isTextOutputEnabled())
299         return;
300
301     QString newMessage;
302     if (!message.isEmpty()) {
303         newMessage = message;
304
305         size_t fileProtocol = newMessage.indexOf(QLatin1String("file://"));
306         if (fileProtocol != -1) {
307             newMessage = newMessage.left(fileProtocol) + urlSuitableForTestResult(newMessage.mid(fileProtocol));
308         }
309     }
310
311     fprintf(stdout, "CONSOLE MESSAGE: ");
312     if (lineNumber)
313         fprintf(stdout, "line %d: ", lineNumber);
314     fprintf(stdout, "%s\n", newMessage.toUtf8().constData());
315 }
316
317 bool WebPage::javaScriptConfirm(QWebFrame*, const QString& msg)
318 {
319     if (!isTextOutputEnabled())
320         return true;
321
322     fprintf(stdout, "CONFIRM: %s\n", msg.toUtf8().constData());
323     return true;
324 }
325
326 bool WebPage::javaScriptPrompt(QWebFrame*, const QString& msg, const QString& defaultValue, QString* result)
327 {
328     if (!isTextOutputEnabled())
329         return true;
330
331     fprintf(stdout, "PROMPT: %s, default text: %s\n", msg.toUtf8().constData(), defaultValue.toUtf8().constData());
332     *result = defaultValue;
333     return true;
334 }
335
336 bool WebPage::acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest& request, NavigationType type)
337 {
338     if (m_drt->testRunner()->waitForPolicy())
339         m_drt->testRunner()->notifyDone();
340
341     return QWebPage::acceptNavigationRequest(frame, request, type);
342 }
343
344 bool WebPage::supportsExtension(QWebPage::Extension extension) const
345 {
346     if (extension == QWebPage::ErrorPageExtension)
347         return m_drt->testRunner()->shouldHandleErrorPages();
348
349     return false;
350 }
351
352 bool WebPage::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output)
353 {
354     const QWebPage::ErrorPageExtensionOption* info = static_cast<const QWebPage::ErrorPageExtensionOption*>(option);
355
356     // Lets handle error pages for the main frame for now.
357     if (info->frame != mainFrame())
358         return false;
359
360     QWebPage::ErrorPageExtensionReturn* errorPage = static_cast<QWebPage::ErrorPageExtensionReturn*>(output);
361
362     errorPage->content = QString("data:text/html,<body/>").toUtf8();
363
364     return true;
365 }
366
367 QObject* WebPage::createPlugin(const QString& classId, const QUrl& url, const QStringList& paramNames, const QStringList& paramValues)
368 {
369     Q_UNUSED(url);
370     Q_UNUSED(paramNames);
371     Q_UNUSED(paramValues);
372
373     if (classId == QLatin1String("QProgressBar"))
374         return new QProgressBar(view());
375     if (classId == QLatin1String("QLabel"))
376         return new QLabel(view());
377
378     return 0;
379 }
380
381 void WebPage::setViewGeometry(const QRect& rect)
382 {
383     if (WebViewGraphicsBased* v = qobject_cast<WebViewGraphicsBased*>(view()))
384         v->scene()->setSceneRect(QRectF(rect));
385     else if (QWidget *v = view())
386         v->setGeometry(rect);
387 }
388
389 WebViewGraphicsBased::WebViewGraphicsBased(QWidget* parent)
390     : m_item(new QGraphicsWebView)
391 {
392     setScene(new QGraphicsScene(this));
393     scene()->addItem(m_item);
394 }
395
396 static DumpRenderTree *s_instance = 0;
397
398 DumpRenderTree::DumpRenderTree()
399     : m_dumpPixelsForAllTests(false)
400     , m_stdin(0)
401     , m_enableTextOutput(false)
402     , m_standAloneMode(false)
403     , m_graphicsBased(false)
404     , m_persistentStoragePath(QString(getenv("DUMPRENDERTREE_TEMP")))
405 {
406     ASSERT(!s_instance);
407     s_instance = this;
408
409     QByteArray viewMode = getenv("QT_DRT_WEBVIEW_MODE");
410     if (viewMode == "graphics")
411         setGraphicsBased(true);
412
413     WebKit::initializeWebKitWidgets();
414     WebCore::initializeWebCoreQt();
415     DumpRenderTreeSupportQt::initialize();
416
417     // Set running in DRT mode for qwebpage to create testable objects.
418     DumpRenderTreeSupportQt::setDumpRenderTreeModeEnabled(true);
419     DumpRenderTreeSupportQt::overwritePluginDirectories();
420     QWebSettings::enablePersistentStorage(m_persistentStoragePath);
421
422     m_networkAccessManager = new NetworkAccessManager(this);
423     // create our primary testing page/view.
424     if (isGraphicsBased()) {
425         WebViewGraphicsBased* view = new WebViewGraphicsBased(0);
426         m_page = new WebPage(view, this);
427         view->setPage(m_page);
428         m_mainView = view;
429     } else {
430         QWebView* view = new QWebView(0);
431         m_page = new WebPage(view, this);
432         view->setPage(m_page);
433         m_mainView = view;
434     }
435     // Use a frame group name for all pages created by DumpRenderTree to allow
436     // testing of cross-page frame lookup.
437     DumpRenderTreeSupportQt::webPageSetGroupName(pageAdapter(), "org.webkit.qt.DumpRenderTree");
438
439     m_mainView->setContextMenuPolicy(Qt::NoContextMenu);
440     m_mainView->resize(QSize(TestRunner::viewWidth, TestRunner::viewHeight));
441
442     // clean up cache by resetting quota.
443     qint64 quota = webPage()->settings()->offlineWebApplicationCacheQuota();
444     webPage()->settings()->setOfflineWebApplicationCacheQuota(quota);
445
446     // create our controllers. This has to be done before connectFrame,
447     // as it exports there to the JavaScript DOM window.
448     m_controller = new TestRunnerQt(this);
449     connect(m_controller, SIGNAL(showPage()), this, SLOT(showPage()));
450     connect(m_controller, SIGNAL(hidePage()), this, SLOT(hidePage()));
451
452     // async geolocation permission set by controller
453     connect(m_controller, SIGNAL(geolocationPermissionSet()), this, SLOT(geolocationPermissionSet()));
454
455     connect(m_controller, SIGNAL(done()), this, SLOT(dump()));
456     m_eventSender = new EventSender(m_page);
457     m_textInputController = new TextInputController(m_page);
458     m_gcController.reset(new GCController());
459
460     // now connect our different signals
461     connect(m_page, SIGNAL(frameCreated(QWebFrame *)),
462             this, SLOT(connectFrame(QWebFrame *)));
463     connectFrame(m_page->mainFrame());
464
465     connect(m_page, SIGNAL(loadFinished(bool)),
466             m_controller, SLOT(maybeDump(bool)));
467     // We need to connect to loadStarted() because notifyDone should only
468     // dump results itself when the last page loaded in the test has finished loading.
469     connect(m_page, SIGNAL(loadStarted()),
470             m_controller, SLOT(resetLoadFinished()));
471     connect(m_page, SIGNAL(windowCloseRequested()), this, SLOT(windowCloseRequested()));
472     connect(m_page, SIGNAL(printRequested(QWebFrame*)), this, SLOT(dryRunPrint(QWebFrame*)));
473
474     connect(m_page->mainFrame(), SIGNAL(titleChanged(const QString&)),
475             SLOT(titleChanged(const QString&)));
476     connect(m_page, SIGNAL(databaseQuotaExceeded(QWebFrame*,QString)),
477             this, SLOT(dumpDatabaseQuota(QWebFrame*,QString)));
478     connect(m_page, SIGNAL(applicationCacheQuotaExceeded(QWebSecurityOrigin *, quint64, quint64)),
479             this, SLOT(dumpApplicationCacheQuota(QWebSecurityOrigin *, quint64, quint64)));
480     connect(m_page, SIGNAL(statusBarMessage(const QString&)),
481             this, SLOT(statusBarMessage(const QString&)));
482
483     QObject::connect(this, SIGNAL(quit()), qApp, SLOT(quit()), Qt::QueuedConnection);
484
485     DumpRenderTreeSupportQt::setDumpRenderTreeModeEnabled(true);
486     DumpRenderTreeSupportQt::setInteractiveFormValidationEnabled(pageAdapter(), true);
487     DumpRenderTreeSupportQt::enableMockScrollbars();
488
489     QFocusEvent event(QEvent::FocusIn, Qt::ActiveWindowFocusReason);
490     QApplication::sendEvent(m_mainView, &event);
491 }
492
493 DumpRenderTree::~DumpRenderTree()
494 {
495     if (!m_redirectOutputFileName.isEmpty())
496         fclose(stdout);
497     if (!m_redirectErrorFileName.isEmpty())
498         fclose(stderr);
499     delete m_mainView;
500     delete m_stdin;
501     s_instance = 0;
502 }
503
504 DumpRenderTree* DumpRenderTree::instance()
505 {
506     return s_instance;
507 }
508
509 static void clearHistory(QWebPage* page)
510 {
511     // QWebHistory::clear() leaves current page, so remove it as well by setting
512     // max item count to 0, and then setting it back to it's original value.
513
514     QWebHistory* history = page->history();
515     int itemCount = history->maximumItemCount();
516
517     history->clear();
518     history->setMaximumItemCount(0);
519     history->setMaximumItemCount(itemCount);
520 }
521
522 void DumpRenderTree::dryRunPrint(QWebFrame* frame)
523 {
524 #if !defined(QT_NO_PRINTER) && HAVE(QTPRINTSUPPORT)
525     NullPrinter printer;
526     frame->print(&printer);
527 #endif
528 }
529
530 void DumpRenderTree::resetToConsistentStateBeforeTesting(const QUrl& url)
531 {
532     // reset so that any current loads are stopped
533     // NOTE: that this has to be done before the testRunner is
534     // reset or we get timeouts for some tests.
535     m_page->blockSignals(true);
536     m_page->triggerAction(QWebPage::Stop);
537     m_page->blockSignals(false);
538
539     QList<QWebSecurityOrigin> knownOrigins = QWebSecurityOrigin::allOrigins();
540     for (int i = 0; i < knownOrigins.size(); ++i)
541         knownOrigins[i].setDatabaseQuota(databaseDefaultQuota);
542
543     // reset the testRunner at this point, so that we under no
544     // circumstance dump (stop the waitUntilDone timer) during the reset
545     // of the DRT.
546     m_controller->reset();
547
548     m_jscController = TestRunner::create(url.toString().toStdString(), m_expectedHash.toStdString());
549
550     // reset mouse clicks counter
551     m_eventSender->resetClickCount();
552
553     closeRemainingWindows();
554     
555     // Call setTextSizeMultiplier(1.0) to reset TextZoomFactor and PageZoomFactor too. 
556     // It should be done before resetSettings() to guarantee resetting QWebSettings::ZoomTextOnly correctly.
557     m_page->mainFrame()->setTextSizeMultiplier(1.0);
558
559     m_page->resetSettings();
560 #ifndef QT_NO_UNDOSTACK
561     m_page->undoStack()->clear();
562 #endif
563
564     clearHistory(m_page);
565     DumpRenderTreeSupportQt::scalePageBy(mainFrameAdapter(), 1, QPoint(0, 0));
566     DumpRenderTreeSupportQt::clearFrameName(mainFrameAdapter());
567     DumpRenderTreeSupportQt::removeUserStyleSheets(pageAdapter());
568
569     m_page->mainFrame()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAsNeeded);
570     m_page->mainFrame()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAsNeeded);
571
572     if (url.scheme() == "http" || url.scheme() == "https") {
573         // credentials may exist from previous tests.
574         m_page->setNetworkAccessManager(0);
575         delete m_networkAccessManager;
576         m_networkAccessManager = new NetworkAccessManager(this);
577         m_page->setNetworkAccessManager(m_networkAccessManager);
578     }
579
580     WorkQueue::shared()->clear();
581     WorkQueue::shared()->setFrozen(false);
582
583     DumpRenderTreeSupportQt::resetOriginAccessWhiteLists();
584
585     DumpRenderTreeSupportQt::setWindowsBehaviorAsEditingBehavior(pageAdapter());
586
587     QLocale::setDefault(QLocale::c());
588
589     testRunner()->setDeveloperExtrasEnabled(true);
590 #ifndef Q_OS_WINCE
591     setlocale(LC_ALL, "");
592 #endif
593
594     DumpRenderTreeSupportQt::clearOpener(mainFrameAdapter());
595 }
596
597 static bool isGlobalHistoryTest(const QUrl& url)
598 {
599     if (url.path().contains("globalhistory/"))
600         return true;
601     return false;
602 }
603
604 static bool isWebInspectorTest(const QUrl& url)
605 {
606     if (url.path().contains("inspector/"))
607         return true;
608     return false;
609 }
610
611 static bool isDumpAsTextTest(const QUrl& url)
612 {
613     if (url.path().contains("dumpAsText/"))
614         return true;
615     return false;
616 }
617
618
619 void DumpRenderTree::open(const QUrl& url)
620 {
621     DumpRenderTreeSupportQt::dumpResourceLoadCallbacksPath(QFileInfo(url.toString()).path());
622     resetToConsistentStateBeforeTesting(url);
623
624     if (isWebInspectorTest(m_page->mainFrame()->url()))
625         testRunner()->closeWebInspector();
626
627     if (isWebInspectorTest(url))
628         testRunner()->showWebInspector();
629
630     if (isDumpAsTextTest(url))
631         m_jscController->setDumpAsText(true);
632
633     if (isGlobalHistoryTest(url))
634         testRunner()->dumpHistoryCallbacks();
635
636     // W3C SVG tests expect to be 480x360
637     bool isW3CTest = url.toString().contains("svg/W3C-SVG-1.1");
638     int width = isW3CTest ? TestRunner::w3cSVGViewWidth : TestRunner::viewWidth;
639     int height = isW3CTest ? TestRunner::w3cSVGViewHeight : TestRunner::viewHeight;
640     m_mainView->resize(QSize(width, height));
641     m_page->setPreferredContentsSize(QSize());
642     m_page->setViewportSize(QSize(width, height));
643
644     QFocusEvent ev(QEvent::FocusIn);
645     m_page->event(&ev);
646
647     WebKit::QtTestSupport::clearMemoryCaches();
648
649     WebKit::QtTestSupport::initializeTestFonts();
650
651     DumpRenderTreeSupportQt::dumpFrameLoader(url.toString().contains("loading/"));
652     setTextOutputEnabled(true);
653     m_page->mainFrame()->load(url);
654 }
655
656 void DumpRenderTree::readLine()
657 {
658     if (!m_stdin) {
659         m_stdin = new QFile;
660         m_stdin->open(stdin, QFile::ReadOnly);
661
662         if (!m_stdin->isReadable()) {
663             emit quit();
664             return;
665         }
666     }
667
668     QByteArray line = m_stdin->readLine().trimmed();
669
670     if (line.isEmpty()) {
671         emit quit();
672         return;
673     }
674
675     processLine(QString::fromLocal8Bit(line.constData(), line.length()));
676 }
677
678 void DumpRenderTree::processArgsLine(const QStringList &args)
679 {
680     setStandAloneMode(true);
681
682     m_standAloneModeTestList = args;
683
684     QFileInfo firstEntry(m_standAloneModeTestList.first());
685     if (firstEntry.isDir()) {
686         QDir folderEntry(m_standAloneModeTestList.first());
687         QStringList supportedExt;
688         // Check for all supported extensions (from Scripts/webkitpy/layout_tests/layout_package/test_files.py).
689         supportedExt << "*.html" << "*.shtml" << "*.xml" << "*.xhtml" << "*.xhtmlmp" << "*.pl" << "*.php" << "*.svg";
690         m_standAloneModeTestList = folderEntry.entryList(supportedExt, QDir::Files);
691         for (int i = 0; i < m_standAloneModeTestList.size(); ++i)
692             m_standAloneModeTestList[i] = folderEntry.absoluteFilePath(m_standAloneModeTestList[i]);
693     }
694     connect(this, SIGNAL(ready()), this, SLOT(loadNextTestInStandAloneMode()), Qt::QueuedConnection);
695
696     if (!m_standAloneModeTestList.isEmpty()) {
697         QString first = m_standAloneModeTestList.takeFirst();
698         processLine(first);
699     }
700 }
701
702 void DumpRenderTree::loadNextTestInStandAloneMode()
703 {
704     if (m_standAloneModeTestList.isEmpty()) {
705         emit quit();
706         return;
707     }
708     QString first = m_standAloneModeTestList.takeFirst();
709     processLine(first);
710 }
711
712 void DumpRenderTree::processLine(const QString &input)
713 {
714     TestCommand command = parseInputLine(std::string(input.toLatin1().constData()));
715     QString pathOrURL = QLatin1String(command.pathOrURL.c_str());
716     m_dumpPixelsForCurrentTest = command.shouldDumpPixels || m_dumpPixelsForAllTests;
717     m_expectedHash = QLatin1String(command.expectedPixelHash.c_str());
718
719     if (pathOrURL.startsWith(QLatin1String("http:"))
720             || pathOrURL.startsWith(QLatin1String("https:"))
721             || pathOrURL.startsWith(QLatin1String("file:"))
722             || pathOrURL == QLatin1String("about:blank")) {
723         open(QUrl(pathOrURL));
724     } else {
725         QFileInfo fi(pathOrURL);
726
727         if (!fi.exists()) {
728             QDir currentDir = QDir::currentPath();
729
730             // Try to be smart about where the test is located
731             if (currentDir.dirName() == QLatin1String("LayoutTests"))
732                 fi = QFileInfo(currentDir, pathOrURL.replace(QRegExp(".*?LayoutTests/(.*)"), "\\1"));
733             else if (!pathOrURL.contains(QLatin1String("LayoutTests")))
734                 fi = QFileInfo(currentDir, pathOrURL.prepend(QLatin1String("LayoutTests/")));
735
736             if (!fi.exists()) {
737                 emit ready();
738                 return;
739             }
740         }
741
742         open(QUrl::fromLocalFile(fi.absoluteFilePath()));
743     }
744
745     if (command.timeout > 0)
746         setTimeout(command.timeout);
747     fflush(stdout);
748 }
749
750 void DumpRenderTree::closeRemainingWindows()
751 {
752     foreach (QObject* widget, windows)
753         delete widget;
754     windows.clear();
755 }
756
757 void DumpRenderTree::initJSObjects()
758 {
759     QWebFrame *frame = qobject_cast<QWebFrame*>(sender());
760     Q_ASSERT(frame);
761
762     JSContextRef context = 0;
763     JSObjectRef window = 0;
764
765     DumpRenderTreeSupportQt::getJSWindowObject(frame->handle(), &context, &window);
766
767     frame->addToJavaScriptWindowObject(QLatin1String("testRunner"), m_controller);
768     frame->addToJavaScriptWindowObject(QLatin1String("eventSender"), m_eventSender);
769     frame->addToJavaScriptWindowObject(QLatin1String("textInputController"), m_textInputController);
770     m_gcController->makeWindowObject(context, window, 0);
771
772     if (m_jscController) {
773         JSObjectRef dummyWindow = JSObjectMake(context, 0, 0);
774         m_jscController->makeWindowObject(context, dummyWindow, 0);
775         JSRetainPtr<JSStringRef> testRunnerName(Adopt, JSStringCreateWithUTF8CString("testRunner"));
776         JSValueRef wrappedTestRunner = JSObjectGetProperty(context, dummyWindow, testRunnerName.get(), 0);
777         JSRetainPtr<JSStringRef> helperScript(Adopt, JSStringCreateWithUTF8CString("(function() {\n"
778                                                                                    "    function bind(fun, thisArg) {\n"
779                                                                                    "        return function() {\n"
780                                                                                    "            return fun.apply(thisArg, Array.prototype.slice.call(arguments));\n"
781                                                                                    "        }\n"
782                                                                                    "    }\n"
783                                                                                    "for (var prop in this.jscBasedTestRunner) {\n"
784                                                                                    "    var pd = Object.getOwnPropertyDescriptor(this.qtBasedTestRunner, prop);\n"
785                                                                                    "    if (pd !== undefined && !pd.writable) continue;\n"
786                                                                                    "    pd = Object.getOwnPropertyDescriptor(this.jscBasedTestRunner, prop);\n"
787                                                                                    "    this.qtBasedTestRunner[prop] = bind(this.jscBasedTestRunner[prop], this.jscBasedTestRunner);\n"
788                                                                                    "}\n"
789                                                                                    "}).apply(this)\n"));
790
791         JSRetainPtr<JSStringRef> qtBasedTestRunnerName(Adopt, JSStringCreateWithUTF8CString("qtBasedTestRunner"));
792         JSRetainPtr<JSStringRef> jscBasedTestRunnerName(Adopt, JSStringCreateWithUTF8CString("jscBasedTestRunner"));
793
794         JSObjectRef args = JSObjectMake(context, 0, 0);
795         JSObjectSetProperty(context, args, qtBasedTestRunnerName.get(), JSObjectGetProperty(context, window, testRunnerName.get(), 0), 0, 0);
796         JSObjectSetProperty(context, args, jscBasedTestRunnerName.get(), wrappedTestRunner, 0, 0);
797
798         JSValueRef ex = 0;
799         JSEvaluateScript(context, helperScript.get(), args, 0, 0, &ex);
800         if (ex) {
801             JSRetainPtr<JSStringRef> msg(Adopt, JSValueToStringCopy(context, ex, 0));
802             fprintf(stderr, "Error evaluating TestRunner setup-script: %s\n", qPrintable(JSStringCopyQString(msg.get())));
803         }
804     }
805
806     DumpRenderTreeSupportQt::injectInternalsObject(frame->handle());
807 }
808
809 void DumpRenderTree::showPage()
810 {
811     m_mainView->show();
812     // we need a paint event but cannot process all the events
813     QPixmap pixmap(m_mainView->size());
814     m_mainView->render(&pixmap);
815 }
816
817 void DumpRenderTree::hidePage()
818 {
819     m_mainView->hide();
820 }
821
822 QString DumpRenderTree::dumpFrameScrollPosition(QWebFrame* frame)
823 {
824     if (!frame || !DumpRenderTreeSupportQt::hasDocumentElement(frame->handle()))
825         return QString();
826
827     QString result;
828     QPoint pos = frame->scrollPosition();
829     if (pos.x() > 0 || pos.y() > 0) {
830         QWebFrame* parent = qobject_cast<QWebFrame *>(frame->parent());
831         if (parent)
832             result.append(QString("frame '%1' ").arg(frame->title()));
833         result.append(QString("scrolled to %1,%2\n").arg(pos.x()).arg(pos.y()));
834     }
835
836     if (m_jscController->dumpChildFrameScrollPositions()) {
837         QList<QWebFrame*> children = frame->childFrames();
838         for (int i = 0; i < children.size(); ++i)
839             result += dumpFrameScrollPosition(children.at(i));
840     }
841     return result;
842 }
843
844 QString DumpRenderTree::dumpFramesAsText(QWebFrame* frame)
845 {
846     if (!frame || !DumpRenderTreeSupportQt::hasDocumentElement(frame->handle()))
847         return QString();
848
849     QString result;
850     QWebFrame* parent = qobject_cast<QWebFrame*>(frame->parent());
851     if (parent) {
852         result.append(QLatin1String("\n--------\nFrame: '"));
853         result.append(frame->frameName());
854         result.append(QLatin1String("'\n--------\n"));
855     }
856
857     QString innerText = frame->toPlainText();
858     result.append(innerText);
859     result.append(QLatin1String("\n"));
860
861     if (m_jscController->dumpChildFramesAsText()) {
862         QList<QWebFrame *> children = frame->childFrames();
863         for (int i = 0; i < children.size(); ++i)
864             result += dumpFramesAsText(children.at(i));
865     }
866
867     return result;
868 }
869
870 static QString dumpHistoryItem(const QWebHistoryItem& item, int indent, bool current)
871 {
872     QString result;
873
874     int start = 0;
875     if (current) {
876         result.append(QLatin1String("curr->"));
877         start = 6;
878     }
879     for (int i = start; i < indent; i++)
880         result.append(' ');
881
882     QString url = item.url().toString();
883     if (url.contains("file://")) {
884         static QString layoutTestsString("/LayoutTests/");
885         static QString fileTestString("(file test):");
886
887         QString res = url.mid(url.indexOf(layoutTestsString) + layoutTestsString.length());
888         if (res.isEmpty())
889             return result;
890
891         result.append(fileTestString);
892         result.append(res);
893     } else {
894         result.append(url);
895     }
896
897     QString target = DumpRenderTreeSupportQt::historyItemTarget(item);
898     if (!target.isEmpty())
899         result.append(QString(QLatin1String(" (in frame \"%1\")")).arg(target));
900
901     if (DumpRenderTreeSupportQt::isTargetItem(item))
902         result.append(QLatin1String("  **nav target**"));
903     result.append(QLatin1String("\n"));
904
905     QMap<QString, QWebHistoryItem> children = DumpRenderTreeSupportQt::getChildHistoryItems(item);
906     foreach (QWebHistoryItem item, children)
907         result += dumpHistoryItem(item, 12, false);
908
909     return result;
910 }
911
912 QString DumpRenderTree::dumpBackForwardList(QWebPage* page)
913 {
914     QWebHistory* history = page->history();
915
916     QString result;
917     result.append(QLatin1String("\n============== Back Forward List ==============\n"));
918
919     // FORMAT:
920     // "        (file test):fast/loader/resources/click-fragment-link.html  **nav target**"
921     // "curr->  (file test):fast/loader/resources/click-fragment-link.html#testfragment  **nav target**"
922
923     int maxItems = history->maximumItemCount();
924
925     foreach (const QWebHistoryItem item, history->backItems(maxItems)) {
926         if (!item.isValid())
927             continue;
928         result.append(dumpHistoryItem(item, 8, false));
929     }
930
931     QWebHistoryItem item = history->currentItem();
932     if (item.isValid())
933         result.append(dumpHistoryItem(item, 8, true));
934
935     foreach (const QWebHistoryItem item, history->forwardItems(maxItems)) {
936         if (!item.isValid())
937             continue;
938         result.append(dumpHistoryItem(item, 8, false));
939     }
940
941     result.append(QLatin1String("===============================================\n"));
942     return result;
943 }
944
945 static const char *methodNameStringForFailedTest(TestRunner *controller)
946 {
947     const char *errorMessage;
948     if (controller->dumpAsText())
949         errorMessage = "[documentElement innerText]";
950     // FIXME: Add when we have support
951     //else if (controller->dumpDOMAsWebArchive())
952     //    errorMessage = "[[mainFrame DOMDocument] webArchive]";
953     //else if (controller->dumpSourceAsWebArchive())
954     //    errorMessage = "[[mainFrame dataSource] webArchive]";
955     else
956         errorMessage = "[mainFrame renderTreeAsExternalRepresentation]";
957
958     return errorMessage;
959 }
960
961 void DumpRenderTree::dump()
962 {
963     // Prevent any further frame load or resource load callbacks from appearing after we dump the result.
964     DumpRenderTreeSupportQt::dumpFrameLoader(false);
965     DumpRenderTreeSupportQt::dumpResourceLoadCallbacks(false);
966
967     QWebFrame *mainFrame = m_page->mainFrame();
968
969     if (isStandAloneMode()) {
970         QString markup = mainFrame->toHtml();
971         fprintf(stdout, "Source:\n\n%s\n", markup.toUtf8().constData());
972     }
973
974     QString mimeType = DumpRenderTreeSupportQt::responseMimeType(mainFrame->handle());
975     if (mimeType == "text/plain")
976         m_jscController->setDumpAsText(true);
977
978     // Dump render text...
979     QString resultString;
980     QString resultContentType = "text/plain";
981     QByteArray resultData;
982     if (m_controller->shouldDumpAsAudio()) {
983         resultContentType = "audio/wav";
984         resultData = m_controller->audioData();
985     } else if (m_jscController->dumpAsText())
986         resultString = dumpFramesAsText(mainFrame);
987     else {
988         resultString = DumpRenderTreeSupportQt::frameRenderTreeDump(mainFrame->handle());
989         resultString += dumpFrameScrollPosition(mainFrame);
990     }
991     if (!resultString.isEmpty()) {
992         fprintf(stdout, "Content-Type: %s\n", resultContentType.toUtf8().constData());
993         fprintf(stdout, "%s", resultString.toUtf8().constData());
994
995         if (m_jscController->dumpBackForwardList()) {
996             fprintf(stdout, "%s", dumpBackForwardList(webPage()).toUtf8().constData());
997             foreach (QObject* widget, windows) {
998                 QWebPage* page = qobject_cast<QWebPage*>(widget->findChild<QWebPage*>());
999                 fprintf(stdout, "%s", dumpBackForwardList(page).toUtf8().constData());
1000             }
1001         }
1002     } else if (!resultData.isEmpty()) {
1003         fprintf(stdout, "Content-Type: %s\n", resultContentType.toUtf8().constData());
1004         fprintf(stdout, "Content-Transfer-Encoding: base64\n");
1005         fprintf(stdout, "%s", resultData.toBase64().constData());
1006     } else
1007         printf("ERROR: nil result from %s", methodNameStringForFailedTest(m_jscController.get()));
1008
1009     // signal end of text block
1010     fputs("#EOF\n", stdout);
1011     fputs("#EOF\n", stderr);
1012
1013     if (m_dumpPixelsForCurrentTest && m_jscController->generatePixelResults()) {
1014         QImage image;
1015         if (!m_jscController->isPrinting()) {
1016             image = QImage(m_page->viewportSize(), QImage::Format_ARGB32);
1017             image.fill(Qt::white);
1018             QPainter painter(&image);
1019             mainFrame->render(&painter);
1020             painter.end();
1021         } else
1022             image = DumpRenderTreeSupportQt::paintPagesWithBoundaries(mainFrame->handle());
1023
1024         if (DumpRenderTreeSupportQt::trackRepaintRects(mainFrameAdapter())) {
1025             QVector<QRect> repaintRects;
1026             DumpRenderTreeSupportQt::getTrackedRepaintRects(mainFrameAdapter(), repaintRects);
1027             QImage mask(image.size(), image.format());
1028             mask.fill(QColor(0, 0, 0, 0.66 * 255));
1029
1030             QPainter maskPainter(&mask);
1031             maskPainter.setCompositionMode(QPainter::CompositionMode_Source);
1032             for (int i = 0; i < repaintRects.size(); ++i)
1033                 maskPainter.fillRect(repaintRects[i], Qt::transparent);
1034
1035             QPainter painter(&image);
1036             painter.drawImage(image.rect(), mask);
1037
1038             DumpRenderTreeSupportQt::setTrackRepaintRects(mainFrameAdapter(), false);
1039         }
1040
1041         QCryptographicHash hash(QCryptographicHash::Md5);
1042         for (int row = 0; row < image.height(); ++row)
1043             hash.addData(reinterpret_cast<const char*>(image.scanLine(row)), image.width() * 4);
1044         QString actualHash = hash.result().toHex();
1045
1046         fprintf(stdout, "\nActualHash: %s\n", qPrintable(actualHash));
1047
1048         bool dumpImage = true;
1049
1050         if (!m_expectedHash.isEmpty()) {
1051             Q_ASSERT(m_expectedHash.length() == 32);
1052             fprintf(stdout, "\nExpectedHash: %s\n", qPrintable(m_expectedHash));
1053
1054             if (m_expectedHash == actualHash)
1055                 dumpImage = false;
1056         }
1057
1058         if (dumpImage) {
1059             image.setText("checksum", actualHash);
1060
1061             QBuffer buffer;
1062             buffer.open(QBuffer::WriteOnly);
1063             image.save(&buffer, "PNG");
1064             buffer.close();
1065             const QByteArray &data = buffer.data();
1066
1067             printf("Content-Type: %s\n", "image/png");
1068             printf("Content-Length: %lu\n", static_cast<unsigned long>(data.length()));
1069
1070             const quint32 bytesToWriteInOneChunk = 1 << 15;
1071             quint32 dataRemainingToWrite = data.length();
1072             const char *ptr = data.data();
1073             while (dataRemainingToWrite) {
1074                 quint32 bytesToWriteInThisChunk = qMin(dataRemainingToWrite, bytesToWriteInOneChunk);
1075                 quint32 bytesWritten = fwrite(ptr, 1, bytesToWriteInThisChunk, stdout);
1076                 if (bytesWritten != bytesToWriteInThisChunk)
1077                     break;
1078                 dataRemainingToWrite -= bytesWritten;
1079                 ptr += bytesWritten;
1080             }
1081         }
1082
1083         fflush(stdout);
1084     }
1085
1086     puts("#EOF");   // terminate the (possibly empty) pixels block
1087
1088     fflush(stdout);
1089     fflush(stderr);
1090
1091      emit ready();
1092 }
1093
1094 void DumpRenderTree::titleChanged(const QString &s)
1095 {
1096     if (m_jscController->dumpTitleChanges())
1097         printf("TITLE CHANGED: '%s'\n", s.toUtf8().data());
1098 }
1099
1100 void DumpRenderTree::connectFrame(QWebFrame *frame)
1101 {
1102     connect(frame, SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(initJSObjects()));
1103     connect(frame, SIGNAL(provisionalLoad()),
1104             testRunner(), SLOT(provisionalLoad()));
1105 }
1106
1107 void DumpRenderTree::dumpDatabaseQuota(QWebFrame* frame, const QString& dbName)
1108 {
1109     if (!m_jscController->dumpDatabaseCallbacks())
1110         return;
1111     QWebSecurityOrigin origin = frame->securityOrigin();
1112     printf("UI DELEGATE DATABASE CALLBACK: exceededDatabaseQuotaForSecurityOrigin:{%s, %s, %i} database:%s\n",
1113            origin.scheme().toUtf8().data(),
1114            origin.host().toUtf8().data(),
1115            origin.port(),
1116            dbName.toUtf8().data());
1117     origin.setDatabaseQuota(databaseDefaultQuota);
1118 }
1119
1120 void DumpRenderTree::dumpApplicationCacheQuota(QWebSecurityOrigin* origin, quint64 defaultOriginQuota, quint64 totalSpaceNeeded)
1121 {
1122     if (m_jscController->dumpApplicationCacheDelegateCallbacks()) {
1123         // For example, numbers from 30000 - 39999 will output as 30000.
1124         // Rounding up or down not really matter for these tests. It's
1125         // sufficient to just get a range of 10000 to determine if we were
1126         // above or below a threshold.
1127         quint64 truncatedSpaceNeeded = (totalSpaceNeeded / 10000) * 10000;
1128         printf("UI DELEGATE APPLICATION CACHE CALLBACK: exceededApplicationCacheOriginQuotaForSecurityOrigin:{%s, %s, %i} totalSpaceNeeded:~%llu\n",
1129                origin->scheme().toUtf8().data(),
1130                origin->host().toUtf8().data(),
1131                origin->port(),
1132                truncatedSpaceNeeded
1133                );
1134     }
1135
1136     if (m_jscController->disallowIncreaseForApplicationCacheQuota())
1137         return;
1138
1139     origin->setApplicationCacheQuota(defaultOriginQuota);
1140 }
1141
1142 void DumpRenderTree::statusBarMessage(const QString& message)
1143 {
1144     if (!m_jscController->dumpStatusCallbacks())
1145         return;
1146
1147     printf("UI DELEGATE STATUS CALLBACK: setStatusText:%s\n", message.toUtf8().constData());
1148 }
1149
1150 QWebPage *DumpRenderTree::createWindow()
1151 {
1152     if (!m_jscController->canOpenWindows())
1153         return 0;
1154
1155     // Create a dummy container object to track the page in DRT.
1156     // QObject is used instead of QWidget to prevent DRT from
1157     // showing the main view when deleting the container.
1158
1159     QObject* container = new QObject(m_mainView);
1160     // create a QWebPage we want to return
1161     QWebPage* page = static_cast<QWebPage*>(new WebPage(container, this));
1162     // gets cleaned up in closeRemainingWindows()
1163     windows.append(container);
1164
1165     // connect the needed signals to the page
1166     connect(page, SIGNAL(frameCreated(QWebFrame*)), this, SLOT(connectFrame(QWebFrame*)));
1167     connectFrame(page->mainFrame());
1168     connect(page, SIGNAL(loadFinished(bool)), m_controller, SLOT(maybeDump(bool)));
1169     connect(page, SIGNAL(windowCloseRequested()), this, SLOT(windowCloseRequested()));
1170
1171     // Use a frame group name for all pages created by DumpRenderTree to allow
1172     // testing of cross-page frame lookup.
1173     DumpRenderTreeSupportQt::webPageSetGroupName(page->handle(), "org.webkit.qt.DumpRenderTree");
1174
1175     return page;
1176 }
1177
1178 void DumpRenderTree::windowCloseRequested()
1179 {
1180     QWebPage* page = qobject_cast<QWebPage*>(sender());
1181     QObject* container = page->parent();
1182     windows.removeAll(container);
1183     container->deleteLater();
1184 }
1185
1186 int DumpRenderTree::windowCount() const
1187 {
1188 // include the main view in the count
1189     return windows.count() + 1;
1190 }
1191
1192 void DumpRenderTree::geolocationPermissionSet() 
1193 {
1194     m_page->permissionSet(QWebPage::Geolocation);
1195 }
1196
1197 void DumpRenderTree::switchFocus(bool focused)
1198 {
1199     QFocusEvent event((focused) ? QEvent::FocusIn : QEvent::FocusOut, Qt::ActiveWindowFocusReason);
1200     if (!isGraphicsBased())
1201         QApplication::sendEvent(m_mainView, &event);
1202     else {
1203         if (WebViewGraphicsBased* view = qobject_cast<WebViewGraphicsBased*>(m_mainView))
1204             view->scene()->sendEvent(view->graphicsView(), &event);
1205     }
1206
1207 }
1208
1209 QWebPageAdapter* DumpRenderTree::pageAdapter() const
1210 {
1211     return m_page->handle();
1212 }
1213
1214 QWebFrameAdapter* DumpRenderTree::mainFrameAdapter() const
1215 {
1216     return m_page->mainFrame()->handle();
1217 }
1218
1219 QList<WebPage*> DumpRenderTree::getAllPages() const
1220 {
1221     QList<WebPage*> pages;
1222     pages.append(m_page);
1223     foreach (QObject* widget, windows) {
1224         if (WebPage* page = widget->findChild<WebPage*>())
1225             pages.append(page);
1226     }
1227     return pages;
1228 }
1229
1230 void DumpRenderTree::setTimeout(int timeout)
1231 {
1232     m_controller->setTimeout(timeout);
1233 }
1234
1235 void DumpRenderTree::setShouldTimeout(bool flag)
1236 {
1237     m_controller->setShouldTimeout(flag);
1238 }