• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "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 
NetworkAccessManager(QObject * parent)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
sslErrorsEncountered(QNetworkReply * reply,const QList<QSslError> & errors)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:
begin(QPaintDevice *)123         virtual bool begin(QPaintDevice*) { return true; }
end()124         virtual bool end() { return true; }
type() const125         virtual QPaintEngine::Type type() const { return QPaintEngine::User; }
drawPixmap(const QRectF & r,const QPixmap & pm,const QRectF & sr)126         virtual void drawPixmap(const QRectF& r, const QPixmap& pm, const QRectF& sr) { }
updateState(const QPaintEngineState & state)127         virtual void updateState(const QPaintEngineState& state) { }
128     };
129 
paintEngine() const130     virtual QPaintEngine* paintEngine() const { return const_cast<NullPaintEngine*>(&m_engine); }
131 
132     NullPaintEngine m_engine;
133 };
134 #endif
135 
WebPage(QObject * parent,DumpRenderTree * drt)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 
~WebPage()167 WebPage::~WebPage()
168 {
169     delete m_webInspector;
170 }
171 
webInspector()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 
resetSettings()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 
createWindow(QWebPage::WebWindowType)214 QWebPage *WebPage::createWindow(QWebPage::WebWindowType)
215 {
216     return m_drt->createWindow();
217 }
218 
javaScriptAlert(QWebFrame *,const QString & message)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 
requestPermission(QWebFrame * frame,QWebPage::Feature feature)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 
cancelPermission(QWebFrame * frame,QWebPage::Feature feature)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 
permissionSet(QWebPage::Feature feature)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 
urlSuitableForTestResult(const QString & url)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 
javaScriptConsoleMessage(const QString & message,int lineNumber,const QString &)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 
javaScriptConfirm(QWebFrame *,const QString & msg)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 
javaScriptPrompt(QWebFrame *,const QString & msg,const QString & defaultValue,QString * result)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 
acceptNavigationRequest(QWebFrame * frame,const QNetworkRequest & request,NavigationType type)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 
supportsExtension(QWebPage::Extension extension) const362 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 
extension(Extension extension,const ExtensionOption * option,ExtensionReturn * output)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 
createPlugin(const QString & classId,const QUrl & url,const QStringList & paramNames,const QStringList & paramValues)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 
setViewGeometry(const QRect & rect)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 
WebViewGraphicsBased(QWidget * parent)407 WebViewGraphicsBased::WebViewGraphicsBased(QWidget* parent)
408     : m_item(new QGraphicsWebView)
409 {
410     setScene(new QGraphicsScene(this));
411     scene()->addItem(m_item);
412 }
413 
DumpRenderTree()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 
~DumpRenderTree()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 
clearHistory(QWebPage * page)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 
dryRunPrint(QWebFrame * frame)525 void DumpRenderTree::dryRunPrint(QWebFrame* frame)
526 {
527 #ifndef QT_NO_PRINTER
528     NullPrinter printer;
529     frame->print(&printer);
530 #endif
531 }
532 
resetToConsistentStateBeforeTesting(const QUrl & url)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 
isGlobalHistoryTest(const QUrl & url)593 static bool isGlobalHistoryTest(const QUrl& url)
594 {
595     if (url.path().contains("globalhistory/"))
596         return true;
597     return false;
598 }
599 
isWebInspectorTest(const QUrl & url)600 static bool isWebInspectorTest(const QUrl& url)
601 {
602     if (url.path().contains("inspector/"))
603         return true;
604     return false;
605 }
606 
isDumpAsTextTest(const QUrl & url)607 static bool isDumpAsTextTest(const QUrl& url)
608 {
609     if (url.path().contains("dumpAsText/"))
610         return true;
611     return false;
612 }
613 
614 
open(const QUrl & url)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 
readLine()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 
processArgsLine(const QStringList & args)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 
loadNextTestInStandAloneMode()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 
processLine(const QString & input)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 
setDumpPixels(bool dump)756 void DumpRenderTree::setDumpPixels(bool dump)
757 {
758     m_dumpPixels = dump;
759 }
760 
closeRemainingWindows()761 void DumpRenderTree::closeRemainingWindows()
762 {
763     foreach (QObject* widget, windows)
764         delete widget;
765     windows.clear();
766 }
767 
initJSObjects()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 
showPage()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 
hidePage()787 void DumpRenderTree::hidePage()
788 {
789     m_mainView->hide();
790 }
791 
dumpFrameScrollPosition(QWebFrame * frame)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 
dumpFramesAsText(QWebFrame * frame)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 
dumpHistoryItem(const QWebHistoryItem & item,int indent,bool current)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 
dumpBackForwardList(QWebPage * page)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 
methodNameStringForFailedTest(LayoutTestController * controller)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 
dump()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 
titleChanged(const QString & s)1036 void DumpRenderTree::titleChanged(const QString &s)
1037 {
1038     if (m_controller->shouldDumpTitleChanges())
1039         printf("TITLE CHANGED: %s\n", s.toUtf8().data());
1040 }
1041 
connectFrame(QWebFrame * frame)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 
dumpDatabaseQuota(QWebFrame * frame,const QString & dbName)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 
dumpApplicationCacheQuota(QWebSecurityOrigin * origin,quint64 defaultOriginQuota)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 
statusBarMessage(const QString & message)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 
createWindow()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 
windowCloseRequested()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 
windowCount() const1119 int DumpRenderTree::windowCount() const
1120 {
1121 // include the main view in the count
1122     return windows.count() + 1;
1123 }
1124 
geolocationPermissionSet()1125 void DumpRenderTree::geolocationPermissionSet()
1126 {
1127     m_page->permissionSet(QWebPage::Geolocation);
1128 }
1129 
switchFocus(bool focused)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 
getAllPages() const1142 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)
initializeFonts()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