コード例 #1
0
def create_profile():
    ans = getattr(create_profile, 'ans', None)
    if ans is None:
        ans = QWebEngineProfile(QApplication.instance())
        ua = 'calibre-editor-preview ' + __version__
        ans.setHttpUserAgent(ua)
        if is_running_from_develop:
            from calibre.utils.rapydscript import compile_editor
            compile_editor()
        js = P('editor.js', data=True, allow_user_override=False)
        cparser = P('csscolorparser.js', data=True, allow_user_override=False)

        insert_scripts(
            ans,
            create_script('csscolorparser.js', cparser),
            create_script('editor.js', js),
        )
        url_handler = UrlSchemeHandler(ans)
        ans.installUrlSchemeHandler(QByteArray(FAKE_PROTOCOL.encode('ascii')),
                                    url_handler)
        s = ans.settings()
        s.setDefaultTextEncoding('utf-8')
        s.setAttribute(s.FullScreenSupportEnabled, False)
        s.setAttribute(s.LinksIncludedInFocusChain, False)
        create_profile.ans = ans
    return ans
コード例 #2
0
ファイル: web_view.py プロジェクト: zwpaper/calibre
def create_profile():
    ans = getattr(create_profile, 'ans', None)
    if ans is None:
        ans = QWebEngineProfile(QApplication.instance())
        osname = 'windows' if iswindows else ('macos' if ismacos else 'linux')
        # DO NOT change the user agent as it is used to workaround
        # Qt bugs see workaround_qt_bug() in ajax.pyj
        ua = 'calibre-viewer {} {}'.format(__version__, osname)
        ans.setHttpUserAgent(ua)
        if is_running_from_develop:
            from calibre.utils.rapydscript import compile_viewer
            prints('Compiling viewer code...')
            compile_viewer()
        js = P('viewer.js', data=True, allow_user_override=False)
        translations_json = get_translations_data() or b'null'
        js = js.replace(b'__TRANSLATIONS_DATA__', translations_json, 1)
        if in_develop_mode:
            js = js.replace(b'__IN_DEVELOP_MODE__', b'1')
        insert_scripts(ans, create_script('viewer.js', js))
        url_handler = UrlSchemeHandler(ans)
        ans.installUrlSchemeHandler(QByteArray(FAKE_PROTOCOL.encode('ascii')), url_handler)
        s = ans.settings()
        s.setDefaultTextEncoding('utf-8')
        s.setAttribute(QWebEngineSettings.WebAttribute.LinksIncludedInFocusChain, False)
        create_profile.ans = ans
    return ans
コード例 #3
0
def create_profile():
    ans = getattr(create_profile, 'ans', None)
    if ans is None:
        ans = QWebEngineProfile(QApplication.instance())
        ua = 'calibre-editor-preview ' + __version__
        ans.setHttpUserAgent(ua)
        if is_running_from_develop:
            from calibre.utils.rapydscript import compile_editor
            compile_editor()
        js = P('editor.js', data=True, allow_user_override=False)
        cparser = P('csscolorparser.js', data=True, allow_user_override=False)
        dark_mode_css = P('dark_mode.css',
                          data=True,
                          allow_user_override=False).decode('utf-8')

        insert_scripts(
            ans, create_script('csscolorparser.js', cparser),
            create_script('editor.js', js),
            create_script('dark-mode.js',
                          '''
            (function() {
                var settings = JSON.parse(navigator.userAgent.split('|')[1]);
                var dark_css = CSS;

                function apply_body_colors(event) {
                    if (document.documentElement) {
                        if (settings.bg) document.documentElement.style.backgroundColor = settings.bg;
                        if (settings.fg) document.documentElement.style.color = settings.fg;
                    }
                    if (document.body) {
                        if (settings.bg) document.body.style.backgroundColor = settings.bg;
                        if (settings.fg) document.body.style.color = settings.fg;
                    }
                }

                function apply_css() {
                    var css = '';
                    if (settings.link) css += 'html > body :link, html > body :link * { color: ' + settings.link + ' !important; }';
                    if (settings.is_dark_theme) { css += dark_css; }
                    var style = document.createElement('style');
                    style.textContent = css;
                    document.documentElement.appendChild(style);
                    apply_body_colors();
                }

                apply_body_colors();
                document.addEventListener("DOMContentLoaded", apply_css);
            })();
            '''.replace('CSS', json.dumps(dark_mode_css), 1),
                          injection_point=QWebEngineScript.InjectionPoint.
                          DocumentCreation))
        url_handler = UrlSchemeHandler(ans)
        ans.installUrlSchemeHandler(QByteArray(FAKE_PROTOCOL.encode('ascii')),
                                    url_handler)
        s = ans.settings()
        s.setDefaultTextEncoding('utf-8')
        s.setAttribute(s.FullScreenSupportEnabled, False)
        s.setAttribute(s.LinksIncludedInFocusChain, False)
        create_profile.ans = ans
    return ans
コード例 #4
0
ファイル: lookup.py プロジェクト: zyhong/calibre
def create_profile():
    ans = getattr(create_profile, 'ans', None)
    if ans is None:
        ans = QWebEngineProfile('viewer-lookup', QApplication.instance())
        ans.setHttpUserAgent(random_user_agent(allow_ie=False))
        ans.setCachePath(os.path.join(cache_dir(), 'ev2vl'))
        js = P('lookup.js', data=True, allow_user_override=False)
        insert_scripts(ans, create_script('lookup.js', js))
        s = ans.settings()
        s.setDefaultTextEncoding('utf-8')
        create_profile.ans = ans
    return ans
