1/*
2 * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)
3 * Copyright (C) 2009 Girish Ramakrishnan <girish@forwardbias.in>
4 * Copyright (C) 2006 George Staikos <staikos@kde.org>
5 * Copyright (C) 2006 Dirk Mueller <mueller@kde.org>
6 * Copyright (C) 2006 Zack Rusin <zack@kde.org>
7 * Copyright (C) 2006 Simon Hausmann <hausmann@kde.org>
8 *
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
21 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
28 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include "launcherwindow.h"
34#include "urlloader.h"
35
36WindowOptions windowOptions;
37
38
39#include <QDir>
40#include <QFile>
41#include <QFileInfo>
42#include <QFontDatabase>
43
44
45#if defined(Q_WS_X11)
46#include <fontconfig/fontconfig.h>
47#endif
48
49
50#if defined(Q_WS_X11)
51// Very similar to WebCore::DumpRenderTree::initializeFonts();
52// Duplicated here so that QtTestBrowser would display contents
53// with the same fonts as run-webkit-tests/DumpRenderTree.
54static void initTestFonts()
55{
56    static int numFonts = -1;
57
58    // Some test cases may add or remove application fonts (via @font-face).
59    // Make sure to re-initialize the font set if necessary.
60    FcFontSet* appFontSet = FcConfigGetFonts(0, FcSetApplication);
61    if (appFontSet && numFonts >= 0 && appFontSet->nfont == numFonts)
62        return;
63
64    QByteArray fontDir = getenv("WEBKIT_TESTFONTS");
65    if (fontDir.isEmpty() || !QDir(fontDir).exists()) {
66        fprintf(stderr,
67                "\n\n"
68                "----------------------------------------------------------------------\n"
69                "WEBKIT_TESTFONTS environment variable is not set correctly.\n"
70                "This variable has to point to the directory containing the fonts\n"
71                "you can clone from git://gitorious.org/qtwebkit/testfonts.git\n"
72                "----------------------------------------------------------------------\n"
73               );
74        exit(1);
75    }
76    // Looks for qt/fonts.conf relative to the directory of the QtTestBrowser
77    // executable.
78    QString configFileString = QCoreApplication::applicationDirPath();
79    configFileString += "/../../../Tools/DumpRenderTree/qt/fonts.conf";
80    QByteArray configFileArray = configFileString.toUtf8();
81    FcConfig* config = FcConfigCreate();
82    if (!FcConfigParseAndLoad (config, (FcChar8*) configFileArray.data(), true))
83        qFatal("Couldn't load font configuration file");
84    if (!FcConfigAppFontAddDir (config, (FcChar8*) fontDir.data()))
85        qFatal("Couldn't add font dir!");
86    FcConfigSetCurrent(config);
87
88    appFontSet = FcConfigGetFonts(config, FcSetApplication);
89    numFonts = appFontSet->nfont;
90}
91#endif
92
93int launcherMain(const QApplication& app)
94{
95#ifdef Q_WS_X11
96    if (windowOptions.useTestFonts)
97        initTestFonts();
98#endif
99
100#ifndef NDEBUG
101    int retVal = app.exec();
102    DumpRenderTreeSupportQt::garbageCollectorCollect();
103    QWebSettings::clearMemoryCaches();
104    return retVal;
105#else
106    return app.exec();
107#endif
108}
109
110class LauncherApplication : public QApplication {
111    Q_OBJECT
112
113public:
114    LauncherApplication(int& argc, char** argv);
115    QStringList urls() const { return m_urls; }
116    bool isRobotized() const { return m_isRobotized; }
117    int robotTimeout() const { return m_robotTimeoutSeconds; }
118    int robotExtraTime() const { return m_robotExtraTimeSeconds; }
119
120private:
121    void handleUserOptions();
122    void applyDefaultSettings();
123
124private:
125    bool m_isRobotized;
126    int m_robotTimeoutSeconds;
127    int m_robotExtraTimeSeconds;
128    QStringList m_urls;
129};
130
131void LauncherApplication::applyDefaultSettings()
132{
133    QWebSettings::setMaximumPagesInCache(4);
134
135    QWebSettings::setObjectCacheCapacities((16*1024*1024) / 8, (16*1024*1024) / 8, 16*1024*1024);
136
137    QWebSettings::globalSettings()->setAttribute(QWebSettings::PluginsEnabled, true);
138    QWebSettings::globalSettings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
139    QWebSettings::enablePersistentStorage();
140}
141
142LauncherApplication::LauncherApplication(int& argc, char** argv)
143    : QApplication(argc, argv, QApplication::GuiServer)
144    , m_isRobotized(false)
145    , m_robotTimeoutSeconds(0)
146    , m_robotExtraTimeSeconds(0)
147{
148    // To allow QWebInspector's configuration persistence
149    setOrganizationName("Nokia");
150    setApplicationName("QtTestBrowser");
151    setApplicationVersion("0.1");
152
153    applyDefaultSettings();
154
155    handleUserOptions();
156}
157
158static void requiresGraphicsView(const QString& option)
159{
160    if (windowOptions.useGraphicsView)
161        return;
162    appQuit(1, QString("%1 only works in combination with the -graphicsbased option").arg(option));
163}
164
165void LauncherApplication::handleUserOptions()
166{
167    QStringList args = arguments();
168    QFileInfo program(args.at(0));
169    QString programName("QtTestBrowser");
170    if (program.exists())
171        programName = program.baseName();
172
173    QList<QString> updateModes(enumToKeys(QGraphicsView::staticMetaObject,
174            "ViewportUpdateMode", "ViewportUpdate"));
175
176    if (args.contains("-help")) {
177        qDebug() << "Usage:" << programName.toLatin1().data()
178             << "[-graphicsbased]"
179             << "[-no-compositing]"
180#if defined(QT_CONFIGURED_WITH_OPENGL)
181             << "[-gl-viewport]"
182             << "[-webgl]"
183#endif
184             << QString("[-viewport-update-mode %1]").arg(formatKeys(updateModes)).toLatin1().data()
185             << "[-cache-webview]"
186             << "[-maximize]"
187             << "[-show-fps]"
188             << "[-r list]"
189             << "[-robot-timeout seconds]"
190             << "[-robot-extra-time seconds]"
191             << "[-inspector-url location]"
192             << "[-tiled-backing-store]"
193             << "[-resizes-to-contents]"
194             << "[-local-storage-enabled]"
195             << "[-offline-storage-database-enabled]"
196             << "[-offline-web-application-cache-enabled]"
197             << "[-set-offline-storage-default-quota maxSize]"
198#if defined(Q_WS_X11)
199             << "[-use-test-fonts]"
200#endif
201             << "[-print-loaded-urls]"
202             << "URLs";
203        appQuit(0);
204    }
205
206    const bool defaultForAnimations = args.contains("-default-animations");
207    if (args.contains("-graphicsbased") || defaultForAnimations)
208        windowOptions.useGraphicsView = true;
209
210    if (args.contains("-no-compositing")) {
211        requiresGraphicsView("-no-compositing");
212        windowOptions.useCompositing = false;
213    }
214
215    if (args.contains("-show-fps")) {
216        requiresGraphicsView("-show-fps");
217        windowOptions.showFrameRate = true;
218    }
219
220    if (args.contains("-cache-webview") || defaultForAnimations) {
221        requiresGraphicsView("-cache-webview");
222        windowOptions.cacheWebView = true;
223    }
224
225    if (args.contains("-tiled-backing-store")) {
226        requiresGraphicsView("-tiled-backing-store");
227        windowOptions.useTiledBackingStore = true;
228    }
229
230    if (args.contains("-resizes-to-contents")) {
231        requiresGraphicsView("-resizes-to-contents");
232        windowOptions.resizesToContents = true;
233    }
234
235    if (args.contains("-local-storage-enabled"))
236        windowOptions.useLocalStorage = true;
237
238    if (args.contains("-maximize"))
239        windowOptions.startMaximized = true;
240
241    if (args.contains("-offline-storage-database-enabled"))
242        windowOptions.useOfflineStorageDatabase = true;
243
244    if (args.contains("-offline-web-application-cache-enabled"))
245        windowOptions.useOfflineWebApplicationCache = true;
246
247    int setOfflineStorageDefaultQuotaIndex = args.indexOf("-set-offline-storage-default-quota");
248    if (setOfflineStorageDefaultQuotaIndex != -1) {
249        unsigned int maxSize = takeOptionValue(&args, setOfflineStorageDefaultQuotaIndex).toUInt();
250        windowOptions.offlineStorageDefaultQuotaSize = maxSize;
251    }
252
253    if (defaultForAnimations)
254        windowOptions.viewportUpdateMode = QGraphicsView::BoundingRectViewportUpdate;
255
256    QString arg1("-viewport-update-mode");
257    int modeIndex = args.indexOf(arg1);
258    if (modeIndex != -1) {
259        requiresGraphicsView(arg1);
260
261        QString mode = takeOptionValue(&args, modeIndex);
262        if (mode.isEmpty())
263            appQuit(1, QString("%1 needs a value of one of [%2]").arg(arg1).arg(formatKeys(updateModes)));
264        int idx = updateModes.indexOf(mode);
265        if (idx == -1)
266            appQuit(1, QString("%1 value has to be one of [%2]").arg(arg1).arg(formatKeys(updateModes)));
267
268        windowOptions.viewportUpdateMode = static_cast<QGraphicsView::ViewportUpdateMode>(idx);
269    }
270#ifdef QT_CONFIGURED_WITH_OPENGL
271    if (args.contains("-gl-viewport") || defaultForAnimations) {
272        requiresGraphicsView("-gl-viewport");
273        windowOptions.useQGLWidgetViewport = true;
274    }
275
276    if (args.contains("-webgl")) {
277        requiresGraphicsView("-webgl");
278        windowOptions.useWebGL = true;
279    }
280#endif
281
282#if defined(Q_WS_X11)
283    if (args.contains("-use-test-fonts"))
284        windowOptions.useTestFonts = true;
285#endif
286
287    if (args.contains("-print-loaded-urls"))
288        windowOptions.printLoadedUrls = true;
289
290    QString inspectorUrlArg("-inspector-url");
291    int inspectorUrlIndex = args.indexOf(inspectorUrlArg);
292    if (inspectorUrlIndex != -1)
293       windowOptions.inspectorUrl = takeOptionValue(&args, inspectorUrlIndex);
294
295    QString remoteInspectorPortArg("-remote-inspector-port");
296    int remoteInspectorPortIndex = args.indexOf(remoteInspectorPortArg);
297    if (remoteInspectorPortIndex != -1)
298        windowOptions.remoteInspectorPort = takeOptionValue(&args, remoteInspectorPortIndex).toInt();
299
300    int robotIndex = args.indexOf("-r");
301    if (robotIndex != -1) {
302        QString listFile = takeOptionValue(&args, robotIndex);
303        if (listFile.isEmpty())
304            appQuit(1, "-r needs a list file to start in robotized mode");
305        if (!QFile::exists(listFile))
306            appQuit(1, "The list file supplied to -r does not exist.");
307
308        m_isRobotized = true;
309        m_urls = QStringList(listFile);
310    } else {
311        int lastArg = args.lastIndexOf(QRegExp("^-.*"));
312        m_urls = (lastArg != -1) ? args.mid(++lastArg) : args.mid(1);
313    }
314
315    int robotTimeoutIndex = args.indexOf("-robot-timeout");
316    if (robotTimeoutIndex != -1)
317        m_robotTimeoutSeconds = takeOptionValue(&args, robotTimeoutIndex).toInt();
318
319    int robotExtraTimeIndex = args.indexOf("-robot-extra-time");
320    if (robotExtraTimeIndex != -1)
321        m_robotExtraTimeSeconds = takeOptionValue(&args, robotExtraTimeIndex).toInt();
322}
323
324
325int main(int argc, char **argv)
326{
327    LauncherApplication app(argc, argv);
328
329    if (app.isRobotized()) {
330        LauncherWindow* window = new LauncherWindow();
331        UrlLoader loader(window->page()->mainFrame(), app.urls().at(0), app.robotTimeout(), app.robotExtraTime());
332        loader.loadNext();
333        window->show();
334        return launcherMain(app);
335    }
336
337    QStringList urls = app.urls();
338
339    if (urls.isEmpty()) {
340        QString defaultIndexFile = QString("%1/%2").arg(QDir::homePath()).arg(QLatin1String("index.html"));
341        if (QFile(defaultIndexFile).exists())
342            urls.append(QString("file://") + defaultIndexFile);
343        else
344            urls.append("");
345    }
346
347    LauncherWindow* window = 0;
348    foreach (QString url, urls) {
349        LauncherWindow* newWindow;
350        if (!window)
351            newWindow = window = new LauncherWindow(&windowOptions);
352        else
353            newWindow = window->newWindow();
354
355        newWindow->load(url);
356    }
357
358    window->show();
359    return launcherMain(app);
360}
361
362#include "main.moc"
363