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