コード例 #5
0
ファイル: web_view.py プロジェクト: benquike/calibre
def create_profile():
    ans = getattr(create_profile, 'ans', None)
    if ans is None:
        ans = QWebEngineProfile(QApplication.instance())
        ua = 'calibre-viewer ' + __version__
        ans.setHttpUserAgent(ua)
        if is_running_from_develop:
            from calibre.utils.rapydscript import compile_viewer
            print('Compiling viewer code...')
            compile_viewer()
        js = P('viewer.js', data=True, allow_user_override=False)
        translations_json = get_translations_data() or b'null'
        js = js.replace(b'__TRANSLATIONS_DATA__', translations_json, 1)
        insert_scripts(ans, create_script('viewer.js', js))
        url_handler = UrlSchemeHandler(ans)
        ans.installUrlSchemeHandler(QByteArray(FAKE_PROTOCOL.encode('ascii')), url_handler)
        s = ans.settings()
        s.setDefaultTextEncoding('utf-8')
        s.setAttribute(s.LinksIncludedInFocusChain, False)
        create_profile.ans = ans
    return ans
コード例 #6
0
    def __init__(self, opts, log, container_root):
        QObject.__init__(self)
        self.interceptor = RequestInterceptor(self)
        self.has_maths = {}
        self.interceptor.log = self.log = log
        self.interceptor.container_root = os.path.normcase(os.path.abspath(container_root))
        self.interceptor.resources_root = os.path.normcase(os.path.abspath(os.path.dirname(mathjax_dir())))
        ans = QWebEngineProfile(QApplication.instance())
        ua = 'calibre-pdf-output ' + __version__
        ans.setHttpUserAgent(ua)
        s = ans.settings()
        s.setDefaultTextEncoding('utf-8')
        ans.setUrlRequestInterceptor(self.interceptor)
        self.profile = ans

        self.opts = opts
        self.workers = []
        self.max_workers = detect_ncpus()
        if iswindows:
            self.original_signal_handlers = {}
        else:
            self.original_signal_handlers = setup_unix_signals(self)
