1 /* ============================================================
2 * Falkon - Qt web browser
3 * Copyright (C) 2010-2018 David Rosca <nowrep@gmail.com>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 * ============================================================ */
18 #include "mainapplication.h"
19 #include "history.h"
20 #include "qztools.h"
21 #include "updater.h"
22 #include "autofill.h"
23 #include "settings.h"
24 #include "autosaver.h"
25 #include "datapaths.h"
26 #include "tabwidget.h"
27 #include "cookiejar.h"
28 #include "bookmarks.h"
29 #include "qzsettings.h"
30 #include "proxystyle.h"
31 #include "pluginproxy.h"
32 #include "iconprovider.h"
33 #include "browserwindow.h"
34 #include "checkboxdialog.h"
35 #include "networkmanager.h"
36 #include "profilemanager.h"
37 #include "restoremanager.h"
38 #include "browsinglibrary.h"
39 #include "downloadmanager.h"
40 #include "clearprivatedata.h"
41 #include "useragentmanager.h"
42 #include "commandlineoptions.h"
43 #include "searchenginesmanager.h"
44 #include "desktopnotificationsfactory.h"
45 #include "html5permissions/html5permissionsmanager.h"
46 #include "scripts.h"
47 #include "sessionmanager.h"
48 #include "closedwindowsmanager.h"
49 #include "protocolhandlermanager.h"
50 #include "../config.h"
51 
52 #include <QWebEngineSettings>
53 #include <QDesktopServices>
54 #include <QFontDatabase>
55 #include <QSqlDatabase>
56 #include <QLibraryInfo>
57 #include <QMessageBox>
58 #include <QTranslator>
59 #include <QThreadPool>
60 #include <QSettings>
61 #include <QProcess>
62 #include <QTimer>
63 #include <QDir>
64 #include <QStandardPaths>
65 #include <QWebEngineProfile>
66 #include <QWebEngineDownloadItem>
67 #include <QWebEngineScriptCollection>
68 #include <QRegularExpression>
69 #include <QtWebEngineWidgetsVersion>
70 #include <QtWebEngineCoreVersion>
71 
72 #if QTWEBENGINECORE_VERSION >= QT_VERSION_CHECK(5, 13, 0)
73 #include <QWebEngineNotification>
74 #endif
75 
76 #ifdef Q_OS_WIN
77 #include <QtWin>
78 #include <QWinJumpList>
79 #include <QWinJumpListCategory>
80 #endif
81 
82 #include <iostream>
83 
84 #if defined(Q_OS_WIN) && !defined(Q_OS_OS2)
85 #include "registerqappassociation.h"
86 #endif
87 
88 static bool s_testMode = false;
89 
MainApplication(int & argc,char ** argv)90 MainApplication::MainApplication(int &argc, char** argv)
91     : QtSingleApplication(argc, argv)
92     , m_isPrivate(false)
93     , m_isPortable(false)
94     , m_isClosing(false)
95     , m_isStartingAfterCrash(false)
96     , m_history(nullptr)
97     , m_bookmarks(nullptr)
98     , m_autoFill(nullptr)
99     , m_cookieJar(nullptr)
100     , m_plugins(nullptr)
101     , m_browsingLibrary(nullptr)
102     , m_networkManager(nullptr)
103     , m_restoreManager(nullptr)
104     , m_sessionManager(nullptr)
105     , m_downloadManager(nullptr)
106     , m_userAgentManager(nullptr)
107     , m_searchEnginesManager(nullptr)
108     , m_closedWindowsManager(nullptr)
109     , m_protocolHandlerManager(nullptr)
110     , m_html5PermissionsManager(nullptr)
111     , m_desktopNotifications(nullptr)
112     , m_webProfile(nullptr)
113     , m_autoSaver(nullptr)
114 #if defined(Q_OS_WIN) && !defined(Q_OS_OS2)
115     , m_registerQAppAssociation(0)
116 #endif
117 {
118     setAttribute(Qt::AA_UseHighDpiPixmaps);
119     setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
120 
121     setApplicationName(QStringLiteral("falkon"));
122     setOrganizationDomain(QStringLiteral("org.kde"));
123     setWindowIcon(QIcon::fromTheme(QSL("falkon"), QIcon(QSL(":icons/falkon.svg"))));
124     setDesktopFileName(QSL("org.kde.falkon"));
125 
126 #ifdef GIT_REVISION
127     setApplicationVersion(QSL("%1 (%2)").arg(Qz::VERSION, GIT_REVISION));
128 #else
129     setApplicationVersion(QString::fromLatin1(Qz::VERSION));
130 #endif
131 
132     // Set fallback icon theme (eg. on Windows/Mac)
133     if (QIcon::fromTheme(QSL("view-refresh")).isNull()) {
134         QIcon::setThemeName(QSL("breeze-fallback"));
135     }
136 
137     // QSQLITE database plugin is required
138     if (!QSqlDatabase::isDriverAvailable(QSL("QSQLITE"))) {
139         QMessageBox::critical(nullptr, QSL("Error"), QSL("Qt SQLite database plugin is not available. Please install it and restart the application."));
140         m_isClosing = true;
141         return;
142     }
143 
144 #ifdef Q_OS_WIN
145     // Set default app font (needed for N'ko)
146     int fontId = QFontDatabase::addApplicationFont(QSL("font.ttf"));
147     if (fontId != -1) {
148         const QStringList families = QFontDatabase::applicationFontFamilies(fontId);
149         if (!families.empty())
150             setFont(QFont(families.at(0)));
151     }
152 #endif
153 
154     QUrl startUrl;
155     QString startProfile;
156     QStringList messages;
157 
158     bool noAddons = false;
159     bool newInstance = false;
160 
161     if (argc > 1) {
162         CommandLineOptions cmd;
163         const auto actions = cmd.getActions();
164         for (const CommandLineOptions::ActionPair &pair : actions) {
165             switch (pair.action) {
166             case Qz::CL_StartWithoutAddons:
167                 noAddons = true;
168                 break;
169             case Qz::CL_StartWithProfile:
170                 startProfile = pair.text;
171                 break;
172             case Qz::CL_StartPortable:
173                 m_isPortable = true;
174                 break;
175             case Qz::CL_NewTab:
176                 messages.append(QStringLiteral("ACTION:NewTab"));
177                 m_postLaunchActions.append(OpenNewTab);
178                 break;
179             case Qz::CL_NewWindow:
180                 messages.append(QStringLiteral("ACTION:NewWindow"));
181                 break;
182             case Qz::CL_ToggleFullScreen:
183                 messages.append(QStringLiteral("ACTION:ToggleFullScreen"));
184                 m_postLaunchActions.append(ToggleFullScreen);
185                 break;
186             case Qz::CL_ShowDownloadManager:
187                 messages.append(QStringLiteral("ACTION:ShowDownloadManager"));
188                 m_postLaunchActions.append(OpenDownloadManager);
189                 break;
190             case Qz::CL_StartPrivateBrowsing:
191                 m_isPrivate = true;
192                 break;
193             case Qz::CL_StartNewInstance:
194                 newInstance = true;
195                 break;
196             case Qz::CL_OpenUrlInCurrentTab:
197                 startUrl = QUrl::fromUserInput(pair.text);
198                 messages.append("ACTION:OpenUrlInCurrentTab" + pair.text);
199                 break;
200             case Qz::CL_OpenUrlInNewWindow:
201                 startUrl = QUrl::fromUserInput(pair.text);
202                 messages.append("ACTION:OpenUrlInNewWindow" + pair.text);
203                 break;
204             case Qz::CL_OpenUrl:
205                 startUrl = QUrl::fromUserInput(pair.text);
206                 messages.append("URL:" + pair.text);
207                 break;
208             case Qz::CL_ExitAction:
209                 m_isClosing = true;
210                 return;
211             case Qz::CL_WMClass:
212                 m_wmClass = pair.text.toUtf8();
213                 break;
214             default:
215                 break;
216             }
217         }
218     }
219 
220     if (!isPortable()) {
221         QSettings falkonConf(QSL("%1/falkon.conf").arg(applicationDirPath()), QSettings::IniFormat);
222         m_isPortable = falkonConf.value(QSL("Config/Portable")).toBool();
223     }
224 
225     if (isPortable()) {
226         std::cout << "Falkon: Running in Portable Mode." << std::endl;
227         DataPaths::setPortableVersion();
228     }
229 
230     // Don't start single application in private browsing
231     if (!isPrivate()) {
232         QString appId = QStringLiteral("org.kde.Falkon");
233 
234         if (isPortable()) {
235             appId.append(QLatin1String(".Portable"));
236         }
237 
238         if (isTestModeEnabled()) {
239             appId.append(QSL(".TestMode"));
240         }
241 
242         if (newInstance) {
243             if (startProfile.isEmpty() || startProfile == QLatin1String("default")) {
244                 std::cout << "New instance cannot be started with default profile!" << std::endl;
245             }
246             else {
247                 // Generate unique appId so it is possible to start more separate instances
248                 // of the same profile. It is dangerous to run more instances of the same profile,
249                 // but if the user wants it, we should allow it.
250                 appId.append(QLatin1Char('.') + startProfile + QString::number(QDateTime::currentMSecsSinceEpoch()));
251             }
252         }
253 
254         setAppId(appId);
255     }
256 
257     // If there is nothing to tell other instance, we need to at least wake it
258     if (messages.isEmpty()) {
259         messages.append(QStringLiteral(" "));
260     }
261 
262     if (isRunning()) {
263         m_isClosing = true;
264         for (const QString &message : qAsConst(messages)) {
265             sendMessage(message);
266         }
267         return;
268     }
269 
270 #ifdef Q_OS_MACOS
271     setQuitOnLastWindowClosed(false);
272     // disable tabbing issue#2261
273     extern void disableWindowTabbing();
274     disableWindowTabbing();
275 #else
276     setQuitOnLastWindowClosed(true);
277 #endif
278 
279     QSettings::setDefaultFormat(QSettings::IniFormat);
280     QDesktopServices::setUrlHandler(QSL("http"), this, "addNewTab");
281     QDesktopServices::setUrlHandler(QSL("https"), this, "addNewTab");
282     QDesktopServices::setUrlHandler(QSL("ftp"), this, "addNewTab");
283 
284     ProfileManager profileManager;
285     profileManager.initConfigDir();
286     profileManager.initCurrentProfile(startProfile);
287 
288     Settings::createSettings(DataPaths::currentProfilePath() + QLatin1String("/settings.ini"));
289 
290     NetworkManager::registerSchemes();
291 
292     m_webProfile = isPrivate() ? new QWebEngineProfile() : QWebEngineProfile::defaultProfile();
293     connect(m_webProfile, &QWebEngineProfile::downloadRequested, this, &MainApplication::downloadRequested);
294 
295 #if QTWEBENGINECORE_VERSION >= QT_VERSION_CHECK(5, 13, 0)
296     m_webProfile->setNotificationPresenter([&] (std::unique_ptr<QWebEngineNotification> notification) {
297         auto notifications = desktopNotifications();
298         notifications->showNotification(
299             QPixmap::fromImage(notification->icon()), notification->title(), notification->message()
300         );
301     });
302 #endif
303 
304     m_networkManager = new NetworkManager(this);
305 
306     setupUserScripts();
307 
308     if (!isPrivate() && !isTestModeEnabled()) {
309         m_sessionManager = new SessionManager(this);
310         m_autoSaver = new AutoSaver(this);
311         connect(m_autoSaver, &AutoSaver::save, m_sessionManager, &SessionManager::autoSaveLastSession);
312 
313         Settings settings;
314         settings.beginGroup(QSL("SessionRestore"));
315         const bool wasRunning = settings.value(QSL("isRunning"), false).toBool();
316         const bool wasRestoring = settings.value(QSL("isRestoring"), false).toBool();
317         settings.setValue(QSL("isRunning"), true);
318         settings.setValue(QSL("isRestoring"), wasRunning);
319         settings.endGroup();
320         settings.sync();
321 
322         m_isStartingAfterCrash = wasRunning && wasRestoring;
323 
324         if (wasRunning) {
325             QTimer::singleShot(60 * 1000, this, [this]() {
326                 Settings().setValue(QSL("SessionRestore/isRestoring"), false);
327             });
328         }
329 
330         // we have to ask about startup session before creating main window
331         if (!m_isStartingAfterCrash && afterLaunch() == SelectSession)
332             m_restoreManager = new RestoreManager(sessionManager()->askSessionFromUser());
333     }
334 
335     loadSettings();
336 
337     m_plugins = new PluginProxy(this);
338     m_autoFill = new AutoFill(this);
339     mApp->protocolHandlerManager();
340 
341     if (!noAddons)
342         m_plugins->loadPlugins();
343 
344     BrowserWindow* window = createWindow(Qz::BW_FirstAppWindow, startUrl);
345     connect(window, SIGNAL(startingCompleted()), this, SLOT(restoreOverrideCursor()));
346 
347     connect(this, &QApplication::focusChanged, this, &MainApplication::onFocusChanged);
348 
349     if (!isPrivate() && !isTestModeEnabled()) {
350 #ifndef DISABLE_CHECK_UPDATES
351         Settings settings;
352         bool checkUpdates = settings.value("Web-Browser-Settings/CheckUpdates", true).toBool();
353 
354         if (checkUpdates) {
355             new Updater(window);
356         }
357 #endif
358 
359         sessionManager()->backupSavedSessions();
360 
361         if (m_isStartingAfterCrash || afterLaunch() == RestoreSession) {
362             m_restoreManager = new RestoreManager(sessionManager()->lastActiveSessionPath());
363             if (!m_restoreManager->isValid()) {
364                 destroyRestoreManager();
365             }
366         }
367 
368         if (!m_isStartingAfterCrash && m_restoreManager) {
369             restoreSession(window, m_restoreManager->restoreData());
370         }
371     }
372 
373     QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, DataPaths::currentProfilePath());
374 
375     connect(this, SIGNAL(messageReceived(QString)), this, SLOT(messageReceived(QString)));
376     connect(this, &QCoreApplication::aboutToQuit, this, &MainApplication::saveSettings);
377 
378     QTimer::singleShot(0, this, &MainApplication::postLaunch);
379 }
380 
~MainApplication()381 MainApplication::~MainApplication()
382 {
383     m_isClosing = true;
384 
385     IconProvider::instance()->saveIconsToDatabase();
386 
387     // Wait for all QtConcurrent jobs to finish
388     QThreadPool::globalInstance()->waitForDone();
389 
390     // Delete all classes that are saving data in destructor
391     delete m_bookmarks;
392     m_bookmarks = nullptr;
393     delete m_cookieJar;
394     m_cookieJar = nullptr;
395 
396     Settings::syncSettings();
397 }
398 
isClosing() const399 bool MainApplication::isClosing() const
400 {
401     return m_isClosing;
402 }
403 
isPrivate() const404 bool MainApplication::isPrivate() const
405 {
406     return m_isPrivate;
407 }
408 
isPortable() const409 bool MainApplication::isPortable() const
410 {
411 #ifdef PORTABLE_BUILD
412     return true;
413 #else
414     return m_isPortable;
415 #endif
416 }
417 
isStartingAfterCrash() const418 bool MainApplication::isStartingAfterCrash() const
419 {
420     return m_isStartingAfterCrash;
421 }
422 
windowCount() const423 int MainApplication::windowCount() const
424 {
425     return m_windows.count();
426 }
427 
windows() const428 QList<BrowserWindow*> MainApplication::windows() const
429 {
430     return m_windows;
431 }
432 
getWindow() const433 BrowserWindow* MainApplication::getWindow() const
434 {
435     if (m_lastActiveWindow) {
436         return m_lastActiveWindow.data();
437     }
438 
439     return m_windows.isEmpty() ? 0 : m_windows.at(0);
440 }
441 
createWindow(Qz::BrowserWindowType type,const QUrl & startUrl)442 BrowserWindow* MainApplication::createWindow(Qz::BrowserWindowType type, const QUrl &startUrl)
443 {
444     if (windowCount() == 0 && type != Qz::BW_MacFirstWindow) {
445         type = Qz::BW_FirstAppWindow;
446     }
447 
448     BrowserWindow* window = new BrowserWindow(type, startUrl);
449     connect(window, &QObject::destroyed, this, &MainApplication::windowDestroyed);
450 
451     m_windows.prepend(window);
452     return window;
453 }
454 
afterLaunch() const455 MainApplication::AfterLaunch MainApplication::afterLaunch() const
456 {
457     return static_cast<AfterLaunch>(Settings().value(QSL("Web-URL-Settings/afterLaunch"), RestoreSession).toInt());
458 }
459 
openSession(BrowserWindow * window,RestoreData & restoreData)460 void MainApplication::openSession(BrowserWindow* window, RestoreData &restoreData)
461 {
462     setOverrideCursor(Qt::BusyCursor);
463 
464     if (!window)
465         window = createWindow(Qz::BW_OtherRestoredWindow);
466 
467     if (window->tabCount() != 0) {
468         // This can only happen when recovering crashed session!
469         // Don't restore tabs in current window as user already opened some new tabs.
470         createWindow(Qz::BW_OtherRestoredWindow)->restoreWindow(restoreData.windows.takeAt(0));
471     } else {
472         window->restoreWindow(restoreData.windows.takeAt(0));
473     }
474 
475     const auto restoreWindows = restoreData.windows;
476     for (const BrowserWindow::SavedWindow &data : restoreWindows) {
477         BrowserWindow* window = createWindow(Qz::BW_OtherRestoredWindow);
478         window->restoreWindow(data);
479     }
480 
481     m_closedWindowsManager->restoreState(restoreData.closedWindows);
482 
483     restoreOverrideCursor();
484 }
485 
restoreSession(BrowserWindow * window,RestoreData restoreData)486 bool MainApplication::restoreSession(BrowserWindow* window, RestoreData restoreData)
487 {
488     if (m_isPrivate || !restoreData.isValid()) {
489         return false;
490     }
491 
492     openSession(window, restoreData);
493 
494     m_restoreManager->clearRestoreData();
495     destroyRestoreManager();
496 
497     return true;
498 }
499 
destroyRestoreManager()500 void MainApplication::destroyRestoreManager()
501 {
502     if (m_restoreManager && m_restoreManager->isValid()) {
503         return;
504     }
505 
506     delete m_restoreManager;
507     m_restoreManager = 0;
508 }
509 
reloadSettings()510 void MainApplication::reloadSettings()
511 {
512     loadSettings();
513     emit settingsReloaded();
514 }
515 
styleName() const516 QString MainApplication::styleName() const
517 {
518     return m_proxyStyle ? m_proxyStyle->name() : QString();
519 }
520 
setProxyStyle(ProxyStyle * style)521 void MainApplication::setProxyStyle(ProxyStyle *style)
522 {
523     m_proxyStyle = style;
524     setStyle(style);
525 }
526 
wmClass() const527 QByteArray MainApplication::wmClass() const
528 {
529     return m_wmClass;
530 }
531 
history()532 History* MainApplication::history()
533 {
534     if (!m_history) {
535         m_history = new History(this);
536     }
537     return m_history;
538 }
539 
bookmarks()540 Bookmarks* MainApplication::bookmarks()
541 {
542     if (!m_bookmarks) {
543         m_bookmarks = new Bookmarks(this);
544     }
545     return m_bookmarks;
546 }
547 
autoFill()548 AutoFill* MainApplication::autoFill()
549 {
550     return m_autoFill;
551 }
552 
cookieJar()553 CookieJar* MainApplication::cookieJar()
554 {
555     if (!m_cookieJar) {
556         m_cookieJar = new CookieJar(this);
557     }
558     return m_cookieJar;
559 }
560 
plugins()561 PluginProxy* MainApplication::plugins()
562 {
563     return m_plugins;
564 }
565 
browsingLibrary()566 BrowsingLibrary* MainApplication::browsingLibrary()
567 {
568     if (!m_browsingLibrary) {
569         m_browsingLibrary = new BrowsingLibrary(getWindow());
570     }
571     return m_browsingLibrary;
572 }
573 
networkManager()574 NetworkManager *MainApplication::networkManager()
575 {
576     return m_networkManager;
577 }
578 
restoreManager()579 RestoreManager* MainApplication::restoreManager()
580 {
581     return m_restoreManager;
582 }
583 
sessionManager()584 SessionManager* MainApplication::sessionManager()
585 {
586     return m_sessionManager;
587 }
588 
downloadManager()589 DownloadManager* MainApplication::downloadManager()
590 {
591     if (!m_downloadManager) {
592         m_downloadManager = new DownloadManager();
593     }
594     return m_downloadManager;
595 }
596 
userAgentManager()597 UserAgentManager* MainApplication::userAgentManager()
598 {
599     if (!m_userAgentManager) {
600         m_userAgentManager = new UserAgentManager(this);
601     }
602     return m_userAgentManager;
603 }
604 
searchEnginesManager()605 SearchEnginesManager* MainApplication::searchEnginesManager()
606 {
607     if (!m_searchEnginesManager) {
608         m_searchEnginesManager = new SearchEnginesManager(this);
609     }
610     return m_searchEnginesManager;
611 }
612 
closedWindowsManager()613 ClosedWindowsManager* MainApplication::closedWindowsManager()
614 {
615     if (!m_closedWindowsManager) {
616         m_closedWindowsManager = new ClosedWindowsManager(this);
617     }
618     return m_closedWindowsManager;
619 }
620 
protocolHandlerManager()621 ProtocolHandlerManager *MainApplication::protocolHandlerManager()
622 {
623     if (!m_protocolHandlerManager) {
624         m_protocolHandlerManager = new ProtocolHandlerManager(this);
625     }
626     return m_protocolHandlerManager;
627 }
628 
html5PermissionsManager()629 HTML5PermissionsManager* MainApplication::html5PermissionsManager()
630 {
631     if (!m_html5PermissionsManager) {
632         m_html5PermissionsManager = new HTML5PermissionsManager(this);
633     }
634     return m_html5PermissionsManager;
635 }
636 
desktopNotifications()637 DesktopNotificationsFactory* MainApplication::desktopNotifications()
638 {
639     if (!m_desktopNotifications) {
640         m_desktopNotifications = new DesktopNotificationsFactory(this);
641     }
642     return m_desktopNotifications;
643 }
644 
webProfile() const645 QWebEngineProfile *MainApplication::webProfile() const
646 {
647     return m_webProfile;
648 }
649 
webSettings() const650 QWebEngineSettings *MainApplication::webSettings() const
651 {
652     return m_webProfile->settings();
653 }
654 
655 // static
instance()656 MainApplication* MainApplication::instance()
657 {
658     return static_cast<MainApplication*>(QCoreApplication::instance());
659 }
660 
661 // static
isTestModeEnabled()662 bool MainApplication::isTestModeEnabled()
663 {
664     return s_testMode;
665 }
666 
667 // static
setTestModeEnabled(bool enabled)668 void MainApplication::setTestModeEnabled(bool enabled)
669 {
670     s_testMode = enabled;
671 }
672 
addNewTab(const QUrl & url)673 void MainApplication::addNewTab(const QUrl &url)
674 {
675     BrowserWindow* window = getWindow();
676 
677     if (window) {
678         window->tabWidget()->addView(url, url.isEmpty() ? Qz::NT_SelectedNewEmptyTab : Qz::NT_SelectedTabAtTheEnd);
679     }
680 }
681 
startPrivateBrowsing(const QUrl & startUrl)682 void MainApplication::startPrivateBrowsing(const QUrl &startUrl)
683 {
684     QUrl url = startUrl;
685     if (QAction* act = qobject_cast<QAction*>(sender())) {
686         url = act->data().toUrl();
687     }
688 
689     QStringList args;
690     args.append(QSL("--private-browsing"));
691     args.append(QSL("--profile=") + ProfileManager::currentProfile());
692 
693     if (!url.isEmpty()) {
694         args << url.toEncoded();
695     }
696 
697     if (!QProcess::startDetached(applicationFilePath(), args)) {
698         qWarning() << "MainApplication: Cannot start new browser process for private browsing!" << applicationFilePath() << args;
699     }
700 }
701 
reloadUserStyleSheet()702 void MainApplication::reloadUserStyleSheet()
703 {
704     const QString userCssFile = Settings().value(QSL("Web-Browser-Settings/userStyleSheet"), QString()).toString();
705     setUserStyleSheet(userCssFile);
706 }
707 
restoreOverrideCursor()708 void MainApplication::restoreOverrideCursor()
709 {
710     QApplication::restoreOverrideCursor();
711 }
712 
changeOccurred()713 void MainApplication::changeOccurred()
714 {
715     if (m_autoSaver)
716         m_autoSaver->changeOccurred();
717 }
718 
quitApplication()719 void MainApplication::quitApplication()
720 {
721     if (m_downloadManager && !m_downloadManager->canClose()) {
722         m_downloadManager->show();
723         return;
724     }
725 
726     for (BrowserWindow *window : qAsConst(m_windows)) {
727         emit window->aboutToClose();
728     }
729 
730     if (m_sessionManager && m_windows.count() > 0) {
731         m_sessionManager->autoSaveLastSession();
732     }
733 
734     m_isClosing = true;
735 
736     for (BrowserWindow *window : qAsConst(m_windows)) {
737         window->close();
738     }
739 
740     // Saving settings in saveSettings() slot called from quit() so
741     // everything gets saved also when quitting application in other
742     // way than clicking Quit action in File menu or closing last window
743     // eg. on Mac (#157)
744 
745     if (!isPrivate()) {
746         removeLockFile();
747     }
748 
749     quit();
750 }
751 
postLaunch()752 void MainApplication::postLaunch()
753 {
754     if (m_postLaunchActions.contains(OpenDownloadManager)) {
755         downloadManager()->show();
756     }
757 
758     if (m_postLaunchActions.contains(OpenNewTab)) {
759         getWindow()->tabWidget()->addView(QUrl(), Qz::NT_SelectedNewEmptyTab);
760     }
761 
762     if (m_postLaunchActions.contains(ToggleFullScreen)) {
763         getWindow()->toggleFullScreen();
764     }
765 
766     createJumpList();
767     initPulseSupport();
768 
769     QTimer::singleShot(5000, this, &MainApplication::runDeferredPostLaunchActions);
770 }
771 
saveState() const772 QByteArray MainApplication::saveState() const
773 {
774     RestoreData restoreData;
775     restoreData.windows.reserve(m_windows.count());
776     for (BrowserWindow *window : qAsConst(m_windows)) {
777         restoreData.windows.append(BrowserWindow::SavedWindow(window));
778     }
779 
780     if (m_restoreManager && m_restoreManager->isValid()) {
781         QDataStream stream(&restoreData.crashedSession, QIODevice::WriteOnly);
782         stream << m_restoreManager->restoreData();
783     }
784 
785     restoreData.closedWindows = m_closedWindowsManager->saveState();
786 
787     QByteArray data;
788     QDataStream stream(&data, QIODevice::WriteOnly);
789 
790     stream << Qz::sessionVersion;
791     stream << restoreData;
792 
793     return data;
794 }
795 
saveSettings()796 void MainApplication::saveSettings()
797 {
798     if (isPrivate()) {
799         return;
800     }
801 
802     m_isClosing = true;
803 
804     Settings settings;
805     settings.beginGroup(QSL("SessionRestore"));
806     settings.setValue(QSL("isRunning"), false);
807     settings.setValue(QSL("isRestoring"), false);
808     settings.endGroup();
809 
810     settings.beginGroup(QSL("Web-Browser-Settings"));
811     bool deleteCache = settings.value(QSL("deleteCacheOnClose"), false).toBool();
812     bool deleteHistory = settings.value(QSL("deleteHistoryOnClose"), false).toBool();
813     bool deleteHtml5Storage = settings.value(QSL("deleteHTML5StorageOnClose"), false).toBool();
814     settings.endGroup();
815 
816     settings.beginGroup(QSL("Cookie-Settings"));
817     bool deleteCookies = settings.value(QSL("deleteCookiesOnClose"), false).toBool();
818     settings.endGroup();
819 
820     if (deleteHistory) {
821         m_history->clearHistory();
822     }
823     if (deleteHtml5Storage) {
824         ClearPrivateData::clearLocalStorage();
825     }
826     if (deleteCookies) {
827         m_cookieJar->deleteAllCookies(false);
828     }
829     if (deleteCache) {
830         QzTools::removeRecursively(mApp->webProfile()->cachePath());
831     }
832 
833     m_searchEnginesManager->saveSettings();
834     m_plugins->shutdown();
835     m_networkManager->shutdown();
836 
837     qzSettings->saveSettings();
838     QFile::remove(DataPaths::currentProfilePath() + QLatin1String("/WebpageIcons.db"));
839 
840     sessionManager()->saveSettings();
841 }
842 
messageReceived(const QString & message)843 void MainApplication::messageReceived(const QString &message)
844 {
845     QWidget* actWin = getWindow();
846     QUrl actUrl;
847 
848     if (message.startsWith(QLatin1String("URL:"))) {
849         const QUrl url = QUrl::fromUserInput(message.mid(4));
850         addNewTab(url);
851         actWin = getWindow();
852     }
853     else if (message.startsWith(QLatin1String("ACTION:"))) {
854         const QString text = message.mid(7);
855         if (text == QLatin1String("NewTab")) {
856             addNewTab();
857         }
858         else if (text == QLatin1String("NewWindow")) {
859             actWin = createWindow(Qz::BW_NewWindow);
860         }
861         else if (text == QLatin1String("ShowDownloadManager")) {
862             downloadManager()->show();
863             actWin = downloadManager();
864         }
865         else if (text == QLatin1String("ToggleFullScreen") && actWin) {
866             BrowserWindow* qz = static_cast<BrowserWindow*>(actWin);
867             qz->toggleFullScreen();
868         }
869         else if (text.startsWith(QLatin1String("OpenUrlInCurrentTab"))) {
870             actUrl = QUrl::fromUserInput(text.mid(19));
871         }
872         else if (text.startsWith(QLatin1String("OpenUrlInNewWindow"))) {
873             createWindow(Qz::BW_NewWindow, QUrl::fromUserInput(text.mid(18)));
874             return;
875         }
876     }
877     else {
878         // User attempted to start another instance, let's open a new window
879         actWin = createWindow(Qz::BW_NewWindow);
880     }
881 
882     if (!actWin) {
883         if (!isClosing()) {
884             // It can only occur if download manager window was still opened
885             createWindow(Qz::BW_NewWindow, actUrl);
886         }
887         return;
888     }
889 
890     actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized);
891     actWin->raise();
892     actWin->activateWindow();
893     actWin->setFocus();
894 
895     BrowserWindow* win = qobject_cast<BrowserWindow*>(actWin);
896 
897     if (win && !actUrl.isEmpty()) {
898         win->loadAddress(actUrl);
899     }
900 }
901 
windowDestroyed(QObject * window)902 void MainApplication::windowDestroyed(QObject* window)
903 {
904     // qobject_cast doesn't work because QObject::destroyed is emitted from destructor
905     Q_ASSERT(static_cast<BrowserWindow*>(window));
906     Q_ASSERT(m_windows.contains(static_cast<BrowserWindow*>(window)));
907 
908     m_windows.removeOne(static_cast<BrowserWindow*>(window));
909 }
910 
onFocusChanged()911 void MainApplication::onFocusChanged()
912 {
913     BrowserWindow* activeBrowserWindow = qobject_cast<BrowserWindow*>(activeWindow());
914 
915     if (activeBrowserWindow) {
916         m_lastActiveWindow = activeBrowserWindow;
917 
918         emit activeWindowChanged(m_lastActiveWindow);
919     }
920 }
921 
runDeferredPostLaunchActions()922 void MainApplication::runDeferredPostLaunchActions()
923 {
924     checkDefaultWebBrowser();
925     checkOptimizeDatabase();
926 }
927 
downloadRequested(QWebEngineDownloadItem * download)928 void MainApplication::downloadRequested(QWebEngineDownloadItem *download)
929 {
930     downloadManager()->download(download);
931 }
932 
loadSettings()933 void MainApplication::loadSettings()
934 {
935     Settings settings;
936     settings.beginGroup(QSL("Themes"));
937     QString activeTheme = settings.value(QSL("activeTheme"), DEFAULT_THEME_NAME).toString();
938     settings.endGroup();
939 
940     loadTheme(activeTheme);
941 
942     QWebEngineSettings* webSettings = m_webProfile->settings();
943 
944     // Web browsing settings
945     settings.beginGroup(QSL("Web-Browser-Settings"));
946 
947     webSettings->setAttribute(QWebEngineSettings::LocalStorageEnabled, settings.value(QSL("HTML5StorageEnabled"), true).toBool());
948     webSettings->setAttribute(QWebEngineSettings::PluginsEnabled, settings.value(QSL("allowPlugins"), true).toBool());
949     webSettings->setAttribute(QWebEngineSettings::JavascriptEnabled, settings.value(QSL("allowJavaScript"), true).toBool());
950     webSettings->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, settings.value(QSL("allowJavaScriptOpenWindow"), false).toBool());
951     webSettings->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, settings.value(QSL("allowJavaScriptAccessClipboard"), true).toBool());
952     webSettings->setAttribute(QWebEngineSettings::LinksIncludedInFocusChain, settings.value(QSL("IncludeLinkInFocusChain"), false).toBool());
953     webSettings->setAttribute(QWebEngineSettings::XSSAuditingEnabled, settings.value(QSL("XSSAuditing"), false).toBool());
954     webSettings->setAttribute(QWebEngineSettings::PrintElementBackgrounds, settings.value(QSL("PrintElementBackground"), true).toBool());
955     webSettings->setAttribute(QWebEngineSettings::SpatialNavigationEnabled, settings.value(QSL("SpatialNavigation"), false).toBool());
956     webSettings->setAttribute(QWebEngineSettings::ScrollAnimatorEnabled, settings.value(QSL("AnimateScrolling"), true).toBool());
957     webSettings->setAttribute(QWebEngineSettings::HyperlinkAuditingEnabled, false);
958     webSettings->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);
959     webSettings->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, true);
960     webSettings->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, false);
961 
962 #if QTWEBENGINEWIDGETS_VERSION >= QT_VERSION_CHECK(5, 10, 0)
963     webSettings->setAttribute(QWebEngineSettings::AllowWindowActivationFromJavaScript, settings.value(QSL("allowJavaScriptActivateWindow"), false).toBool());
964 #endif
965 
966 #if QTWEBENGINEWIDGETS_VERSION >= QT_VERSION_CHECK(5, 11, 0)
967     webSettings->setAttribute(QWebEngineSettings::JavascriptCanPaste, settings.value(QSL("allowJavaScriptPaste"), true).toBool());
968     webSettings->setAttribute(QWebEngineSettings::PlaybackRequiresUserGesture, settings.value(QSL("DisableVideoAutoPlay"), false).toBool());
969     webSettings->setAttribute(QWebEngineSettings::WebRTCPublicInterfacesOnly, settings.value(QSL("WebRTCPublicIpOnly"), true).toBool());
970     webSettings->setUnknownUrlSchemePolicy(QWebEngineSettings::AllowAllUnknownUrlSchemes);
971 #endif
972 
973 #if QTWEBENGINEWIDGETS_VERSION >= QT_VERSION_CHECK(5, 12, 0)
974     webSettings->setAttribute(QWebEngineSettings::DnsPrefetchEnabled, settings.value(QSL("DNSPrefetch"), true).toBool());
975 #endif
976 
977 #if QTWEBENGINEWIDGETS_VERSION >= QT_VERSION_CHECK(5, 13, 0)
978     webSettings->setAttribute(QWebEngineSettings::PdfViewerEnabled, settings.value(QSL("intPDFViewer"), false).toBool());
979     webSettings->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, settings.value(QSL("screenCaptureEnabled"), false).toBool());
980 #endif
981 
982     webSettings->setDefaultTextEncoding(settings.value(QSL("DefaultEncoding"), webSettings->defaultTextEncoding()).toString());
983 
984     setWheelScrollLines(settings.value(QSL("wheelScrollLines"), wheelScrollLines()).toInt());
985 
986     const QString userCss = settings.value(QSL("userStyleSheet"), QString()).toString();
987     settings.endGroup();
988 
989     setUserStyleSheet(userCss);
990 
991     settings.beginGroup(QSL("Browser-Fonts"));
992     webSettings->setFontFamily(QWebEngineSettings::StandardFont, settings.value(QSL("StandardFont"), webSettings->fontFamily(QWebEngineSettings::StandardFont)).toString());
993     webSettings->setFontFamily(QWebEngineSettings::CursiveFont, settings.value(QSL("CursiveFont"), webSettings->fontFamily(QWebEngineSettings::CursiveFont)).toString());
994     webSettings->setFontFamily(QWebEngineSettings::FantasyFont, settings.value(QSL("FantasyFont"), webSettings->fontFamily(QWebEngineSettings::FantasyFont)).toString());
995     webSettings->setFontFamily(QWebEngineSettings::FixedFont, settings.value(QSL("FixedFont"), webSettings->fontFamily(QWebEngineSettings::FixedFont)).toString());
996     webSettings->setFontFamily(QWebEngineSettings::SansSerifFont, settings.value(QSL("SansSerifFont"), webSettings->fontFamily(QWebEngineSettings::SansSerifFont)).toString());
997     webSettings->setFontFamily(QWebEngineSettings::SerifFont, settings.value(QSL("SerifFont"), webSettings->fontFamily(QWebEngineSettings::SerifFont)).toString());
998     webSettings->setFontSize(QWebEngineSettings::DefaultFontSize, settings.value(QSL("DefaultFontSize"), 15).toInt());
999     webSettings->setFontSize(QWebEngineSettings::DefaultFixedFontSize, settings.value(QSL("FixedFontSize"), 14).toInt());
1000     webSettings->setFontSize(QWebEngineSettings::MinimumFontSize, settings.value(QSL("MinimumFontSize"), 3).toInt());
1001     webSettings->setFontSize(QWebEngineSettings::MinimumLogicalFontSize, settings.value(QSL("MinimumLogicalFontSize"), 5).toInt());
1002     settings.endGroup();
1003 
1004     QWebEngineProfile* profile = QWebEngineProfile::defaultProfile();
1005     profile->setPersistentCookiesPolicy(QWebEngineProfile::AllowPersistentCookies);
1006     profile->setPersistentStoragePath(DataPaths::currentProfilePath());
1007 
1008     QString defaultPath = DataPaths::path(DataPaths::Cache);
1009     if (!defaultPath.startsWith(DataPaths::currentProfilePath()))
1010         defaultPath.append(QLatin1Char('/') + ProfileManager::currentProfile());
1011     const QString &cachePath = settings.value(QSL("Web-Browser-Settings/CachePath"), defaultPath).toString();
1012     profile->setCachePath(cachePath);
1013 
1014     const bool allowCache = settings.value(QSL("Web-Browser-Settings/AllowLocalCache"), true).toBool();
1015     profile->setHttpCacheType(allowCache ? QWebEngineProfile::DiskHttpCache : QWebEngineProfile::MemoryHttpCache);
1016 
1017     const int cacheSize = settings.value(QSL("Web-Browser-Settings/LocalCacheSize"), 50).toInt() * 1000 * 1000;
1018     profile->setHttpCacheMaximumSize(cacheSize);
1019 
1020     settings.beginGroup(QSL("SpellCheck"));
1021     profile->setSpellCheckEnabled(settings.value(QSL("Enabled"), false).toBool());
1022     profile->setSpellCheckLanguages(settings.value(QSL("Languages")).toStringList());
1023     settings.endGroup();
1024 
1025     if (isPrivate()) {
1026         webSettings->setAttribute(QWebEngineSettings::LocalStorageEnabled, false);
1027         history()->setSaving(false);
1028     }
1029 
1030     if (m_downloadManager) {
1031         m_downloadManager->loadSettings();
1032     }
1033 
1034     qzSettings->loadSettings();
1035     userAgentManager()->loadSettings();
1036     networkManager()->loadSettings();
1037 }
1038 
loadTheme(const QString & name)1039 void MainApplication::loadTheme(const QString &name)
1040 {
1041     QString activeThemePath = DataPaths::locate(DataPaths::Themes, name);
1042 
1043     if (activeThemePath.isEmpty()) {
1044         qWarning() << "Cannot load theme " << name;
1045         activeThemePath = QSL("%1/%2").arg(DataPaths::path(DataPaths::Themes), DEFAULT_THEME_NAME);
1046     }
1047 
1048     QString qss = QzTools::readAllFileContents(activeThemePath + QLatin1String("/main.css"));
1049 
1050 #if defined(Q_OS_MACOS)
1051     qss.append(QzTools::readAllFileContents(activeThemePath + QLatin1String("/mac.css")));
1052 #elif defined(Q_OS_UNIX)
1053     qss.append(QzTools::readAllFileContents(activeThemePath + QLatin1String("/linux.css")));
1054 #elif defined(Q_OS_WIN) || defined(Q_OS_OS2)
1055     qss.append(QzTools::readAllFileContents(activeThemePath + QLatin1String("/windows.css")));
1056 #endif
1057 
1058     if (isRightToLeft()) {
1059         qss.append(QzTools::readAllFileContents(activeThemePath + QLatin1String("/rtl.css")));
1060     }
1061 
1062     if (isPrivate()) {
1063         qss.append(QzTools::readAllFileContents(activeThemePath + QLatin1String("/private.css")));
1064     }
1065 
1066     qss.append(QzTools::readAllFileContents(DataPaths::currentProfilePath() + QL1S("/userChrome.css")));
1067 
1068     QString relativePath = QDir::current().relativeFilePath(activeThemePath);
1069     qss.replace(QRegularExpression(QSL("url\\s*\\(\\s*([^\\*:\\);]+)\\s*\\)")), QSL("url(%1/\\1)").arg(relativePath));
1070     setStyleSheet(qss);
1071 }
1072 
checkDefaultWebBrowser()1073 void MainApplication::checkDefaultWebBrowser()
1074 {
1075     if (isPortable()) {
1076         return;
1077     }
1078 
1079 #if defined(Q_OS_WIN) && !defined(Q_OS_OS2)
1080     Settings settings;
1081     bool checkNow = settings.value(QSL("Web-Browser-Settings/CheckDefaultBrowser"), DEFAULT_CHECK_DEFAULTBROWSER).toBool();
1082 
1083     if (!checkNow) {
1084         return;
1085     }
1086 
1087     bool checkAgain = true;
1088 
1089     if (!associationManager()->isDefaultForAllCapabilities()) {
1090         CheckBoxDialog dialog(QMessageBox::Yes | QMessageBox::No, getWindow());
1091         dialog.setDefaultButton(QMessageBox::Yes);
1092         dialog.setText(tr("Falkon is not currently your default browser. Would you like to make it your default browser?"));
1093         dialog.setCheckBoxText(tr("Always perform this check when starting Falkon."));
1094         dialog.setDefaultCheckState(Qt::Checked);
1095         dialog.setWindowTitle(tr("Default Browser"));
1096         dialog.setIcon(QMessageBox::Warning);
1097 
1098         if (dialog.exec() == QMessageBox::Yes) {
1099             if (!mApp->associationManager()->showNativeDefaultAppSettingsUi())
1100                 mApp->associationManager()->registerAllAssociation();
1101         }
1102 
1103         checkAgain = dialog.isChecked();
1104     }
1105 
1106     settings.setValue(QSL("Web-Browser-Settings/CheckDefaultBrowser"), checkAgain);
1107 #endif
1108 }
1109 
checkOptimizeDatabase()1110 void MainApplication::checkOptimizeDatabase()
1111 {
1112     Settings settings;
1113     settings.beginGroup(QSL("Browser"));
1114     const int numberOfRuns = settings.value(QSL("RunsWithoutOptimizeDb"), 0).toInt();
1115     settings.setValue(QSL("RunsWithoutOptimizeDb"), numberOfRuns + 1);
1116 
1117     if (numberOfRuns > 20) {
1118         std::cout << "Optimizing database..." << std::endl;
1119         IconProvider::instance()->clearOldIconsInDatabase();
1120         settings.setValue(QSL("RunsWithoutOptimizeDb"), 0);
1121     }
1122 
1123     settings.endGroup();
1124 }
1125 
setupUserScripts()1126 void MainApplication::setupUserScripts()
1127 {
1128     // WebChannel for SafeJsWorld
1129     QWebEngineScript script;
1130     script.setName(QSL("_falkon_webchannel"));
1131     script.setInjectionPoint(QWebEngineScript::DocumentCreation);
1132     script.setWorldId(WebPage::SafeJsWorld);
1133     script.setRunsOnSubFrames(true);
1134     script.setSourceCode(Scripts::setupWebChannel());
1135     m_webProfile->scripts()->insert(script);
1136 
1137     // falkon:restore
1138     QWebEngineScript falkonRestore;
1139     falkonRestore.setWorldId(WebPage::SafeJsWorld);
1140     falkonRestore.setSourceCode(QzTools::readAllFileContents(QSL(":html/restore.user.js")));
1141     m_webProfile->scripts()->insert(falkonRestore);
1142 
1143     // falkon:speeddial
1144     QWebEngineScript falkonSpeedDial;
1145     falkonSpeedDial.setWorldId(WebPage::SafeJsWorld);
1146     falkonSpeedDial.setSourceCode(Scripts::setupSpeedDial());
1147     m_webProfile->scripts()->insert(falkonSpeedDial);
1148 
1149     // document.window object addons
1150     QWebEngineScript documentWindowAddons;
1151     documentWindowAddons.setName(QSL("_falkon_window_object"));
1152     documentWindowAddons.setInjectionPoint(QWebEngineScript::DocumentCreation);
1153     documentWindowAddons.setWorldId(WebPage::UnsafeJsWorld);
1154     documentWindowAddons.setRunsOnSubFrames(true);
1155     documentWindowAddons.setSourceCode(Scripts::setupWindowObject());
1156     m_webProfile->scripts()->insert(documentWindowAddons);
1157 }
1158 
setUserStyleSheet(const QString & filePath)1159 void MainApplication::setUserStyleSheet(const QString &filePath)
1160 {
1161     QString userCss;
1162 
1163 #if !defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
1164     // Don't grey out selection on losing focus (to prevent graying out found text)
1165     QString highlightColor;
1166     QString highlightedTextColor;
1167 #ifdef Q_OS_MACOS
1168     highlightColor = QLatin1String("#b6d6fc");
1169     highlightedTextColor = QLatin1String("#000");
1170 #else
1171     QPalette pal = style()->standardPalette();
1172     highlightColor = pal.color(QPalette::Highlight).name();
1173     highlightedTextColor = pal.color(QPalette::HighlightedText).name();
1174 #endif
1175     userCss += QString("::selection {background: %1; color: %2;} ").arg(highlightColor, highlightedTextColor);
1176 #endif
1177 
1178     userCss += QzTools::readAllFileContents(filePath).remove(QLatin1Char('\n'));
1179 
1180     const QString name = QStringLiteral("_falkon_userstylesheet");
1181 
1182     QWebEngineScript oldScript = m_webProfile->scripts()->findScript(name);
1183     if (!oldScript.isNull()) {
1184         m_webProfile->scripts()->remove(oldScript);
1185     }
1186 
1187     if (userCss.isEmpty())
1188         return;
1189 
1190     QWebEngineScript script;
1191     script.setName(name);
1192     script.setInjectionPoint(QWebEngineScript::DocumentReady);
1193     script.setWorldId(WebPage::SafeJsWorld);
1194     script.setRunsOnSubFrames(true);
1195     script.setSourceCode(Scripts::setCss(userCss));
1196     m_webProfile->scripts()->insert(script);
1197 }
1198 
createJumpList()1199 void MainApplication::createJumpList()
1200 {
1201 #ifdef Q_OS_WIN
1202     QWinJumpList *jumpList = new QWinJumpList(this);
1203     jumpList->clear();
1204 
1205     // Frequent
1206     QWinJumpListCategory *frequent = jumpList->frequent();
1207     frequent->setVisible(true);
1208     const QVector<HistoryEntry> mostList = m_history->mostVisited(7);
1209     for (const HistoryEntry &entry : mostList) {
1210         frequent->addLink(IconProvider::iconForUrl(entry.url), entry.title, applicationFilePath(), QStringList{entry.url.toEncoded()});
1211     }
1212 
1213     // Tasks
1214     QWinJumpListCategory *tasks = jumpList->tasks();
1215     tasks->setVisible(true);
1216     tasks->addLink(IconProvider::newTabIcon(), tr("Open new tab"), applicationFilePath(), {QSL("--new-tab")});
1217     tasks->addLink(IconProvider::newWindowIcon(), tr("Open new window"), applicationFilePath(), {QSL("--new-window")});
1218     tasks->addLink(IconProvider::privateBrowsingIcon(), tr("Open new private window"), applicationFilePath(), {QSL("--private-browsing")});
1219 #endif
1220 }
1221 
initPulseSupport()1222 void MainApplication::initPulseSupport()
1223 {
1224     qputenv("PULSE_PROP_OVERRIDE_application.name", "Falkon");
1225     qputenv("PULSE_PROP_OVERRIDE_application.icon_name", "falkon");
1226     qputenv("PULSE_PROP_OVERRIDE_media.icon_name", "falkon");
1227 }
1228 
1229 #if defined(Q_OS_WIN) && !defined(Q_OS_OS2)
associationManager()1230 RegisterQAppAssociation* MainApplication::associationManager()
1231 {
1232     if (!m_registerQAppAssociation) {
1233         QString desc = tr("Falkon is a new and very fast Qt web browser. Falkon is licensed under GPL version 3 or (at your option) any later version. It is based on QtWebEngine and Qt Framework.");
1234         QString fileIconPath = QApplication::applicationFilePath() + ",1";
1235         QString appIconPath = QApplication::applicationFilePath() + ",0";
1236         m_registerQAppAssociation = new RegisterQAppAssociation("Falkon", QApplication::applicationFilePath(), appIconPath, desc, this);
1237         m_registerQAppAssociation->addCapability(".html", "FalkonHTML", "Falkon HTML Document", fileIconPath, RegisterQAppAssociation::FileAssociation);
1238         m_registerQAppAssociation->addCapability(".htm", "FalkonHTML", "Falkon HTML Document", fileIconPath, RegisterQAppAssociation::FileAssociation);
1239         m_registerQAppAssociation->addCapability("http", "FalkonURL", "Falkon URL", appIconPath, RegisterQAppAssociation::UrlAssociation);
1240         m_registerQAppAssociation->addCapability("https", "FalkonURL", "Falkon URL", appIconPath, RegisterQAppAssociation::UrlAssociation);
1241         m_registerQAppAssociation->addCapability("ftp", "FalkonURL", "Falkon URL", appIconPath, RegisterQAppAssociation::UrlAssociation);
1242     }
1243     return m_registerQAppAssociation;
1244 }
1245 #endif
1246 
1247 #ifdef Q_OS_MACOS
1248 #include <QFileOpenEvent>
1249 
event(QEvent * e)1250 bool MainApplication::event(QEvent* e)
1251 {
1252     switch (e->type()) {
1253     case QEvent::FileOpen: {
1254         QFileOpenEvent *ev = static_cast<QFileOpenEvent*>(e);
1255         if (!ev->url().isEmpty()) {
1256             addNewTab(ev->url());
1257         } else if (!ev->file().isEmpty()) {
1258             addNewTab(QUrl::fromLocalFile(ev->file()));
1259         } else {
1260             return false;
1261         }
1262         return true;
1263     }
1264 
1265     case QEvent::ApplicationActivate:
1266         if (!activeWindow() && m_windows.isEmpty())
1267             createWindow(Qz::BW_NewWindow);
1268         break;
1269 
1270     default:
1271         break;
1272     }
1273 
1274     return QtSingleApplication::event(e);
1275 }
1276 #endif
1277