Home | History | Annotate | Download | only in qt
      1 /*
      2  * Copyright (C) 2005, 2006 Apple Computer, Inc.  All rights reserved.
      3  * Copyright (C) 2006 Nikolas Zimmermann <zimmermann (at) 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 
     34 #include "DumpRenderTreeQt.h"
     35 #include "DumpRenderTreeSupportQt.h"
     36 #include "EventSenderQt.h"
     37 #include "GCControllerQt.h"
     38 #include "LayoutTestControllerQt.h"
     39 #include "TextInputControllerQt.h"
     40 #include "PlainTextControllerQt.h"
     41 #include "testplugin.h"
     42 #include "WorkQueue.h"
     43 
     44 #include <QApplication>
     45 #include <QBuffer>
     46 #include <QCryptographicHash>
     47 #include <QDir>
     48 #include <QFile>
     49 #include <QFileInfo>
     50 #include <QFocusEvent>
     51 #include <QFontDatabase>
     52 #include <QLocale>
     53 #include <QNetworkAccessManager>
     54 #include <QNetworkReply>
     55 #include <QNetworkRequest>
     56 #include <QPaintDevice>
     57 #include <QPaintEngine>
     58 #ifndef QT_NO_PRINTER
     59 #include <QPrinter>
     60 #endif
     61 #include <QUndoStack>
     62 #include <QUrl>
     63 
     64 #include <qwebsettings.h>
     65 #include <qwebsecurityorigin.h>
     66 
     67 #ifndef QT_NO_UITOOLS
     68 #include <QtUiTools/QUiLoader>
     69 #endif
     70 
     71 #ifdef Q_WS_X11
     72 #include <fontconfig/fontconfig.h>
     73 #endif
     74 
     75 #include <limits.h>
     76 #include <locale.h>
     77 
     78 #ifndef Q_OS_WIN
     79 #include <unistd.h>
     80 #endif
     81 
     82 #include <qdebug.h>
     83 
     84 namespace WebCore {
     85 
     86 const int databaseDefaultQuota = 5 * 1024 * 1024;
     87 
     88 NetworkAccessManager::NetworkAccessManager(QObject* parent)
     89     : QNetworkAccessManager(parent)
     90 {
     91 #ifndef QT_NO_OPENSSL
     92     connect(this, SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError>&)),
     93             this, SLOT(sslErrorsEncountered(QNetworkReply*, const QList<QSslError>&)));
     94 #endif
     95 }
     96 
     97 #ifndef QT_NO_OPENSSL
     98 void NetworkAccessManager::sslErrorsEncountered(QNetworkReply* reply, const QList<QSslError>& errors)
     99 {
    100     if (reply->url().host() == "127.0.0.1" || reply->url().host() == "localhost") {
    101         bool ignore = true;
    102 
    103         // Accept any HTTPS certificate.
    104         foreach (const QSslError& error, errors) {
    105             if (error.error() < QSslError::UnableToGetIssuerCertificate || error.error() > QSslError::HostNameMismatch) {
    106                 ignore = false;
    107                 break;
    108             }
    109         }
    110 
    111         if (ignore)
    112             reply->ignoreSslErrors();
    113     }
    114 }
    115 #endif
    116 
    117 
    118 #ifndef QT_NO_PRINTER
    119 class NullPrinter : public QPrinter {
    120 public:
    121     class NullPaintEngine : public QPaintEngine {
    122     public:
    123         virtual bool begin(QPaintDevice*) { return true; }
    124         virtual bool end() { return true; }
    125         virtual QPaintEngine::Type type() const { return QPaintEngine::User; }
    126         virtual void drawPixmap(const QRectF& r, const QPixmap& pm, const QRectF& sr) { }
    127         virtual void updateState(const QPaintEngineState& state) { }
    128     };
    129 
    130     virtual QPaintEngine* paintEngine() const { return const_cast<NullPaintEngine*>(&m_engine); }
    131 
    132     NullPaintEngine m_engine;
    133 };
    134 #endif
    135 
    136 WebPage::WebPage(QObject* parent, DumpRenderTree* drt)
    137     : QWebPage(parent)
    138     , m_webInspector(0)
    139     , m_drt(drt)
    140 {
    141     QWebSettings* globalSettings = QWebSettings::globalSettings();
    142 
    143     globalSettings->setFontSize(QWebSettings::MinimumFontSize, 0);
    144     globalSettings->setFontSize(QWebSettings::MinimumLogicalFontSize, 5);
    145     globalSettings->setFontSize(QWebSettings::DefaultFontSize, 16);
    146     globalSettings->setFontSize(QWebSettings::DefaultFixedFontSize, 13);
    147 
    148     globalSettings->setAttribute(QWebSettings::JavascriptCanOpenWindows, true);
    149     globalSettings->setAttribute(QWebSettings::JavascriptCanAccessClipboard, true);
    150     globalSettings->setAttribute(QWebSettings::LinksIncludedInFocusChain, false);
    151     globalSettings->setAttribute(QWebSettings::PluginsEnabled, true);
    152     globalSettings->setAttribute(QWebSettings::LocalContentCanAccessRemoteUrls, true);
    153     globalSettings->setAttribute(QWebSettings::JavascriptEnabled, true);
    154     globalSettings->setAttribute(QWebSettings::PrivateBrowsingEnabled, false);
    155     globalSettings->setAttribute(QWebSettings::SpatialNavigationEnabled, false);
    156 
    157     connect(this, SIGNAL(geometryChangeRequested(const QRect &)),
    158             this, SLOT(setViewGeometry(const QRect & )));
    159 
    160     setNetworkAccessManager(m_drt->networkAccessManager());
    161     setPluginFactory(new TestPlugin(this));
    162 
    163     connect(this, SIGNAL(featurePermissionRequested(QWebFrame*, QWebPage::Feature)), this, SLOT(requestPermission(QWebFrame*, QWebPage::Feature)));
    164     connect(this, SIGNAL(featurePermissionRequestCanceled(QWebFrame*, QWebPage::Feature)), this, SLOT(cancelPermission(QWebFrame*, QWebPage::Feature)));
    165 }
    166 
    167 WebPage::~WebPage()
    168 {
    169     delete m_webInspector;
    170 }
    171 
    172 QWebInspector* WebPage::webInspector()
    173 {
    174     if (!m_webInspector) {
    175         m_webInspector = new QWebInspector;
    176         m_webInspector->setPage(this);
    177     }
    178     return m_webInspector;
    179 }
    180 
    181 void WebPage::resetSettings()
    182 {
    183     // After each layout test, reset the settings that may have been changed by
    184     // layoutTestController.overridePreference() or similar.
    185     settings()->resetFontSize(QWebSettings::DefaultFontSize);
    186     settings()->resetAttribute(QWebSettings::JavascriptCanOpenWindows);
    187     settings()->resetAttribute(QWebSettings::JavascriptEnabled);
    188     settings()->resetAttribute(QWebSettings::PrivateBrowsingEnabled);
    189     settings()->resetAttribute(QWebSettings::SpatialNavigationEnabled);
    190     settings()->resetAttribute(QWebSettings::LinksIncludedInFocusChain);
    191     settings()->resetAttribute(QWebSettings::OfflineWebApplicationCacheEnabled);
    192     settings()->resetAttribute(QWebSettings::LocalContentCanAccessRemoteUrls);
    193     settings()->resetAttribute(QWebSettings::LocalContentCanAccessFileUrls);
    194     settings()->resetAttribute(QWebSettings::PluginsEnabled);
    195     settings()->resetAttribute(QWebSettings::JavascriptCanAccessClipboard);
    196     settings()->resetAttribute(QWebSettings::AutoLoadImages);
    197 
    198     m_drt->layoutTestController()->setCaretBrowsingEnabled(false);
    199     m_drt->layoutTestController()->setFrameFlatteningEnabled(false);
    200     m_drt->layoutTestController()->setSmartInsertDeleteEnabled(true);
    201     m_drt->layoutTestController()->setSelectTrailingWhitespaceEnabled(false);
    202 
    203     // globalSettings must be reset explicitly.
    204     m_drt->layoutTestController()->setXSSAuditorEnabled(false);
    205 
    206     QWebSettings::setMaximumPagesInCache(0); // reset to default
    207     settings()->setUserStyleSheetUrl(QUrl()); // reset to default
    208 
    209     DumpRenderTreeSupportQt::setMinimumTimerInterval(this, DumpRenderTreeSupportQt::defaultMinimumTimerInterval());
    210 
    211     m_pendingGeolocationRequests.clear();
    212 }
    213 
    214 QWebPage *WebPage::createWindow(QWebPage::WebWindowType)
    215 {
    216     return m_drt->createWindow();
    217 }
    218 
    219 void WebPage::javaScriptAlert(QWebFrame*, const QString& message)
    220 {
    221     if (!isTextOutputEnabled())
    222         return;
    223 
    224     fprintf(stdout, "ALERT: %s\n", message.toUtf8().constData());
    225 }
    226 
    227 void WebPage::requestPermission(QWebFrame* frame, QWebPage::Feature feature)
    228 {
    229     switch (feature) {
    230     case Notifications:
    231         if (!m_drt->layoutTestController()->ignoreReqestForPermission())
    232             setFeaturePermission(frame, feature, PermissionGrantedByUser);
    233         break;
    234     case Geolocation:
    235         if (m_drt->layoutTestController()->isGeolocationPermissionSet())
    236             if (m_drt->layoutTestController()->geolocationPermission())
    237                 setFeaturePermission(frame, feature, PermissionGrantedByUser);
    238             else
    239                 setFeaturePermission(frame, feature, PermissionDeniedByUser);
    240         else
    241             m_pendingGeolocationRequests.append(frame);
    242         break;
    243     default:
    244         break;
    245     }
    246 }
    247 
    248 void WebPage::cancelPermission(QWebFrame* frame, QWebPage::Feature feature)
    249 {
    250     switch (feature) {
    251     case Geolocation:
    252         m_pendingGeolocationRequests.removeOne(frame);
    253         break;
    254     default:
    255         break;
    256     }
    257 }
    258 
    259 void WebPage::permissionSet(QWebPage::Feature feature)
    260 {
    261     switch (feature) {
    262     case Geolocation:
    263         {
    264         Q_ASSERT(m_drt->layoutTestController()->isGeolocationPermissionSet());
    265         foreach (QWebFrame* frame, m_pendingGeolocationRequests)
    266             if (m_drt->layoutTestController()->geolocationPermission())
    267                 setFeaturePermission(frame, feature, PermissionGrantedByUser);
    268             else
    269                 setFeaturePermission(frame, feature, PermissionDeniedByUser);
    270 
    271         m_pendingGeolocationRequests.clear();
    272         break;
    273         }
    274     default:
    275         break;
    276     }
    277 }
    278 
    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: line %d: %s\n", lineNumber, newMessage.toUtf8().constData());
    303 }
    304 
    305 bool WebPage::javaScriptConfirm(QWebFrame*, const QString& msg)
    306 {
    307     if (!isTextOutputEnabled())
    308         return true;
    309 
    310     fprintf(stdout, "CONFIRM: %s\n", msg.toUtf8().constData());
    311     return true;
    312 }
    313 
    314 bool WebPage::javaScriptPrompt(QWebFrame*, const QString& msg, const QString& defaultValue, QString* result)
    315 {
    316     if (!isTextOutputEnabled())
    317         return true;
    318 
    319     fprintf(stdout, "PROMPT: %s, default text: %s\n", msg.toUtf8().constData(), defaultValue.toUtf8().constData());
    320     *result = defaultValue;
    321     return true;
    322 }
    323 
    324 bool WebPage::acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest& request, NavigationType type)
    325 {
    326     if (m_drt->layoutTestController()->waitForPolicy()) {
    327         QString url = QString::fromUtf8(request.url().toEncoded());
    328         QString typeDescription;
    329 
    330         switch (type) {
    331         case NavigationTypeLinkClicked:
    332             typeDescription = "link clicked";
    333             break;
    334         case NavigationTypeFormSubmitted:
    335             typeDescription = "form submitted";
    336             break;
    337         case NavigationTypeBackOrForward:
    338             typeDescription = "back/forward";
    339             break;
    340         case NavigationTypeReload:
    341             typeDescription = "reload";
    342             break;
    343         case NavigationTypeFormResubmitted:
    344             typeDescription = "form resubmitted";
    345             break;
    346         case NavigationTypeOther:
    347             typeDescription = "other";
    348             break;
    349         default:
    350             typeDescription = "illegal value";
    351         }
    352 
    353         if (isTextOutputEnabled())
    354             fprintf(stdout, "Policy delegate: attempt to load %s with navigation type '%s'\n",
    355                     url.toUtf8().constData(), typeDescription.toUtf8().constData());
    356 
    357         m_drt->layoutTestController()->notifyDone();
    358     }
    359     return QWebPage::acceptNavigationRequest(frame, request, type);
    360 }
    361 
    362 bool WebPage::supportsExtension(QWebPage::Extension extension) const
    363 {
    364     if (extension == QWebPage::ErrorPageExtension)
    365         return m_drt->layoutTestController()->shouldHandleErrorPages();
    366 
    367     return false;
    368 }
    369 
    370 bool WebPage::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output)
    371 {
    372     const QWebPage::ErrorPageExtensionOption* info = static_cast<const QWebPage::ErrorPageExtensionOption*>(option);
    373 
    374     // Lets handle error pages for the main frame for now.
    375     if (info->frame != mainFrame())
    376         return false;
    377 
    378     QWebPage::ErrorPageExtensionReturn* errorPage = static_cast<QWebPage::ErrorPageExtensionReturn*>(output);
    379 
    380     errorPage->content = QString("data:text/html,<body/>").toUtf8();
    381 
    382     return true;
    383 }
    384 
    385 QObject* WebPage::createPlugin(const QString& classId, const QUrl& url, const QStringList& paramNames, const QStringList& paramValues)
    386 {
    387     Q_UNUSED(url);
    388     Q_UNUSED(paramNames);
    389     Q_UNUSED(paramValues);
    390 #ifndef QT_NO_UITOOLS
    391     QUiLoader loader;
    392     return loader.createWidget(classId, view());
    393 #else
    394     Q_UNUSED(classId);
    395     return 0;
    396 #endif
    397 }
    398 
    399 void WebPage::setViewGeometry(const QRect& rect)
    400 {
    401     if (WebViewGraphicsBased* v = qobject_cast<WebViewGraphicsBased*>(view()))
    402         v->scene()->setSceneRect(QRectF(rect));
    403     else if (QWidget *v = view())
    404         v->setGeometry(rect);
    405 }
    406 
    407 WebViewGraphicsBased::WebViewGraphicsBased(QWidget* parent)
    408     : m_item(new QGraphicsWebView)
    409 {
    410     setScene(new QGraphicsScene(this));
    411     scene()->addItem(m_item);
    412 }
    413 
    414 DumpRenderTree::DumpRenderTree()
    415     : m_dumpPixels(false)
    416     , m_stdin(0)
    417     , m_enableTextOutput(false)
    418     , m_standAloneMode(false)
    419     , m_graphicsBased(false)
    420     , m_persistentStoragePath(QString(getenv("DUMPRENDERTREE_TEMP")))
    421 {
    422     QByteArray viewMode = getenv("QT_DRT_WEBVIEW_MODE");
    423     if (viewMode == "graphics")
    424         setGraphicsBased(true);
    425 
    426     // Set running in DRT mode for qwebpage to create testable objects.
    427     DumpRenderTreeSupportQt::setDumpRenderTreeModeEnabled(true);
    428     DumpRenderTreeSupportQt::overwritePluginDirectories();
    429     DumpRenderTreeSupportQt::activeMockDeviceOrientationClient(true);
    430     QWebSettings::enablePersistentStorage(m_persistentStoragePath);
    431 
    432     m_networkAccessManager = new NetworkAccessManager(this);
    433     // create our primary testing page/view.
    434     if (isGraphicsBased()) {
    435         WebViewGraphicsBased* view = new WebViewGraphicsBased(0);
    436         m_page = new WebPage(view, this);
    437         view->setPage(m_page);
    438         m_mainView = view;
    439     } else {
    440         QWebView* view = new QWebView(0);
    441         m_page = new WebPage(view, this);
    442         view->setPage(m_page);
    443         m_mainView = view;
    444     }
    445     // Use a frame group name for all pages created by DumpRenderTree to allow
    446     // testing of cross-page frame lookup.
    447     DumpRenderTreeSupportQt::webPageSetGroupName(m_page, "org.webkit.qt.DumpRenderTree");
    448 
    449     m_mainView->setContextMenuPolicy(Qt::NoContextMenu);
    450     m_mainView->resize(QSize(LayoutTestController::maxViewWidth, LayoutTestController::maxViewHeight));
    451 
    452     // clean up cache by resetting quota.
    453     qint64 quota = webPage()->settings()->offlineWebApplicationCacheQuota();
    454     webPage()->settings()->setOfflineWebApplicationCacheQuota(quota);
    455 
    456     // create our controllers. This has to be done before connectFrame,
    457     // as it exports there to the JavaScript DOM window.
    458     m_controller = new LayoutTestController(this);
    459     connect(m_controller, SIGNAL(showPage()), this, SLOT(showPage()));
    460     connect(m_controller, SIGNAL(hidePage()), this, SLOT(hidePage()));
    461 
    462     // async geolocation permission set by controller
    463     connect(m_controller, SIGNAL(geolocationPermissionSet()), this, SLOT(geolocationPermissionSet()));
    464 
    465     connect(m_controller, SIGNAL(done()), this, SLOT(dump()));
    466     m_eventSender = new EventSender(m_page);
    467     m_textInputController = new TextInputController(m_page);
    468     m_plainTextController = new PlainTextController(m_page);
    469     m_gcController = new GCController(m_page);
    470 
    471     // now connect our different signals
    472     connect(m_page, SIGNAL(frameCreated(QWebFrame *)),
    473             this, SLOT(connectFrame(QWebFrame *)));
    474     connectFrame(m_page->mainFrame());
    475 
    476     connect(m_page, SIGNAL(loadFinished(bool)),
    477             m_controller, SLOT(maybeDump(bool)));
    478     // We need to connect to loadStarted() because notifyDone should only
    479     // dump results itself when the last page loaded in the test has finished loading.
    480     connect(m_page, SIGNAL(loadStarted()),
    481             m_controller, SLOT(resetLoadFinished()));
    482     connect(m_page, SIGNAL(windowCloseRequested()), this, SLOT(windowCloseRequested()));
    483     connect(m_page, SIGNAL(printRequested(QWebFrame*)), this, SLOT(dryRunPrint(QWebFrame*)));
    484 
    485     connect(m_page->mainFrame(), SIGNAL(titleChanged(const QString&)),
    486             SLOT(titleChanged(const QString&)));
    487     connect(m_page, SIGNAL(databaseQuotaExceeded(QWebFrame*,QString)),
    488             this, SLOT(dumpDatabaseQuota(QWebFrame*,QString)));
    489     connect(m_page, SIGNAL(applicationCacheQuotaExceeded(QWebSecurityOrigin *, quint64)),
    490             this, SLOT(dumpApplicationCacheQuota(QWebSecurityOrigin *, quint64)));
    491     connect(m_page, SIGNAL(statusBarMessage(const QString&)),
    492             this, SLOT(statusBarMessage(const QString&)));
    493 
    494     QObject::connect(this, SIGNAL(quit()), qApp, SLOT(quit()), Qt::QueuedConnection);
    495 
    496     DumpRenderTreeSupportQt::setDumpRenderTreeModeEnabled(true);
    497     QFocusEvent event(QEvent::FocusIn, Qt::ActiveWindowFocusReason);
    498     QApplication::sendEvent(m_mainView, &event);
    499 }
    500 
    501 DumpRenderTree::~DumpRenderTree()
    502 {
    503     if (!m_redirectOutputFileName.isEmpty())
    504         fclose(stdout);
    505     if (!m_redirectErrorFileName.isEmpty())
    506         fclose(stderr);
    507     delete m_mainView;
    508     delete m_stdin;
    509     DumpRenderTreeSupportQt::removeMockDeviceOrientation();
    510 }
    511 
    512 static void clearHistory(QWebPage* page)
    513 {
    514     // QWebHistory::clear() leaves current page, so remove it as well by setting
    515     // max item count to 0, and then setting it back to it's original value.
    516 
    517     QWebHistory* history = page->history();
    518     int itemCount = history->maximumItemCount();
    519 
    520     history->clear();
    521     history->setMaximumItemCount(0);
    522     history->setMaximumItemCount(itemCount);
    523 }
    524 
    525 void DumpRenderTree::dryRunPrint(QWebFrame* frame)
    526 {
    527 #ifndef QT_NO_PRINTER
    528     NullPrinter printer;
    529     frame->print(&printer);
    530 #endif
    531 }
    532 
    533 void DumpRenderTree::resetToConsistentStateBeforeTesting(const QUrl& url)
    534 {
    535     // reset so that any current loads are stopped
    536     // NOTE: that this has to be done before the layoutTestController is
    537     // reset or we get timeouts for some tests.
    538     m_page->blockSignals(true);
    539     m_page->triggerAction(QWebPage::Stop);
    540     m_page->blockSignals(false);
    541 
    542     QList<QWebSecurityOrigin> knownOrigins = QWebSecurityOrigin::allOrigins();
    543     for (int i = 0; i < knownOrigins.size(); ++i)
    544         knownOrigins[i].setDatabaseQuota(databaseDefaultQuota);
    545 
    546     // reset the layoutTestController at this point, so that we under no
    547     // circumstance dump (stop the waitUntilDone timer) during the reset
    548     // of the DRT.
    549     m_controller->reset();
    550 
    551     // reset mouse clicks counter
    552     m_eventSender->resetClickCount();
    553 
    554     closeRemainingWindows();
    555 
    556     m_page->resetSettings();
    557 #ifndef QT_NO_UNDOSTACK
    558     m_page->undoStack()->clear();
    559 #endif
    560     m_page->mainFrame()->setZoomFactor(1.0);
    561     clearHistory(m_page);
    562     DumpRenderTreeSupportQt::clearFrameName(m_page->mainFrame());
    563 
    564     m_page->mainFrame()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAsNeeded);
    565     m_page->mainFrame()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAsNeeded);
    566 
    567     if (url.scheme() == "http" || url.scheme() == "https") {
    568         // credentials may exist from previous tests.
    569         m_page->setNetworkAccessManager(0);
    570         delete m_networkAccessManager;
    571         m_networkAccessManager = new NetworkAccessManager(this);
    572         m_page->setNetworkAccessManager(m_networkAccessManager);
    573     }
    574 
    575     WorkQueue::shared()->clear();
    576     WorkQueue::shared()->setFrozen(false);
    577 
    578     DumpRenderTreeSupportQt::resetOriginAccessWhiteLists();
    579 
    580     // Qt defaults to Windows editing behavior.
    581     DumpRenderTreeSupportQt::setEditingBehavior(m_page, "win");
    582 
    583     QLocale::setDefault(QLocale::c());
    584 
    585     layoutTestController()->setDeveloperExtrasEnabled(true);
    586 #ifndef Q_OS_WINCE
    587     setlocale(LC_ALL, "");
    588 #endif
    589 
    590     DumpRenderTreeSupportQt::clearOpener(m_page->mainFrame());
    591 }
    592 
    593 static bool isGlobalHistoryTest(const QUrl& url)
    594 {
    595     if (url.path().contains("globalhistory/"))
    596         return true;
    597     return false;
    598 }
    599 
    600 static bool isWebInspectorTest(const QUrl& url)
    601 {
    602     if (url.path().contains("inspector/"))
    603         return true;
    604     return false;
    605 }
    606 
    607 static bool isDumpAsTextTest(const QUrl& url)
    608 {
    609     if (url.path().contains("dumpAsText/"))
    610         return true;
    611     return false;
    612 }
    613 
    614 
    615 void DumpRenderTree::open(const QUrl& url)
    616 {
    617     DumpRenderTreeSupportQt::dumpResourceLoadCallbacksPath(QFileInfo(url.toString()).path());
    618     resetToConsistentStateBeforeTesting(url);
    619 
    620     if (isWebInspectorTest(m_page->mainFrame()->url()))
    621         layoutTestController()->closeWebInspector();
    622 
    623     if (isWebInspectorTest(url))
    624         layoutTestController()->showWebInspector();
    625 
    626     if (isDumpAsTextTest(url)) {
    627         layoutTestController()->dumpAsText();
    628         setDumpPixels(false);
    629     }
    630 
    631     if (isGlobalHistoryTest(url))
    632         layoutTestController()->dumpHistoryCallbacks();
    633 
    634     // W3C SVG tests expect to be 480x360
    635     bool isW3CTest = url.toString().contains("svg/W3C-SVG-1.1");
    636     int width = isW3CTest ? 480 : LayoutTestController::maxViewWidth;
    637     int height = isW3CTest ? 360 : LayoutTestController::maxViewHeight;
    638     m_mainView->resize(QSize(width, height));
    639     m_page->setPreferredContentsSize(QSize());
    640     m_page->setViewportSize(QSize(width, height));
    641 
    642     QFocusEvent ev(QEvent::FocusIn);
    643     m_page->event(&ev);
    644 
    645     QWebSettings::clearMemoryCaches();
    646 #if !(defined(Q_OS_SYMBIAN) && QT_VERSION <= QT_VERSION_CHECK(4, 6, 2))
    647     QFontDatabase::removeAllApplicationFonts();
    648 #endif
    649 #if defined(Q_WS_X11)
    650     initializeFonts();
    651 #endif
    652 
    653     DumpRenderTreeSupportQt::dumpFrameLoader(url.toString().contains("loading/"));
    654     setTextOutputEnabled(true);
    655     m_page->mainFrame()->load(url);
    656 }
    657 
    658 void DumpRenderTree::readLine()
    659 {
    660     if (!m_stdin) {
    661         m_stdin = new QFile;
    662         m_stdin->open(stdin, QFile::ReadOnly);
    663 
    664         if (!m_stdin->isReadable()) {
    665             emit quit();
    666             return;
    667         }
    668     }
    669 
    670     QByteArray line = m_stdin->readLine().trimmed();
    671 
    672     if (line.isEmpty()) {
    673         emit quit();
    674         return;
    675     }
    676 
    677     processLine(QString::fromLocal8Bit(line.constData(), line.length()));
    678 }
    679 
    680 void DumpRenderTree::processArgsLine(const QStringList &args)
    681 {
    682     setStandAloneMode(true);
    683 
    684     m_standAloneModeTestList = args;
    685 
    686     QFileInfo firstEntry(m_standAloneModeTestList.first());
    687     if (firstEntry.isDir()) {
    688         QDir folderEntry(m_standAloneModeTestList.first());
    689         QStringList supportedExt;
    690         // Check for all supported extensions (from Scripts/webkitpy/layout_tests/layout_package/test_files.py).
    691         supportedExt << "*.html" << "*.shtml" << "*.xml" << "*.xhtml" << "*.xhtmlmp" << "*.pl" << "*.php" << "*.svg";
    692         m_standAloneModeTestList = folderEntry.entryList(supportedExt, QDir::Files);
    693         for (int i = 0; i < m_standAloneModeTestList.size(); ++i)
    694             m_standAloneModeTestList[i] = folderEntry.absoluteFilePath(m_standAloneModeTestList[i]);
    695     }
    696     connect(this, SIGNAL(ready()), this, SLOT(loadNextTestInStandAloneMode()));
    697 
    698     if (!m_standAloneModeTestList.isEmpty()) {
    699         QString first = m_standAloneModeTestList.takeFirst();
    700         processLine(first);
    701     }
    702 }
    703 
    704 void DumpRenderTree::loadNextTestInStandAloneMode()
    705 {
    706     if (m_standAloneModeTestList.isEmpty()) {
    707         emit quit();
    708         return;
    709     }
    710     QString first = m_standAloneModeTestList.takeFirst();
    711     processLine(first);
    712 }
    713 
    714 void DumpRenderTree::processLine(const QString &input)
    715 {
    716     QString line = input;
    717 
    718     m_expectedHash = QString();
    719     if (m_dumpPixels) {
    720         // single quote marks the pixel dump hash
    721         int i = line.indexOf('\'');
    722         if (i > -1) {
    723             m_expectedHash = line.mid(i + 1, line.length());
    724             line.remove(i, line.length());
    725         }
    726     }
    727 
    728     if (line.startsWith(QLatin1String("http:"))
    729             || line.startsWith(QLatin1String("https:"))
    730             || line.startsWith(QLatin1String("file:"))) {
    731         open(QUrl(line));
    732     } else {
    733         QFileInfo fi(line);
    734 
    735         if (!fi.exists()) {
    736             QDir currentDir = QDir::currentPath();
    737 
    738             // Try to be smart about where the test is located
    739             if (currentDir.dirName() == QLatin1String("LayoutTests"))
    740                 fi = QFileInfo(currentDir, line.replace(QRegExp(".*?LayoutTests/(.*)"), "\\1"));
    741             else if (!line.contains(QLatin1String("LayoutTests")))
    742                 fi = QFileInfo(currentDir, line.prepend(QLatin1String("LayoutTests/")));
    743 
    744             if (!fi.exists()) {
    745                 emit ready();
    746                 return;
    747             }
    748         }
    749 
    750         open(QUrl::fromLocalFile(fi.absoluteFilePath()));
    751     }
    752 
    753     fflush(stdout);
    754 }
    755 
    756 void DumpRenderTree::setDumpPixels(bool dump)
    757 {
    758     m_dumpPixels = dump;
    759 }
    760 
    761 void DumpRenderTree::closeRemainingWindows()
    762 {
    763     foreach (QObject* widget, windows)
    764         delete widget;
    765     windows.clear();
    766 }
    767 
    768 void DumpRenderTree::initJSObjects()
    769 {
    770     QWebFrame *frame = qobject_cast<QWebFrame*>(sender());
    771     Q_ASSERT(frame);
    772     frame->addToJavaScriptWindowObject(QLatin1String("layoutTestController"), m_controller);
    773     frame->addToJavaScriptWindowObject(QLatin1String("eventSender"), m_eventSender);
    774     frame->addToJavaScriptWindowObject(QLatin1String("textInputController"), m_textInputController);
    775     frame->addToJavaScriptWindowObject(QLatin1String("GCController"), m_gcController);
    776     frame->addToJavaScriptWindowObject(QLatin1String("plainText"), m_plainTextController);
    777 }
    778 
    779 void DumpRenderTree::showPage()
    780 {
    781     m_mainView->show();
    782     // we need a paint event but cannot process all the events
    783     QPixmap pixmap(m_mainView->size());
    784     m_mainView->render(&pixmap);
    785 }
    786 
    787 void DumpRenderTree::hidePage()
    788 {
    789     m_mainView->hide();
    790 }
    791 
    792 QString DumpRenderTree::dumpFrameScrollPosition(QWebFrame* frame)
    793 {
    794     if (!frame || !DumpRenderTreeSupportQt::hasDocumentElement(frame))
    795         return QString();
    796 
    797     QString result;
    798     QPoint pos = frame->scrollPosition();
    799     if (pos.x() > 0 || pos.y() > 0) {
    800         QWebFrame* parent = qobject_cast<QWebFrame *>(frame->parent());
    801         if (parent)
    802             result.append(QString("frame '%1' ").arg(frame->title()));
    803         result.append(QString("scrolled to %1,%2\n").arg(pos.x()).arg(pos.y()));
    804     }
    805 
    806     if (m_controller->shouldDumpChildFrameScrollPositions()) {
    807         QList<QWebFrame*> children = frame->childFrames();
    808         for (int i = 0; i < children.size(); ++i)
    809             result += dumpFrameScrollPosition(children.at(i));
    810     }
    811     return result;
    812 }
    813 
    814 QString DumpRenderTree::dumpFramesAsText(QWebFrame* frame)
    815 {
    816     if (!frame || !DumpRenderTreeSupportQt::hasDocumentElement(frame))
    817         return QString();
    818 
    819     QString result;
    820     QWebFrame* parent = qobject_cast<QWebFrame*>(frame->parent());
    821     if (parent) {
    822         result.append(QLatin1String("\n--------\nFrame: '"));
    823         result.append(frame->frameName());
    824         result.append(QLatin1String("'\n--------\n"));
    825     }
    826 
    827     QString innerText = frame->toPlainText();
    828     result.append(innerText);
    829     result.append(QLatin1String("\n"));
    830 
    831     if (m_controller->shouldDumpChildrenAsText()) {
    832         QList<QWebFrame *> children = frame->childFrames();
    833         for (int i = 0; i < children.size(); ++i)
    834             result += dumpFramesAsText(children.at(i));
    835     }
    836 
    837     return result;
    838 }
    839 
    840 static QString dumpHistoryItem(const QWebHistoryItem& item, int indent, bool current)
    841 {
    842     QString result;
    843 
    844     int start = 0;
    845     if (current) {
    846         result.append(QLatin1String("curr->"));
    847         start = 6;
    848     }
    849     for (int i = start; i < indent; i++)
    850         result.append(' ');
    851 
    852     QString url = item.url().toEncoded();
    853     if (url.contains("file://")) {
    854         static QString layoutTestsString("/LayoutTests/");
    855         static QString fileTestString("(file test):");
    856 
    857         QString res = url.mid(url.indexOf(layoutTestsString) + layoutTestsString.length());
    858         if (res.isEmpty())
    859             return result;
    860 
    861         result.append(fileTestString);
    862         result.append(res);
    863     } else {
    864         result.append(url);
    865     }
    866 
    867     QString target = DumpRenderTreeSupportQt::historyItemTarget(item);
    868     if (!target.isEmpty())
    869         result.append(QString(QLatin1String(" (in frame \"%1\")")).arg(target));
    870 
    871     if (DumpRenderTreeSupportQt::isTargetItem(item))
    872         result.append(QLatin1String("  **nav target**"));
    873     result.append(QLatin1String("\n"));
    874 
    875     QMap<QString, QWebHistoryItem> children = DumpRenderTreeSupportQt::getChildHistoryItems(item);
    876     foreach (QWebHistoryItem item, children)
    877         result += dumpHistoryItem(item, 12, false);
    878 
    879     return result;
    880 }
    881 
    882 QString DumpRenderTree::dumpBackForwardList(QWebPage* page)
    883 {
    884     QWebHistory* history = page->history();
    885 
    886     QString result;
    887     result.append(QLatin1String("\n============== Back Forward List ==============\n"));
    888 
    889     // FORMAT:
    890     // "        (file test):fast/loader/resources/click-fragment-link.html  **nav target**"
    891     // "curr->  (file test):fast/loader/resources/click-fragment-link.html#testfragment  **nav target**"
    892 
    893     int maxItems = history->maximumItemCount();
    894 
    895     foreach (const QWebHistoryItem item, history->backItems(maxItems)) {
    896         if (!item.isValid())
    897             continue;
    898         result.append(dumpHistoryItem(item, 8, false));
    899     }
    900 
    901     QWebHistoryItem item = history->currentItem();
    902     if (item.isValid())
    903         result.append(dumpHistoryItem(item, 8, true));
    904 
    905     foreach (const QWebHistoryItem item, history->forwardItems(maxItems)) {
    906         if (!item.isValid())
    907             continue;
    908         result.append(dumpHistoryItem(item, 8, false));
    909     }
    910 
    911     result.append(QLatin1String("===============================================\n"));
    912     return result;
    913 }
    914 
    915 static const char *methodNameStringForFailedTest(LayoutTestController *controller)
    916 {
    917     const char *errorMessage;
    918     if (controller->shouldDumpAsText())
    919         errorMessage = "[documentElement innerText]";
    920     // FIXME: Add when we have support
    921     //else if (controller->dumpDOMAsWebArchive())
    922     //    errorMessage = "[[mainFrame DOMDocument] webArchive]";
    923     //else if (controller->dumpSourceAsWebArchive())
    924     //    errorMessage = "[[mainFrame dataSource] webArchive]";
    925     else
    926         errorMessage = "[mainFrame renderTreeAsExternalRepresentation]";
    927 
    928     return errorMessage;
    929 }
    930 
    931 void DumpRenderTree::dump()
    932 {
    933     // Prevent any further frame load or resource load callbacks from appearing after we dump the result.
    934     DumpRenderTreeSupportQt::dumpFrameLoader(false);
    935     DumpRenderTreeSupportQt::dumpResourceLoadCallbacks(false);
    936 
    937     QWebFrame *mainFrame = m_page->mainFrame();
    938 
    939     if (isStandAloneMode()) {
    940         QString markup = mainFrame->toHtml();
    941         fprintf(stdout, "Source:\n\n%s\n", markup.toUtf8().constData());
    942     }
    943 
    944     QString mimeType = DumpRenderTreeSupportQt::responseMimeType(mainFrame);
    945     if (mimeType == "text/plain")
    946         m_controller->dumpAsText();
    947 
    948     // Dump render text...
    949     QString resultString;
    950     if (m_controller->shouldDumpAsText())
    951         resultString = dumpFramesAsText(mainFrame);
    952     else {
    953         resultString = mainFrame->renderTreeDump();
    954         resultString += dumpFrameScrollPosition(mainFrame);
    955     }
    956     if (!resultString.isEmpty()) {
    957         fprintf(stdout, "Content-Type: text/plain\n");
    958         fprintf(stdout, "%s", resultString.toUtf8().constData());
    959 
    960         if (m_controller->shouldDumpBackForwardList()) {
    961             fprintf(stdout, "%s", dumpBackForwardList(webPage()).toUtf8().constData());
    962             foreach (QObject* widget, windows) {
    963                 QWebPage* page = qobject_cast<QWebPage*>(widget->findChild<QWebPage*>());
    964                 fprintf(stdout, "%s", dumpBackForwardList(page).toUtf8().constData());
    965             }
    966         }
    967 
    968     } else
    969         printf("ERROR: nil result from %s", methodNameStringForFailedTest(m_controller));
    970 
    971     // signal end of text block
    972     fputs("#EOF\n", stdout);
    973     fputs("#EOF\n", stderr);
    974 
    975     // FIXME: All other ports don't dump pixels, if generatePixelResults is false.
    976     if (m_dumpPixels) {
    977         QImage image(m_page->viewportSize(), QImage::Format_ARGB32);
    978         image.fill(Qt::white);
    979         QPainter painter(&image);
    980         mainFrame->render(&painter);
    981         painter.end();
    982 
    983         QCryptographicHash hash(QCryptographicHash::Md5);
    984         for (int row = 0; row < image.height(); ++row)
    985             hash.addData(reinterpret_cast<const char*>(image.scanLine(row)), image.width() * 4);
    986         QString actualHash = hash.result().toHex();
    987 
    988         fprintf(stdout, "\nActualHash: %s\n", qPrintable(actualHash));
    989 
    990         bool dumpImage = true;
    991 
    992         if (!m_expectedHash.isEmpty()) {
    993             Q_ASSERT(m_expectedHash.length() == 32);
    994             fprintf(stdout, "\nExpectedHash: %s\n", qPrintable(m_expectedHash));
    995 
    996             if (m_expectedHash == actualHash)
    997                 dumpImage = false;
    998         }
    999 
   1000         if (dumpImage) {
   1001             image.setText("checksum", actualHash);
   1002 
   1003             QBuffer buffer;
   1004             buffer.open(QBuffer::WriteOnly);
   1005             image.save(&buffer, "PNG");
   1006             buffer.close();
   1007             const QByteArray &data = buffer.data();
   1008 
   1009             printf("Content-Type: %s\n", "image/png");
   1010             printf("Content-Length: %lu\n", static_cast<unsigned long>(data.length()));
   1011 
   1012             const quint32 bytesToWriteInOneChunk = 1 << 15;
   1013             quint32 dataRemainingToWrite = data.length();
   1014             const char *ptr = data.data();
   1015             while (dataRemainingToWrite) {
   1016                 quint32 bytesToWriteInThisChunk = qMin(dataRemainingToWrite, bytesToWriteInOneChunk);
   1017                 quint32 bytesWritten = fwrite(ptr, 1, bytesToWriteInThisChunk, stdout);
   1018                 if (bytesWritten != bytesToWriteInThisChunk)
   1019                     break;
   1020                 dataRemainingToWrite -= bytesWritten;
   1021                 ptr += bytesWritten;
   1022             }
   1023         }
   1024 
   1025         fflush(stdout);
   1026     }
   1027 
   1028     puts("#EOF");   // terminate the (possibly empty) pixels block
   1029 
   1030     fflush(stdout);
   1031     fflush(stderr);
   1032 
   1033      emit ready();
   1034 }
   1035 
   1036 void DumpRenderTree::titleChanged(const QString &s)
   1037 {
   1038     if (m_controller->shouldDumpTitleChanges())
   1039         printf("TITLE CHANGED: %s\n", s.toUtf8().data());
   1040 }
   1041 
   1042 void DumpRenderTree::connectFrame(QWebFrame *frame)
   1043 {
   1044     connect(frame, SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(initJSObjects()));
   1045     connect(frame, SIGNAL(provisionalLoad()),
   1046             layoutTestController(), SLOT(provisionalLoad()));
   1047 }
   1048 
   1049 void DumpRenderTree::dumpDatabaseQuota(QWebFrame* frame, const QString& dbName)
   1050 {
   1051     if (!m_controller->shouldDumpDatabaseCallbacks())
   1052         return;
   1053     QWebSecurityOrigin origin = frame->securityOrigin();
   1054     printf("UI DELEGATE DATABASE CALLBACK: exceededDatabaseQuotaForSecurityOrigin:{%s, %s, %i} database:%s\n",
   1055            origin.scheme().toUtf8().data(),
   1056            origin.host().toUtf8().data(),
   1057            origin.port(),
   1058            dbName.toUtf8().data());
   1059     origin.setDatabaseQuota(databaseDefaultQuota);
   1060 }
   1061 
   1062 void DumpRenderTree::dumpApplicationCacheQuota(QWebSecurityOrigin* origin, quint64 defaultOriginQuota)
   1063 {
   1064     if (!m_controller->shouldDumpApplicationCacheDelegateCallbacks())
   1065         return;
   1066 
   1067     printf("UI DELEGATE APPLICATION CACHE CALLBACK: exceededApplicationCacheOriginQuotaForSecurityOrigin:{%s, %s, %i}\n",
   1068            origin->scheme().toUtf8().data(),
   1069            origin->host().toUtf8().data(),
   1070            origin->port()
   1071            );
   1072     origin->setApplicationCacheQuota(defaultOriginQuota);
   1073 }
   1074 
   1075 void DumpRenderTree::statusBarMessage(const QString& message)
   1076 {
   1077     if (!m_controller->shouldDumpStatusCallbacks())
   1078         return;
   1079 
   1080     printf("UI DELEGATE STATUS CALLBACK: setStatusText:%s\n", message.toUtf8().constData());
   1081 }
   1082 
   1083 QWebPage *DumpRenderTree::createWindow()
   1084 {
   1085     if (!m_controller->canOpenWindows())
   1086         return 0;
   1087 
   1088     // Create a dummy container object to track the page in DRT.
   1089     // QObject is used instead of QWidget to prevent DRT from
   1090     // showing the main view when deleting the container.
   1091 
   1092     QObject* container = new QObject(m_mainView);
   1093     // create a QWebPage we want to return
   1094     QWebPage* page = static_cast<QWebPage*>(new WebPage(container, this));
   1095     // gets cleaned up in closeRemainingWindows()
   1096     windows.append(container);
   1097 
   1098     // connect the needed signals to the page
   1099     connect(page, SIGNAL(frameCreated(QWebFrame*)), this, SLOT(connectFrame(QWebFrame*)));
   1100     connectFrame(page->mainFrame());
   1101     connect(page, SIGNAL(loadFinished(bool)), m_controller, SLOT(maybeDump(bool)));
   1102     connect(page, SIGNAL(windowCloseRequested()), this, SLOT(windowCloseRequested()));
   1103 
   1104     // Use a frame group name for all pages created by DumpRenderTree to allow
   1105     // testing of cross-page frame lookup.
   1106     DumpRenderTreeSupportQt::webPageSetGroupName(page, "org.webkit.qt.DumpRenderTree");
   1107 
   1108     return page;
   1109 }
   1110 
   1111 void DumpRenderTree::windowCloseRequested()
   1112 {
   1113     QWebPage* page = qobject_cast<QWebPage*>(sender());
   1114     QObject* container = page->parent();
   1115     windows.removeAll(container);
   1116     container->deleteLater();
   1117 }
   1118 
   1119 int DumpRenderTree::windowCount() const
   1120 {
   1121 // include the main view in the count
   1122     return windows.count() + 1;
   1123 }
   1124 
   1125 void DumpRenderTree::geolocationPermissionSet()
   1126 {
   1127     m_page->permissionSet(QWebPage::Geolocation);
   1128 }
   1129 
   1130 void DumpRenderTree::switchFocus(bool focused)
   1131 {
   1132     QFocusEvent event((focused) ? QEvent::FocusIn : QEvent::FocusOut, Qt::ActiveWindowFocusReason);
   1133     if (!isGraphicsBased())
   1134         QApplication::sendEvent(m_mainView, &event);
   1135     else {
   1136         if (WebViewGraphicsBased* view = qobject_cast<WebViewGraphicsBased*>(m_mainView))
   1137             view->scene()->sendEvent(view->graphicsView(), &event);
   1138     }
   1139 
   1140 }
   1141 
   1142 QList<WebPage*> DumpRenderTree::getAllPages() const
   1143 {
   1144     QList<WebPage*> pages;
   1145     pages.append(m_page);
   1146     foreach (QObject* widget, windows) {
   1147         if (WebPage* page = widget->findChild<WebPage*>())
   1148             pages.append(page);
   1149     }
   1150     return pages;
   1151 }
   1152 
   1153 #if defined(Q_WS_X11)
   1154 void DumpRenderTree::initializeFonts()
   1155 {
   1156     static int numFonts = -1;
   1157 
   1158     // Some test cases may add or remove application fonts (via @font-face).
   1159     // Make sure to re-initialize the font set if necessary.
   1160     FcFontSet* appFontSet = FcConfigGetFonts(0, FcSetApplication);
   1161     if (appFontSet && numFonts >= 0 && appFontSet->nfont == numFonts)
   1162         return;
   1163 
   1164     QByteArray fontDir = getenv("WEBKIT_TESTFONTS");
   1165     if (fontDir.isEmpty() || !QDir(fontDir).exists()) {
   1166         fprintf(stderr,
   1167                 "\n\n"
   1168                 "----------------------------------------------------------------------\n"
   1169                 "WEBKIT_TESTFONTS environment variable is not set correctly.\n"
   1170                 "This variable has to point to the directory containing the fonts\n"
   1171                 "you can clone from git://gitorious.org/qtwebkit/testfonts.git\n"
   1172                 "----------------------------------------------------------------------\n"
   1173                );
   1174         exit(1);
   1175     }
   1176     char currentPath[PATH_MAX+1];
   1177     if (!getcwd(currentPath, PATH_MAX))
   1178         qFatal("Couldn't get current working directory");
   1179     QByteArray configFile = currentPath;
   1180     FcConfig *config = FcConfigCreate();
   1181     configFile += "/Tools/DumpRenderTree/qt/fonts.conf";
   1182     if (!FcConfigParseAndLoad (config, (FcChar8*) configFile.data(), true))
   1183         qFatal("Couldn't load font configuration file");
   1184     if (!FcConfigAppFontAddDir (config, (FcChar8*) fontDir.data()))
   1185         qFatal("Couldn't add font dir!");
   1186     FcConfigSetCurrent(config);
   1187 
   1188     appFontSet = FcConfigGetFonts(config, FcSetApplication);
   1189     numFonts = appFontSet->nfont;
   1190 }
   1191 #endif
   1192 
   1193 }
   1194