コード例 #7
0
ファイル: njaxt.py プロジェクト: ujjwal96/njaXt
class Njaxt(QtWidgets.QMainWindow, njaxt_ui.Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.fuzzer = None
        self.stopLoop = False
        self.fuzz_widget = FuzzerWidget()
        self.payload_widget = PayloadsWidget()
        self.init_webview()
        self.init_sigslot()
        self.show()

    def init_webview(self):
        self.webProfile = QWebEngineProfile(self.webView)
        self.webProfile.setHttpUserAgent(
            "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36"
        )
        self.webProfile.setHttpAcceptLanguage("en-US,en;q=0.8")
        self.webProfile.settings().setAttribute(
            QWebEngineSettings.LocalStorageEnabled, True)
        self.set_scripts()
        self.webPage = QWebEnginePage(self.webProfile, self)

        self.webView.setPage(self.webPage)
        self.webView.loadProgress.connect(self.set_progress)
        self.webView.loadFinished.connect(
            lambda: self.lineEdit.setText(self.webView.url().toString()))

    def set_scripts(self):
        script = QWebEngineScript()
        script.setInjectionPoint(QWebEngineScript.DocumentCreation)
        script.setWorldId(QWebEngineScript.MainWorld)
        script.setSourceCode("""(function() {
                                    var _old_alert = window.alert;
                                    window.alert = function() {
                                        document.activeElement.innerHTML += "<br>alerting";
                                        _old_alert.apply(window,arguments);
                                        document.activeElement.innerHTML += "<br>done alerting<br>";
                                    };
                                })();
                             """)
        self.webProfile.scripts().insert(script)

    def init_sigslot(self):
        self.lineEdit.returnPressed.connect(self.make_fuzzer)
        self.pushButton.clicked.connect(self.make_fuzzer)
        self.pushButtonReset.clicked.connect(self.handle_reset)

        self.actionAdvanced_Fuzzer.triggered.connect(self.show_fuzz)
        self.fuzz_widget.pushButton.clicked.connect(self.make_fuzzer)

        self.actionWordlist.triggered.connect(self.show_payloads)

    def set_progress(self, p):
        self.progressBar.setValue(p)

    def show_fuzz(self):
        self.fuzz_widget.show()

    def show_payloads(self):
        self.payload_widget.show()

    def make_fuzzer(self):
        if self.fuzz_widget.data:
            self.fuzzer = fuzzer.Fuzzer(self.fuzz_widget.data)
        else:
            self.fuzzer = fuzzer.Fuzzer({"url": self.lineEdit.text()})

        self.fuzz()

    def handle_reset(self):
        # Reset Fuzz Widget
        self.fuzz_widget.reset_widget()
        # Reset Main Form
        self.fuzzer = fuzzer.Fuzzer({"url": ""})
        self.init_webview()
        self.lineEdit.setText("")
        self.set_progress(0)
        self.stopLoop = False

    def detect_xss(self, source):
        if "<br>alerting" in source:
            print(self.lineEdit.text())
            self.stopLoop = True

    def fuzz(self):
        loop = QEventLoop()
        for request in self.fuzzer.requests(PAYLOADS):
            self.webView.loadFinished.connect(loop.quit)
            self.webView.load(request)
            loop.exec()
            self.webView.page().toHtml(self.detect_xss)
            if self.stopLoop:
                break
コード例 #8
0
class MainApplication(QtSingleApp):
    s_testMode = False

    # type AfterLaunch
    OpenBlankPage = 0
    OpenHomePage = 1
    OpenSpeedDial = 2
    RestoreSession = 3
    SelectSession = 4

    def __init__(self, argv):  # noqa 901
        super(MainApplication, self).__init__(argv)
        self._isPrivate = False
        self._isPortable = True
        self._isClosing = False
        self._isStartingAfterCrash = False

        self._history = None  # History
        self._bookmarks = None  # Bookmarks
        self._autoFill = None  # AutoFill
        self._cookieJar = None  # CookieJar
        self._plugins = None  # PluginProxy
        self._browsingLibrary = None  # BrowsingLibrary

        self._networkManager = None
        self._restoreManager = None
        self._sessionManager = None
        self._downloadManager = None
        self._userAgentManager = None
        self._searchEnginesManager = None
        self._closedWindowsManager = None
        self._protocolHandlerManager = None
        self._html5PermissionsManager = None
        self._desktopNotifications = None  # DesktopNotificationsFactory
        self._webProfile = None  # QWebEngineProfile

        self._autoSaver = None
        self._proxyStyle = None
        self._wmClass = QByteArray()

        self._windows = []
        self._lastActiveWindow = None
        self._postLaunchActions = []

        self.setAttribute(Qt.AA_UseHighDpiPixmaps)
        self.setAttribute(Qt.AA_DontCreateNativeWidgetSiblings)

        self.setApplicationName('demo')
        self.setOrganizationDomain('org.autowin')
        self.setWindowIcon(QIcon.fromTheme('demo', QIcon(':/icons/demo.svg')))
        self.setDesktopFileName('orig.autowin.demo')

        self.setApplicationVersion('1.0')

        # Set fallback icon theme (eg. on Windows/Mac)
        if QIcon.fromTheme('view-refresh').isNull():
            QIcon.setThemeName('breeze-fallback')

        # QSQLITE database plugin is required
        if not QSqlDatabase.isDriverAvailable('QSQLITE'):
            QMessageBox.Critical(
                None, 'Error', 'Qt SQLite database plugin is not available.'
                ' Please install it and restart the application.')
            self._isClosing = True
            return
        if const.OS_WIN:
            # Set default app font (needed for N'ko)
            fontId = QFontDatabase.addApplicationFont('font.ttf')
            if fontId != -1:
                families = QFontDatabase.applicationFontFamilies(fontId)
                if not families.empty():
                    self.setFont(QFont(families.at(0)))

        startUrl = QUrl()
        startProfile = ''
        messages = []

        noAddons = False
        newInstance = False

        if len(argv) > 1:
            cmd = CommandLineOptions()
            for pair in cmd.getActions():
                action = pair.action
                text = pair.text
                if action == const.CL_StartWithoutAddons:
                    noAddons = True
                elif action == const.CL_StartWithProfile:
                    startProfile = text
                elif action == const.CL_StartPortable:
                    self._isPortable = True
                elif action == const.CL_NewTab:
                    messages.append("ACTION:NewTab")
                    self._postLaunchActions.append(self.OpenNewTab)
                elif action == const.CL_NewWindow:
                    messages.append("ACTION:NewWindow")
                elif action == const.CL_ToggleFullScreen:
                    messages.append("ACTION:ToggleFullScreen")
                    self._postLaunchActions.append(self.ToggleFullScreen)
                elif action == const.CL_ShowDownloadManager:
                    messages.append("ACTION:ShowDownloadManager")
                    self._postLaunchActions.append(self.OpenDownloadManager)
                elif action == const.CL_StartPrivateBrowsing:
                    self._isPrivate = True
                elif action == const.CL_StartNewInstance:
                    newInstance = True
                elif action == const.CL_OpenUrlInCurrentTab:
                    startUrl = QUrl.fromUserInput(text)
                    messages.append("ACTION:OpenUrlInCurrentTab" + text)
                elif action == const.CL_OpenUrlInNewWindow:
                    startUrl = QUrl.fromUserInput(text)
                    messages.append("ACTION:OpenUrlInNewWindow" + text)
                elif action == const.CL_OpenUrl:
                    startUrl = QUrl.fromUserInput(text)
                    messages.append("URL:" + text)
                elif action == const.CL_ExitAction:
                    self._isClosing = True
                    return
                elif action == const.CL_WMClass:
                    self._wmClass = text

        if not self.isPortable():
            appConf = QSettings(
                pathjoin(self.applicationDirPath(), '%s.conf' % const.APPNAME),
                QSettings.IniFormat)
            appConf.value('Config/Portable')

        if self.isPortable():
            print('%s: Running in Portable Mode.' % const.APPNAME)
            DataPaths.setPortableVersion()

        # Don't start single application in private browsing
        if not self.isPrivate():
            appId = 'org.autowin.mc'

            if self.isPortable():
                appId += '.Portable'

            if self.isTestModeEnabled():
                appId += '.TestMode'

            if newInstance:
                if not startProfile or startProfile == 'default':
                    print(
                        'New instance cannot be started with default profile!')
                else:
                    # Generate unique appId so it is possible to start more
                    # separate instances of the same profile. It is dangerous to
                    # run more instance of the same profile, but if the user
                    # wants it, we should allow it.
                    appId += '.' + str(QDateTime.currentMSecsSinceEpoch())

            self.setAppId(appId)

        # If there is nothing to tell other instance, we need to at least weak it
        if not messages:
            messages.append(' ')

        if self.isRunning():
            self._isClosing = True
            for message in messages:
                self.sendMessage(message)
            return

        if const.OS_MACOS:
            self.setQuitOnLastWindowClosed(False)
            # TODO:
            # disable tabbing issue #2261
            # extern void disableWindowTabbing();
            # self.disableWindowTabbing()
        else:
            self.setQuitOnLastWindowClosed(True)

        QSettings.setDefaultFormat(QSettings.IniFormat)
        QDesktopServices.setUrlHandler('http', self.addNewTab)
        QDesktopServices.setUrlHandler('https', self.addNewTab)
        QDesktopServices.setUrlHandler('ftp', self.addNewTab)

        profileManager = ProfileManager()
        profileManager.initConfigDir()
        profileManager.initCurrentProfile(startProfile)

        Settings.createSettings(
            pathjoin(DataPaths.currentProfilePath(), 'settings.ini'))

        NetworkManager.registerSchemes()

        if self.isPrivate():
            self._webProfile = QWebEngineProfile()
        else:
            self._webProfile = QWebEngineProfile.defaultProfile()
        self._webProfile.downloadRequested.connect(self.downloadRequested)

        self._networkManager = NetworkManager(self)

        self.setupUserScripts()

        if not self.isPrivate() and not self.isTestModeEnabled():
            self._sessionManager = SessionManager(self)
            self._autoSaver = AutoSaver(self)
            self._autoSaver.save.connect(
                self._sessionManager.autoSaveLastSession)

            settings = Settings()
            settings.beginGroup('SessionRestore')
            wasRunning = settings.value('isRunning', False)
            wasRestoring = settings.value('isRestoring', False)
            settings.setValue('isRunning', True)
            settings.setValue('isRestoring', wasRunning)
            settings.endGroup()
            settings.sync()

            self._isStartingAfterCrash = bool(wasRunning and wasRestoring)

            if wasRunning:
                QTimer.singleShot(
                    60 * 1000, lambda: Settings().setValue(
                        'SessionRestore/isRestoring', False))

            # we have to ask about startup session before creating main window
            if self._isStartingAfterCrash and self.afterLaunch(
            ) == self.SelectSession:
                self._restoreManager = RestoreManager(
                    self.sessionManager().askSessionFromUser())

        self.loadSettings()

        self._plugins = PluginProxy(self)
        self._autoFill = AutoFill(self)
        self.app.protocolHandlerManager()

        if not noAddons:
            self._plugins.loadPlugins()

        window = self.createWindow(const.BW_FirstAppWindow, startUrl)
        window.startingCompleted.connect(self.restoreOverrideCursor)

        self.focusChanged.connect(self.onFocusChanged)

        if not self.isPrivate() and not self.isTestModeEnabled():
            # check updates
            settings = Settings()
            checkUpdates = settings.value('Web-Browser-Settings/CheckUpdates',
                                          True)

            if checkUpdates:
                Updater(window)

            self.sessionManager().backupSavedSessions()

            if self._isStartingAfterCrash or self.afterLaunch(
            ) == self.RestoreSession:
                self._restoreManager = RestoreManager(
                    self.sessionManager().lastActiveSessionPath())
                if not self._restoreManager.isValid():
                    self.destroyRestoreManager()

            if not self._isStartingAfterCrash and self._restoreManager:
                self.restoreSession(window, self._restoreManager.restoreData())

        QSettings.setPath(QSettings.IniFormat, QSettings.UserScope,
                          DataPaths.currentProfilePath())

        self.messageReceived.connect(self.messageReceivedCb)
        self.aboutToQuit.connect(self.saveSettings)

        QTimer.singleShot(0, self.postLaunch)

    def __delete__(self):
        self._isClosing = True

        IconProvider.instance().saveIconsToDatabase()

        # Wait for all QtConcurrent jobs to finish
        QThreadPool.globalInstance().waitForDone()

        # Delete all classes that are saving data in destructor
        self._bookmarks = None
        self._cookieJar = None

        Settings.syncSettings()

    @cached_property
    def app(self):
        return MainApplication.instance()

    def isClosing(self):
        return self._isClosing

    def isPrivate(self):
        return self._isPrivate

    def isPortable(self):
        return self._isPortable

    def isStartingAfterCrash(self):
        return self._isStartingAfterCrash

    def windowCount(self):
        return len(self._windows)

    def windows(self):
        '''
        @return: QList<BrowserWindow*>
        '''
        return self._windows

    def getWindow(self):
        '''
        @return: BrowserWindow
        '''
        if self._lastActiveWindow:
            return self._lastActiveWindow
        if not self._windows:
            return None
        else:
            return self._windows[0]

    def createWindow(self, type_, startUrl=QUrl()):
        '''
        @param: type_ const.BrowserWindowType
        @param: startUrl QUrl
        '''
        window = BrowserWindow(type_, startUrl)

        def windowDestroyFunc():
            self.windowDestroyed(window)

        window.destroyed.connect(windowDestroyFunc)

        self._windows.insert(0, window)
        return window

    def afterLaunch(self):
        return Settings().value('Web-URL-Settings/afterLaunch',
                                self.RestoreSession,
                                type=int)

    def openSession(self, window, restoreData):
        '''
        @param: window BrowserWindow*
        @param: restoreData RestoreData
        '''
        self.setOverrideCursor(Qt.BusyCursor)
        if not window:
            window = self.createWindow(const.BW_OtherRestoredWindow)
        if window.tabCount() != 0:
            # This can only happend when recovering crashed session!
            # Don't restore tabs in current window as user already opened some
            # new tabs
            window = self.createWindow(const.BW_OtherRestoredWindow) \
                .restoreWindow(restoreData.windows.pop(0))
        else:
            window.restoreWindow(restoreData.windows.pop(0))

        # data -> BrowserWindow::SavedWindow
        for data in restoreData.windows:
            window = self.createWindow(const.BW_OtherRestoredWindow)
            window.restoreWindow(data)
        self._closedWindowsManager.restoreState(restoreData.closedWindows)
        self.restoreOverrideCursor()

    def restoreSession(self, window, restoreData):
        if self._isPrivate or not restoreData.isValid():
            return False

        self.openSession(window, restoreData)

        self._restoreManager.clearRestoreData()
        self.destroyRestoreManager()

        return True

    def destroyRestoreManager(self):
        if self._restoreManager and self._restoreManager.isValid():
            return True
        self._restoreManager = None

    def reloadSettings(self):
        self.loadSettings()
        self.settingsReloaded.emit()

    def styleName(self):
        if self._proxyStyle:
            return self._proxyStyle.name()
        else:
            return ''

    def setProxyStyle(self, style):
        '''
        @param: style ProxyStyle
        '''
        self._proxyStyle = style
        self.setStyle(style)

    def wmClass(self):
        return self._wmClass

    def history(self):
        if not self._history:
            self._history = History(self)
        return self._history

    def bookmarks(self):
        if not self._bookmarks:
            self._bookmarks = Bookmarks(self)
        return self._bookmarks

    def autoFill(self):
        return self._autoFill

    def cookieJar(self):
        if not self._cookieJar:
            self._cookieJar = CookieJar(self)
        return self._cookieJar

    def plugins(self):
        return self._plugins

    def browsingLibrary(self):
        if not self._browsingLibrary:
            self._browsingLibrary = BrowsingLibrary(self.getWindow())
        return self._browsingLibrary

    def networkManager(self):
        return self._networkManager

    def restoreManager(self):
        return self._restoreManager

    def sessionManager(self):
        return self._sessionManager

    def downloadManager(self):
        if not self._downloadManager:
            self._downloadManager = DownloadManager()
        return self._downloadManager

    def userAgentManager(self):
        if not self._userAgentManager:
            self._userAgentManager = UserAgentManager()
        return self._userAgentManager

    def searchEnginesManager(self):
        if not self._searchEnginesManager:
            self._searchEnginesManager = SearchEnginesManager()
        return self._searchEnginesManager

    def closedWindowsManager(self):
        if not self._closedWindowsManager:
            self._closedWindowsManager = ClosedWindowsManager()
        return self._closedWindowsManager

    def protocolHandlerManager(self):
        if not self._protocolHandlerManager:
            self._protocolHandlerManager = ProtocolHandlerManager()
        return self._protocolHandlerManager

    def html5PermissionsManager(self):
        if not self._html5PermissionsManager:
            self._html5PermissionsManager = HTML5PermissionsManager(self)
        return self._html5PermissionsManager

    def desktopNotifications(self):
        if not self._desktopNotifications:
            self._desktopNotifications = DesktopNotificationsFactory(self)
        return self._desktopNotifications

    def webProfile(self):
        return self._webProfile

    def webSettings(self):
        return self._webProfile.settings()

    @classmethod
    def instance(cls):
        return QCoreApplication.instance()

    @classmethod
    def isTestModeEnabled(cls):
        return cls.s_testMode

    @classmethod
    def setTestModeEnabled(cls, enabled):
        cls.s_testMode = enabled

    # Q_SLOTS
    def addNewTab(self, url=QUrl()):
        window = self.getWindow()
        if window:
            window.tabWidget().addView(
                url,
                url.isEmpty() and const.NT_SelectedNewEmptyTab
                or const.NT_SelectedTabAtTheEnd)

    def startPrivateBrowsing(self, startUrl=QUrl()):
        url = startUrl
        act = self.sender()
        if isinstance(act, QAction):
            url = QUrl(act.data())
        args = [const.MAIN_PATH]
        args.append('--private-browsing')
        args.append('--profile=%s' % ProfileManager.currentProfile())
        if not url.isEmpty():
            args.append(url.toEncoded().data().decode())
        if not QProcess.startDetached(self.applicationFilePath(), args):
            print(
                'MainApplication: Cannot start new browser process for private browsing!'
                ' %s %s' % (self.applicationFilePath(), args),
                file=stderr)

    def reloadUserStyleSheet(self):
        userCssFile = Settings().value('Web-Browser-Settings/userStyleSheet',
                                       '')
        self.setUserStyleSheet(userCssFile)

    def restoreOverrideCursor(self):
        super().restoreOverrideCursor()

    def changeOccurred(self):
        if self._autoSaver:
            self._autoSaver.changeOccurred()

    def quitApplication(self):
        if self._downloadManager and not self._downloadManager.canClose():
            self._downloadManager.show()
            return
        for window in self._windows:
            window.aboutToClose.emit()
        if self._sessionManager and len(self._windows) > 0:
            self._sessionManager.autoSaveLastSession()
        self._isClosing = True
        for window in self._windows:
            window.close()
        # Saving settings in saveSettings() slot called from quit() so
        # everything gets saved also when quitting application in other
        # way than clicking Quit action in File menu or closing last window
        # eg. on Mac (#157)
        if not self.isPrivate():
            self.removeLockFile()
        self.quit()

    # Q_SIGNALS
    settingsReloaded = pyqtSignal()
    activeWindowChanged = pyqtSignal(BrowserWindow)

    # private Q_SLOTS
    def postLaunch(self):
        if self.OpenDownloadManager in self._postLaunchActions:
            self.downloadManager().show()
        if self.OpenNewTab in self._postLaunchActions:
            self.getWindow().tabWidget().addView(QUrl(),
                                                 const.NT_SelectedNewEmptyTab)
        if self.ToggleFullScreen in self._postLaunchActions:
            self.getWindow().toggleFullScreen()

        self._createJumpList()
        self._initPulseSupport()

        QTimer.singleShot(5000, self.runDeferredPostLaunchActions)

    def saveState(self):
        restoreData = RestoreData()
        for window in self._windows:
            restoreData.windows.append(BrowserWindow.SavedWindow(window))
        if self._restoreManager and self._restoreManager.isValid():
            stream = QDataStream(restoreData.crashedSession,
                                 QIODevice.WriteOnly)
            stream.writeQVariant(self._restoreManager.restoreData())
        restoreData.closedWindows = self._closedWindowsManager.saveState()
        data = QByteArray()
        stream = QDataStream(data, QIODevice.WriteOnly)

        stream.writeInt(const.sessionVersion)
        stream.writeBytes(pickle.dumps(restoreData))
        return data

    def saveSettings(self):
        if self.isPrivate():
            return
        self._isClosing = True
        settings = Settings()
        settings.beginGroup('SessionRestore')
        settings.setValue('isRunning', False)
        settings.setValue('isRestoring', False)
        settings.endGroup()

        settings.beginGroup('Web-Browser-Settings')
        deleteCache = settings.value('deleteCacheOnClose', False)
        deleteHistory = settings.value('deleteHistoryOnClose', False)
        deleteHtml5Storage = settings.value('deleteHTML5StorageOnClose', False)
        settings.endGroup()

        settings.beginGroup('Cookies-Settings')
        deleteCookies = settings.value('deleteCookiesOnClose', False)
        settings.endGroup()
        settings.sync()
        del settings

        if deleteHistory:
            self._history.clearHistory()
        if deleteHtml5Storage:
            ClearPrivateData.clearLocalStorage()
        if deleteCookies:
            self._cookieJar.deleteAllCookies(False)
        if deleteCache:
            gVar.appTools.removeRecursively(self.app.webProfile().cachePath())

        if self._searchEnginesManager:
            self._searchEnginesManager.saveSettings()
        self._plugins.shutdown()
        self._networkManager.shutdown()

        gVar.appSettings.saveSettings()
        webpageIconsPath = pathjoin(DataPaths.currentProfilePath(),
                                    'webpageIcons.db')
        if pathexists(webpageIconsPath):
            remove(webpageIconsPath)
        self.sessionManager().saveSettings()

    def messageReceivedCb(self, message):
        '''
        @param: message string
        '''
        actWin = self.getWindow()
        actUrl = QUrl()
        if message.startswith('URL:'):
            url = QUrl.fromUserInput(message[4:])
            self.addNewTab(url)
            actWin = self.getWindow()
        elif message.startswith('ACTION:'):
            text = message[7:]
            if text == 'NewTab':
                self.addNewTab()
            elif text == 'NewWindow':
                actWin = self.createWindow(const.BW_NewWindow)
            elif text == 'ShowDownloadManager':
                self.downloadManager().show()
                actWin = self.downloadManager()
            elif text == 'ToggleFullScreen' and actWin:
                assert (isinstance(actWin, BrowserWindow))
                actWin.toggleFullScreen()
            elif text.startswith('OpenUrlInCurrentTab'):
                actUrl = QUrl.fromUserInput(text[:19])
            elif text.startswith('OpenUrlInNewTab'):
                # user attempted to start another instance. let's open a new one
                self.createWindow(const.BW_NewWindow,
                                  QUrl.fromUserInput(text[18:]))
                return
        else:
            actWin = self.createWindow(const.BW_NewWindow)

        if not actWin:
            if not self.isClosing():
                # It can only occur if download manager window was still opened
                self.createWindow(const.BW_NewWindow, actUrl)
            return

        actWin.setWindowState(actWin.windowState() & Qt.WindowMinimized)
        actWin.raise_()
        actWin.activateWindow()
        actWin.setFocus()

        win = actWin
        if isinstance(win, BrowserWindow) and not actWin.isEmpty():
            win.loadAddress(actUrl)

    def windowDestroyed(self, window):
        '''
        @param: message QObject*
        '''
        assert (window)
        assert (window in self._windows)
        self._windows.remove(window)

    def onFocusChanged(self):
        activeBrowserWindow = self.activeWindow()
        if isinstance(activeBrowserWindow, BrowserWindow):
            self._lastActiveWindow = activeBrowserWindow
            self.activeWindowChanged.emit(self._lastActiveWindow)

    def runDeferredPostLaunchActions(self):
        self.checkDefaultWebBrowser()
        self.checkOptimizeDatabase()

    def downloadRequested(self, download):
        '''
        @param: download QWebEngineDownloadItem
        '''
        self.downloadManager().download(download)

    # private
    # enum PostLaunchAction
    OpenDownloadManager = 0
    OpenNewTab = 1
    ToggleFullScreen = 2

    def loadSettings(self):
        settings = Settings()
        settings.beginGroup('Themes')
        activeTheme = settings.value('activeTheme', const.DEFAULT_THEME_NAME)
        settings.endGroup()

        self.loadTheme(activeTheme)

        webSettings = self._webProfile.settings()

        # Web browsing settings
        settings.beginGroup('Web-Browser-Settings')

        webSettings.setAttribute(QWebEngineSettings.LocalStorageEnabled,
                                 settings.value("HTML5StorageEnabled", True))
        webSettings.setAttribute(QWebEngineSettings.PluginsEnabled,
                                 settings.value("allowPlugins", True))
        webSettings.setAttribute(QWebEngineSettings.JavascriptEnabled,
                                 settings.value("allowJavaScript", True))
        webSettings.setAttribute(
            QWebEngineSettings.JavascriptCanOpenWindows,
            settings.value("allowJavaScriptOpenWindow", False))
        webSettings.setAttribute(
            QWebEngineSettings.JavascriptCanAccessClipboard,
            settings.value("allowJavaScriptAccessClipboard", True))
        webSettings.setAttribute(
            QWebEngineSettings.LinksIncludedInFocusChain,
            settings.value("IncludeLinkInFocusChain", False))
        webSettings.setAttribute(QWebEngineSettings.XSSAuditingEnabled,
                                 settings.value("XSSAuditing", False))
        webSettings.setAttribute(
            QWebEngineSettings.PrintElementBackgrounds,
            settings.value("PrintElementBackground", True))
        webSettings.setAttribute(QWebEngineSettings.SpatialNavigationEnabled,
                                 settings.value("SpatialNavigation", False))
        webSettings.setAttribute(QWebEngineSettings.ScrollAnimatorEnabled,
                                 settings.value("AnimateScrolling", True))
        webSettings.setAttribute(QWebEngineSettings.HyperlinkAuditingEnabled,
                                 False)
        webSettings.setAttribute(QWebEngineSettings.FullScreenSupportEnabled,
                                 True)
        webSettings.setAttribute(
            QWebEngineSettings.LocalContentCanAccessRemoteUrls, True)
        webSettings.setAttribute(QWebEngineSettings.FocusOnNavigationEnabled,
                                 False)

        webSettings.setAttribute(
            QWebEngineSettings.AllowWindowActivationFromJavaScript,
            settings.value("allowJavaScriptActivationWindow", False))

        webSettings.setAttribute(QWebEngineSettings.JavascriptCanPaste,
                                 settings.value("allowJavaScriptPaste", True))
        webSettings.setAttribute(
            QWebEngineSettings.PlaybackRequiresUserGesture,
            settings.value("DisableVideoAutoPlay", False))
        webSettings.setAttribute(QWebEngineSettings.WebRTCPublicInterfacesOnly,
                                 settings.value("WebRTCPublicIpOnly", True))
        webSettings.setUnknownUrlSchemePolicy(
            QWebEngineSettings.AllowAllUnknownUrlSchemes)

        webSettings.setAttribute(QWebEngineSettings.DnsPrefetchEnabled,
                                 settings.value("DNSPrefetch", True))

        webSettings.setDefaultTextEncoding(
            settings.value("DefaultEncoding",
                           webSettings.defaultTextEncoding(),
                           type=str))

        scrollLines = settings.value("wheelScrollLines",
                                     self.wheelScrollLines())
        self.setWheelScrollLines(scrollLines)

        userCss = settings.value("userStyleSheet", '')
        settings.endGroup()

        self.setUserStyleSheet(userCss)

        settings.beginGroup("Browser-Fonts")
        webSettings.setFontFamily(
            QWebEngineSettings.StandardFont,
            settings.value("StandardFont",
                           webSettings.fontFamily(
                               QWebEngineSettings.StandardFont),
                           type=str))
        webSettings.setFontFamily(
            QWebEngineSettings.CursiveFont,
            settings.value("CursiveFont",
                           webSettings.fontFamily(
                               QWebEngineSettings.CursiveFont),
                           type=str))
        webSettings.setFontFamily(
            QWebEngineSettings.FantasyFont,
            settings.value("FantasyFont",
                           webSettings.fontFamily(
                               QWebEngineSettings.FantasyFont),
                           type=str))
        webSettings.setFontFamily(
            QWebEngineSettings.FixedFont,
            settings.value("FixedFont",
                           webSettings.fontFamily(
                               QWebEngineSettings.FixedFont),
                           type=str))
        webSettings.setFontFamily(
            QWebEngineSettings.SansSerifFont,
            settings.value("SansSerifFont",
                           webSettings.fontFamily(
                               QWebEngineSettings.SansSerifFont),
                           type=str))
        webSettings.setFontFamily(
            QWebEngineSettings.SerifFont,
            settings.value("SerifFont",
                           webSettings.fontFamily(
                               QWebEngineSettings.SerifFont),
                           type=str))
        webSettings.setFontSize(QWebEngineSettings.DefaultFontSize,
                                settings.value("DefaultFontSize", 15))
        webSettings.setFontSize(QWebEngineSettings.DefaultFixedFontSize,
                                settings.value("FixedFontSize", 14))
        webSettings.setFontSize(QWebEngineSettings.MinimumFontSize,
                                settings.value("MinimumFontSize", 3))
        webSettings.setFontSize(QWebEngineSettings.MinimumLogicalFontSize,
                                settings.value("MinimumLogicalFontSize", 5))
        settings.endGroup()

        profile = QWebEngineProfile.defaultProfile()
        profile.setPersistentCookiesPolicy(
            QWebEngineProfile.AllowPersistentCookies)
        profile.setPersistentStoragePath(DataPaths.currentProfilePath())

        defaultPath = DataPaths.path(DataPaths.Cache)
        if not defaultPath.startswith(DataPaths.currentProfilePath()):
            defaultPath += '/' + ProfileManager.currentProfile()
        cachePath = settings.value("Web-Browser-Settings/CachePath",
                                   defaultPath,
                                   type=str)
        profile.setCachePath(cachePath)

        allowCache = settings.value("Web-Browser-Settings/AllowLocalCache",
                                    True)
        profile.setHttpCacheType(allowCache and QWebEngineProfile.DiskHttpCache
                                 or QWebEngineProfile.MemoryHttpCache)

        cacheSize = settings.value("Web-Browser-Settings/LocalCacheSize",
                                   50) * 1000 * 1000
        profile.setHttpCacheMaximumSize(cacheSize)

        settings.beginGroup("SpellCheck")
        profile.setSpellCheckEnabled(settings.value("Enabled", False))
        profile.setSpellCheckLanguages(settings.value("Languages", type=list))
        settings.endGroup()

        if self.isPrivate():
            webSettings.setAttribute(QWebEngineSettings.LocalStorageEnabled,
                                     False)
            self.history().setSaving(False)

        if self._downloadManager:
            self._downloadManager.loadSettings()

        gVar.appSettings.loadSettings()
        self.userAgentManager().loadSettings()
        self.networkManager().loadSettings()

    def loadTheme(self, name):
        activeThemePath = DataPaths.locate(DataPaths.Themes, name)

        if not activeThemePath:
            print('Warning: Cannot load theme %s' % name)
            activeThemePath = '%s/%s' % (DataPaths.path(
                DataPaths.Themes), const.DEFAULT_THEME_NAME)

        qss = gVar.appTools.readAllFileContents(activeThemePath + '/main.css')

        if const.OS_MACOS:
            qss += gVar.appTools.readAllFileContents(activeThemePath +
                                                     '/mac.css')
        elif const.OS_UNIX:
            qss += gVar.appTools.readAllFileContents(activeThemePath +
                                                     '/linux.css')
        elif const.OS_WIN:
            qss += gVar.appTools.readAllFileContents(activeThemePath +
                                                     '/windows.css')

        if self.isRightToLeft():
            qss += gVar.appTools.readAllFileContents(activeThemePath +
                                                     'rtl.css')

        if self.isPrivate():
            qss += gVar.appTools.readAllFileContents(activeThemePath +
                                                     'private.css')

        qss += gVar.appTools.readAllFileContents(activeThemePath +
                                                 'userChrome.css')

        relativePath = QDir.current().relativeFilePath(activeThemePath)
        qss = re.sub("url\\s*\\(\\s*([^\\*:\\);]+)\\s*\\)",
                     r'url(' + relativePath + r'/\1)', qss)
        self.setStyleSheet(qss)

    def setupUserScripts(self):
        # WebChannel for SafeJsWorld
        script = QWebEngineScript()
        script.setName('_app_webchannel')
        script.setInjectionPoint(QWebEngineScript.DocumentCreation)
        script.setWorldId(WebPage.SafeJsWorld)
        script.setRunsOnSubFrames(True)
        script.setSourceCode(Scripts.setupWebChannel())
        self._webProfile.scripts().insert(script)

        # app:restore
        appRestore = QWebEngineScript()
        appRestore.setWorldId(WebPage.SafeJsWorld)
        appRestore.setSourceCode(
            gVar.appTools.readAllFileContents(':html/restore.user.js'))
        self._webProfile.scripts().insert(appRestore)

        # app:speeddial
        appSpeedDial = QWebEngineScript()
        appSpeedDial.setWorldId(WebPage.SafeJsWorld)
        appSpeedDial.setSourceCode(Scripts.setupSpeedDial())
        self._webProfile.scripts().insert(appSpeedDial)

        # document.window object addons
        documentWindowAddons = QWebEngineScript()
        documentWindowAddons.setName('_app_window_object')
        documentWindowAddons.setInjectionPoint(
            QWebEngineScript.DocumentCreation)
        documentWindowAddons.setWorldId(WebPage.UnsafeJsWorld)
        documentWindowAddons.setRunsOnSubFrames(True)
        documentWindowAddons.setSourceCode(Scripts.setupWindowObject())
        self._webProfile.scripts().insert(documentWindowAddons)

    def setUserStyleSheet(self, filePath):
        userCss = ''
        if not const.OS_UNIX and not const.OS_MACOS:
            # Don't grey out selection on losing focus (to prevent graying out
            # found text)
            highlightColor = ''
            highlightedTextColor = ''
            if const.OS_MACOS:  # TODO: ? can not enter this branch
                highlightColor = '#b6d6fc'
                highlightedTextColor = '#000'
            else:
                pal = self.style().standardPalette()
                highlightColor = pal.color(QPalette.Highlight).name()
                highlightedTextColor = pal.color(
                    QPalette.HighlightedText).name()
            userCss += "::selection {background: %s; color: %s;} " % \
                (highlightColor, highlightedTextColor)

        userCss += gVar.appTools.readAllFileContents(filePath).replace(
            '\n', '')

        name = '_app_userstylesheet'

        oldScript = self._webProfile.scripts().findScript(name)
        if not oldScript.isNull():
            self._webProfile.scripts().remove(oldScript)

        if not userCss:
            return

        script = QWebEngineScript()
        script.setName(name)
        script.setInjectionPoint(QWebEngineScript.DocumentReady)
        script.setWorldId(WebPage.SafeJsWorld)
        script.setRunsOnSubFrames(True)
        script.setSourceCode(Scripts.setCss(userCss))
        self._webProfile.scripts().insert(script)

    def checkDefaultWebBrowser(self):
        pass

    def checkOptimizeDatabase(self):
        pass

    # private:
    def _createJumpList(self):
        pass

    def _initPulseSupport(self):
        pass
        environ["PULSE_PROP_OVERRIDE_application.name"] = "App"
        environ["PULSE_PROP_OVERRIDE_application.icon_name"] = "app"
        environ["PULSE_PROP_OVERRIDE_media.icon_name"] = "app"