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