Merge branch 'drop-legacy'
This commit is contained in:
commit
43ff79be0b
@ -6,18 +6,12 @@ python: 3.6
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
env: DOCKER=debian-jessie
|
||||
services: docker
|
||||
- os: linux
|
||||
env: DOCKER=archlinux
|
||||
services: docker
|
||||
- os: linux
|
||||
env: DOCKER=archlinux-webengine QUTE_BDD_WEBENGINE=true
|
||||
services: docker
|
||||
- os: linux
|
||||
env: DOCKER=ubuntu-xenial
|
||||
services: docker
|
||||
- os: linux
|
||||
env: TESTENV=py36-pyqt571
|
||||
- os: linux
|
||||
|
@ -99,11 +99,8 @@ Requirements
|
||||
|
||||
The following software and libraries are required to run qutebrowser:
|
||||
|
||||
* http://www.python.org/[Python] 3.4 or newer (3.6 recommended) - note that
|
||||
support for Python 3.4
|
||||
https://github.com/qutebrowser/qutebrowser/issues/2742[will be dropped soon].
|
||||
* http://qt.io/[Qt] 5.2.0 or newer (5.9 recommended - note that support for Qt
|
||||
< 5.7.1 will be dropped soon) with the following modules:
|
||||
* http://www.python.org/[Python] 3.5 or newer (3.6 recommended)
|
||||
* http://qt.io/[Qt] 5.7.1 or newer with the following modules:
|
||||
- QtCore / qtbase
|
||||
- QtQuick (part of qtbase in some distributions)
|
||||
- QtSQL (part of qtbase in some distributions)
|
||||
@ -111,9 +108,8 @@ The following software and libraries are required to run qutebrowser:
|
||||
- QtWebKit (old or link:https://github.com/annulen/webkit/wiki[reloaded]/NG).
|
||||
Note that support for legacy QtWebKit (before 5.212) will be
|
||||
dropped soon.
|
||||
* http://www.riverbankcomputing.com/software/pyqt/intro[PyQt] 5.2.0 or newer
|
||||
(5.9 recommended) for Python 3. Note that support for PyQt < 5.7 will be
|
||||
dropped soon.
|
||||
* http://www.riverbankcomputing.com/software/pyqt/intro[PyQt] 5.7.0 or newer
|
||||
(5.9 recommended) for Python 3.
|
||||
* https://pypi.python.org/pypi/setuptools/[pkg_resources/setuptools]
|
||||
* http://fdik.org/pyPEG/[pyPEG2]
|
||||
* http://jinja.pocoo.org/[jinja2]
|
||||
|
@ -22,8 +22,8 @@ Breaking changes
|
||||
|
||||
- (TODO) Support for legacy QtWebKit (before 5.212 which is distributed
|
||||
independently from Qt) is dropped.
|
||||
- (TODO) Support for Python 3.4 is dropped.
|
||||
- (TODO) Support for Qt before 5.7 is dropped.
|
||||
- Support for Python 3.4 is dropped.
|
||||
- Support for Qt before 5.7.1 and PyQt before 5.7 is dropped.
|
||||
- (TODO) New dependency on ruamel.yaml; dropped PyYAML dependency.
|
||||
- (TODO) The QtWebEngine backend is now used by default if available.
|
||||
- New dependency on the QtSql module and Qt sqlite support.
|
||||
@ -36,6 +36,7 @@ Breaking changes
|
||||
work properly anymore.
|
||||
- Various documentation files got moved to the doc/ subfolder,
|
||||
`qutebrowser.desktop` got moved to misc/.
|
||||
- The `--harfbuzz` commandline argument got dropped
|
||||
|
||||
Major changes
|
||||
~~~~~~~~~~~~~
|
||||
@ -50,6 +51,9 @@ Added
|
||||
- New back/forward indicator in the statusbar
|
||||
- New `bindings.key_mappings` setting to map keys to other keys
|
||||
- New `qt_args` setting to pass additional arguments to Qt/Chromium
|
||||
- New `backend` setting to select the backend to use (auto/webengine/webkit).
|
||||
Together with the previous setting, this should make wrapper scripts
|
||||
unnecessary.
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
@ -180,7 +180,7 @@ In the _scripts/_ subfolder there's a `run_profile.py` which profiles the code
|
||||
and shows a graphical representation of what takes how much time.
|
||||
|
||||
It uses the built-in Python
|
||||
https://docs.python.org/3.4/library/profile.html[cProfile] module and can show
|
||||
https://docs.python.org/3.6/library/profile.html[cProfile] module and can show
|
||||
the output in four different ways:
|
||||
|
||||
* Raw profile file (`--profile-tool=none`)
|
||||
@ -535,11 +535,11 @@ ____
|
||||
Setting up a Windows Development Environment
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Install https://www.python.org/downloads/release/python-344/[Python 3.4]
|
||||
* Install https://sourceforge.net/projects/pyqt/files/PyQt5/PyQt-5.5.1/[PyQt 5.5]
|
||||
* Create a file at `C:\Windows\system32\python3.bat` with the following content:
|
||||
`@C:\Python34\python %*`
|
||||
This will make the Python 3.4 interpreter available as `python3`, which is used by various development scripts.
|
||||
* Install https://www.python.org/downloads/release/python-362/[Python 3.6].
|
||||
* Install PyQt via `pip install PyQt5`
|
||||
* Create a file at `C:\Windows\system32\python3.bat` with the following content (adjust the path as necessary):
|
||||
`@C:\Python36\python %*`
|
||||
This will make the Python 3.6 interpreter available as `python3`, which is used by various development scripts.
|
||||
* Install git from the https://git-scm.com/download/win[git-scm downloads page]
|
||||
Try not to enable `core.autocrlf`, since that will cause `flake8` to complain a lot. Use an editor that can deal with plain line feeds instead.
|
||||
* Clone your favourite qutebrowser repository.
|
||||
|
@ -205,26 +205,6 @@ Experiencing freezing on sites like duckduckgo and youtube.::
|
||||
See https://github.com/qutebrowser/qutebrowser/issues/357[Issue #357]
|
||||
for more details.
|
||||
|
||||
Experiencing segfaults (crashes) on Debian systems.::
|
||||
For Debian it's highly recommended to install the `gstreamer0.10-plugins-base` package.
|
||||
This is a workaround for a bug in Qt, it has been fixed upstream in Qt 5.4
|
||||
More details can be found
|
||||
https://bugs.webkit.org/show_bug.cgi?id=119951[here].
|
||||
|
||||
Segfaults on Facebook, Medium, Amazon, ...::
|
||||
If you are on a Debian or Ubuntu based system, you might experience some crashes
|
||||
visiting these sites. This is caused by various bugs in Qt which have been
|
||||
fixed in Qt 5.4. However Debian and Ubuntu are slow to adopt or upgrade
|
||||
some packages. On Debian Jessie, it's recommended to use the experimental
|
||||
repos as described in https://github.com/qutebrowser/qutebrowser/blob/master/doc/install.asciidoc#on-debian--ubuntu[the documentation].
|
||||
+
|
||||
Since Ubuntu Trusty (using Qt 5.2.1),
|
||||
https://bugreports.qt.io/browse/QTBUG-42417?jql=component%20%3D%20WebKit%20and%20resolution%20%3D%20Done%20and%20fixVersion%20in%20(5.3.0%2C%20%225.3.0%20Alpha%22%2C%20%225.3.0%20Beta1%22%2C%20%225.3.0%20RC1%22%2C%205.3.1%2C%205.3.2%2C%205.4.0%2C%20%225.4.0%20Alpha%22%2C%20%225.4.0%20Beta%22%2C%20%225.4.0%20RC%22)%20and%20priority%20in%20(%22P2%3A%20Important%22%2C%20%22P1%3A%20Critical%22%2C%20%22P0%3A%20Blocker%22)[over
|
||||
70 important bugs] have been fixed in QtWebKit. For Debian Jessie (using Qt 5.3.2)
|
||||
it's still
|
||||
https://bugreports.qt.io/browse/QTBUG-42417?jql=component%20%3D%20WebKit%20and%20resolution%20%3D%20Done%20and%20fixVersion%20in%20(5.4.0%2C%20%225.4.0%20Alpha%22%2C%20%225.4.0%20Beta%22%2C%20%225.4.0%20RC%22)%20and%20priority%20in%20(%22P2%3A%20Important%22%2C%20%22P1%3A%20Critical%22%2C%20%22P0%3A%20Blocker%22)[nearly
|
||||
20 important bugs].
|
||||
|
||||
When using QtWebEngine, qutebrowser reports "Render Process Crashed" and the console prints a traceback on Gentoo Linux or another Source-Based Distro::
|
||||
As stated in https://gcc.gnu.org/gcc-6/changes.html[GCC's Website] GCC 6 has introduced some optimizations that could break non-conforming codebases, like QtWebEngine. +
|
||||
As a workaround, you can disable the nullpointer check optimization by adding the -fno-delete-null-pointer-checks flag while compiling. +
|
||||
|
@ -12,6 +12,7 @@
|
||||
|<<aliases,aliases>>|Aliases for commands.
|
||||
|<<auto_save.interval,auto_save.interval>>|How often (in milliseconds) to auto-save config/cookies/etc.
|
||||
|<<auto_save.session,auto_save.session>>|Always restore open sites when qutebrowser is reopened.
|
||||
|<<backend,backend>>|The backend to use to display websites.
|
||||
|<<bindings.commands,bindings.commands>>|Keybindings mapping keys to commands in different modes.
|
||||
|<<bindings.default,bindings.default>>|Default keybindings. If you want to add bindings, modify `bindings.commands` instead.
|
||||
|<<bindings.key_mappings,bindings.key_mappings>>|This setting can be used to map keys to other keys.
|
||||
@ -285,6 +286,23 @@ Valid values:
|
||||
|
||||
Default: empty
|
||||
|
||||
[[backend]]
|
||||
=== backend
|
||||
The backend to use to display websites.
|
||||
qutebrowser supports two different web rendering engines / backends, QtWebKit and QtWebEngine.
|
||||
QtWebKit is based on WebKit (similar to Safari). It was discontinued by the Qt project with Qt 5.6, but picked up as a well maintained fork: https://github.com/annulen/webkit/wiki - qutebrowser only supports the fork.
|
||||
QtWebEngine is Qt's official successor to QtWebKit and based on the Chromium project. It's slightly more resource hungry that QtWebKit and has a couple of missing features in qutebrowser, but is generally the preferred choice.
|
||||
|
||||
Type: <<types,String>>
|
||||
|
||||
Valid values:
|
||||
|
||||
* +auto+: Automatically select either QtWebEngine or QtWebKit
|
||||
* +webkit+: Force QtWebKit
|
||||
* +webengine+: Force QtWebEngine
|
||||
|
||||
Default: +pass:[auto]+
|
||||
|
||||
[[bindings.commands]]
|
||||
=== bindings.commands
|
||||
Keybindings mapping keys to commands in different modes.
|
||||
|
@ -3,39 +3,50 @@ Installing qutebrowser
|
||||
|
||||
toc::[]
|
||||
|
||||
NOTE: qutebrowser recently had some bigger dependency changes for v1.0.0, which
|
||||
means those instructions might be out of date in some places.
|
||||
https://github.com/qutebrowser/qutebrowser/blob/master/doc/contributing.asciidoc[Please help]
|
||||
updating them if you notice something being broken!
|
||||
|
||||
On Debian / Ubuntu
|
||||
------------------
|
||||
|
||||
qutebrowser should run on these systems:
|
||||
How to install qutebrowser depends a lot on the version of Debian/Ubuntu you're
|
||||
running.
|
||||
|
||||
* Debian jessie or newer
|
||||
* Ubuntu Trusty (14.04 LTS) or newer
|
||||
* Any other distribution based on these (e.g. Linux Mint 17+)
|
||||
Debian Jessie / Ubuntu 14.04 LTS / Linux Mint < 18
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Unfortunately there is no Debian package in the official repos yet, but installing qutebrowser is
|
||||
still relatively easy!
|
||||
Those distributions only have Python 3.4 and a too old Qt version available,
|
||||
while qutebrowser requires Python 3.5 and Qt 5.7.1 or newer.
|
||||
|
||||
You can use packages that are built for every release or build it yourself from git.
|
||||
It should be possible to install Python 3.5 e.g. from the
|
||||
https://launchpad.net/~deadsnakes/+archive/ubuntu/ppa[deadsnakes PPA] or via_ipca
|
||||
https://github.com/pyenv/pyenv[pyenv], but nobody tried that yet.
|
||||
|
||||
On Ubuntu 16.04 and 16.10 it's recommended to <<tox,install qutebrowser via tox>>
|
||||
instead in order to be able to use the new QtWebEngine backend. Newer versions
|
||||
have a QtWebEngine package in the repositories.
|
||||
If you get qutebrowser running on those distributions, please
|
||||
https://github.com/qutebrowser/qutebrowser/blob/master/doc/contributing.asciidoc[contribute]
|
||||
to update this documentation!
|
||||
|
||||
Using the packages
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
Ubuntu 16.04 LTS / Linux Mint 18
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Ubuntu 16.04 doesn't come with an up-to-date engine (a new enough QtWebKit, or
|
||||
QtWebEngine). However, it comes with Python 3.5, so you can
|
||||
<<tox,install qutebrowser via tox>>.
|
||||
|
||||
Debian Stretch / Ubuntu 17.04 and newer
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Those versions come with QtWebEngine in the repositories. This makes it possible
|
||||
to install qutebrowser via the Debian package.
|
||||
|
||||
Install the dependencies via apt-get:
|
||||
|
||||
----
|
||||
# apt-get install python3-lxml python-tox python3-pyqt5 python3-pyqt5.qtwebkit python3-pyqt5.qtquick python3-sip python3-jinja2 python3-pygments python3-yaml python3-pyqt5.qtsql libqt5sql5-sqlite
|
||||
# apt install python-tox python3-{lxml,pyqt5,sip,jinja2,pygments,yaml} python3-pyqt5.qt{webengine,quick,opengl,sql} libqt5sql5-sqlite
|
||||
----
|
||||
|
||||
On Debian Stretch or Ubuntu 17.04 or later, it's also recommended to use the
|
||||
newer QtWebEngine backend.
|
||||
|
||||
To do so, install `python3-pyqt5.qtwebengine` and `python3-pyqt5.qtopengl`, then
|
||||
start qutebrowser with `--backend webengine`.
|
||||
|
||||
Get the qutebrowser package from the
|
||||
https://github.com/qutebrowser/qutebrowser/releases[release page] and download
|
||||
the https://qutebrowser.org/python3-pypeg2_2.15.2-1_all.deb[PyPEG2 package].
|
||||
@ -47,35 +58,27 @@ Install the packages:
|
||||
# dpkg -i qutebrowser_*_all.deb
|
||||
----
|
||||
|
||||
Build it from git
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Install the dependencies via apt-get:
|
||||
Some additional hints:
|
||||
|
||||
- Alternatively, you can <<tox,install qutebrowser via tox>> to get a newer
|
||||
QtWebEngine version.
|
||||
- If running from git, run the following to generate the documentation for the
|
||||
`:help` command:
|
||||
+
|
||||
----
|
||||
# apt-get install python3-pyqt5 python3-pyqt5.qtwebkit python3-pyqt5.qtquick python-tox python3-sip python3-dev python3-pyqt5.qtsql libqt5sql5-sqlite
|
||||
----
|
||||
|
||||
On Debian Stretch or Ubuntu 17.04 or later, it's also recommended to install
|
||||
`python3-pyqt5.qtwebengine` and start qutebrowser with `--backend webengine` in
|
||||
order to use the new backend.
|
||||
|
||||
To generate the documentation for the `:help` command, when using the git
|
||||
repository (rather than a release):
|
||||
|
||||
----
|
||||
# apt-get install asciidoc source-highlight
|
||||
# apt-get install --no-install-recommends asciidoc source-highlight
|
||||
$ python3 scripts/asciidoc2html.py
|
||||
----
|
||||
|
||||
If video or sound don't seem to work, try installing the gstreamer plugins:
|
||||
|
||||
- If you prefer using QtWebKit, there's an up-to-date version available in
|
||||
Debian experimental, or from http://repo.paretje.be/unstable/[this repository]
|
||||
for Debian Stretch.
|
||||
- If video or sound don't work with QtWebKit, try installing the gstreamer plugins:
|
||||
+
|
||||
----
|
||||
# apt-get install gstreamer1.0-plugins-{bad,base,good,ugly}
|
||||
----
|
||||
|
||||
Then <<tox,install qutebrowser via tox>>.
|
||||
|
||||
On Fedora
|
||||
---------
|
||||
|
||||
@ -116,7 +119,7 @@ $ rm -r qutebrowser-git
|
||||
|
||||
or you could use an AUR helper, e.g. `yaourt -S qutebrowser-git`.
|
||||
|
||||
If video or sound don't seem to work, try installing the gstreamer plugins:
|
||||
If video or sound don't work with QtWebKit, try installing the gstreamer plugins:
|
||||
|
||||
----
|
||||
# pacman -S gst-plugins-{base,good,bad,ugly} gst-libav
|
||||
@ -125,6 +128,8 @@ If video or sound don't seem to work, try installing the gstreamer plugins:
|
||||
On Gentoo
|
||||
---------
|
||||
|
||||
WARNING: The Gentoo packages (even the live version) are lagging behind a lot, which means those instructions probably won't work anymore. Until things are looking better, it's recommended to <<tox,install qutebrowser via tox>>.
|
||||
|
||||
A version of qutebrowser is available in the main repository and can be installed with:
|
||||
|
||||
----
|
||||
@ -161,15 +166,11 @@ To update to the last Live version, remember to do
|
||||
|
||||
To include qutebrowser among the updates.
|
||||
|
||||
Make sure you have `python3_4` in your `PYTHON_TARGETS`
|
||||
(`/etc/portage/make.conf`) and rebuild your system (`emerge -uDNav @world`) if
|
||||
necessary.
|
||||
You'll also need to install `dev-qt/qtwebengine` or a newer QtWebKit using
|
||||
https://gist.github.com/annulen/309569fb61e5d64a703c055c1e726f71[this ebuild].
|
||||
|
||||
It's also recommended to install QtWebKit-NG via
|
||||
https://gist.github.com/annulen/309569fb61e5d64a703c055c1e726f71[this ebuild],
|
||||
or install Qt >= 5.7.1 with QtWebEngine in order to use an up-to-date backend.
|
||||
|
||||
If video or sound don't seem to work, try installing the gstreamer plugins:
|
||||
If video or sound don't work with QtWebKit, try installing the gstreamer
|
||||
plugins:
|
||||
|
||||
----
|
||||
# emerge -av gst-plugins-{base,good,bad,ugly,libav}
|
||||
@ -214,6 +215,8 @@ On openSUSE
|
||||
|
||||
There are prebuilt RPMs available at https://software.opensuse.org/download.html?project=network&package=qutebrowser[OBS].
|
||||
|
||||
To use the QtWebEngine backend, install `libqt5-qtwebengine`.
|
||||
|
||||
On OpenBSD
|
||||
----------
|
||||
|
||||
|
@ -84,9 +84,6 @@ show it.
|
||||
*--force-color*::
|
||||
Force colored logging
|
||||
|
||||
*--harfbuzz* '{old,new,system,auto}'::
|
||||
HarfBuzz engine version to use. Default: auto.
|
||||
|
||||
*--relaxed-config*::
|
||||
Silently remove unknown config options.
|
||||
|
||||
|
@ -19,15 +19,12 @@ markers =
|
||||
qtwebengine_todo: Features still missing with QtWebEngine
|
||||
qtwebengine_skip: Tests not applicable with QtWebEngine
|
||||
qtwebkit_skip: Tests not applicable with QtWebKit
|
||||
qtwebkit_ng_xfail: Tests failing with QtWebKit-NG
|
||||
qtwebkit_ng_skip: Tests skipped with QtWebKit-NG
|
||||
qtwebengine_flaky: Tests which are flaky (and currently skipped) with QtWebEngine
|
||||
qtwebengine_mac_xfail: Tests which fail on macOS with QtWebEngine
|
||||
js_prompt: Tests needing to display a javascript prompt
|
||||
this: Used to mark tests during development
|
||||
no_invalid_lines: Don't fail on unparseable lines in end2end tests
|
||||
issue2478: Tests which are broken on Windows with QtWebEngine, https://github.com/qutebrowser/qutebrowser/issues/2478
|
||||
qt55: Tests only running on Qt 5.5 or later
|
||||
qt_log_level_fail = WARNING
|
||||
qt_log_ignore =
|
||||
^SpellCheck: .*
|
||||
|
@ -52,11 +52,14 @@ from qutebrowser.browser.webkit.network import networkmanager
|
||||
from qutebrowser.keyinput import macros
|
||||
from qutebrowser.mainwindow import mainwindow, prompt
|
||||
from qutebrowser.misc import (readline, ipc, savemanager, sessions,
|
||||
crashsignal, earlyinit, objects, sql, cmdhistory)
|
||||
from qutebrowser.misc import utilcmds # pylint: disable=unused-import
|
||||
from qutebrowser.utils import (log, version, message, utils, qtutils, urlutils,
|
||||
objreg, usertypes, standarddir, error)
|
||||
# We import utilcmds to run the cmdutils.register decorators.
|
||||
crashsignal, earlyinit, sql, cmdhistory)
|
||||
from qutebrowser.utils import (log, version, message, utils, urlutils, objreg,
|
||||
usertypes, standarddir, error)
|
||||
# pylint: disable=unused-import
|
||||
# We import those to run the cmdutils.register decorators.
|
||||
from qutebrowser.mainwindow.statusbar import command
|
||||
from qutebrowser.misc import utilcmds
|
||||
# pylint: enable=unused-import
|
||||
|
||||
|
||||
qApp = None
|
||||
@ -74,7 +77,7 @@ def run(args):
|
||||
standarddir.init(args)
|
||||
|
||||
log.init.debug("Initializing config...")
|
||||
config.early_init()
|
||||
config.early_init(args)
|
||||
|
||||
global qApp
|
||||
qApp = Application(args)
|
||||
@ -329,17 +332,6 @@ def _open_special_pages(args):
|
||||
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||
window='last-focused')
|
||||
|
||||
# Legacy QtWebKit warning
|
||||
|
||||
needs_warning = (objects.backend == usertypes.Backend.QtWebKit and
|
||||
not qtutils.is_qtwebkit_ng())
|
||||
warning_shown = general_sect.get('backend-warning-shown') == '1'
|
||||
|
||||
if not warning_shown and needs_warning:
|
||||
tabbed_browser.tabopen(QUrl('qute://backend-warning'),
|
||||
background=False)
|
||||
general_sect['backend-warning-shown'] = '1'
|
||||
|
||||
# Quickstart page
|
||||
|
||||
quickstart_done = general_sect.get('quickstart-done') == '1'
|
||||
|
@ -24,6 +24,7 @@ import sys
|
||||
import os.path
|
||||
import shlex
|
||||
import functools
|
||||
import typing
|
||||
|
||||
from PyQt5.QtWidgets import QApplication, QTabBar, QDialog
|
||||
from PyQt5.QtCore import Qt, QUrl, QEvent, QUrlQuery
|
||||
@ -39,10 +40,11 @@ from qutebrowser.browser import (urlmarks, browsertab, inspector, navigate,
|
||||
webelem, downloads)
|
||||
from qutebrowser.keyinput import modeman
|
||||
from qutebrowser.utils import (message, usertypes, log, qtutils, urlutils,
|
||||
objreg, utils, typing, debug)
|
||||
objreg, utils, debug)
|
||||
from qutebrowser.utils.usertypes import KeyMode
|
||||
from qutebrowser.misc import editor, guiprocess
|
||||
from qutebrowser.completion.models import urlmodel, miscmodels
|
||||
from qutebrowser.mainwindow import mainwindow
|
||||
|
||||
|
||||
class CommandDispatcher:
|
||||
@ -70,7 +72,6 @@ class CommandDispatcher:
|
||||
|
||||
def _new_tabbed_browser(self, private):
|
||||
"""Get a tabbed-browser from a new window."""
|
||||
from qutebrowser.mainwindow import mainwindow
|
||||
new_window = mainwindow.MainWindow(private=private)
|
||||
new_window.show()
|
||||
return new_window.tabbed_browser
|
||||
|
@ -24,6 +24,7 @@ import posixpath
|
||||
from qutebrowser.browser import webelem
|
||||
from qutebrowser.config import config
|
||||
from qutebrowser.utils import objreg, urlutils, log, message, qtutils
|
||||
from qutebrowser.mainwindow import mainwindow
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
@ -134,7 +135,6 @@ def prevnext(*, browsertab, win_id, baseurl, prev=False,
|
||||
window=win_id)
|
||||
|
||||
if window:
|
||||
from qutebrowser.mainwindow import mainwindow
|
||||
new_window = mainwindow.MainWindow(
|
||||
private=cur_tabbed_browser.private)
|
||||
new_window.show()
|
||||
|
@ -28,7 +28,6 @@ import json
|
||||
import os
|
||||
import time
|
||||
import urllib.parse
|
||||
import datetime
|
||||
import textwrap
|
||||
import pkg_resources
|
||||
|
||||
@ -37,7 +36,7 @@ from PyQt5.QtCore import QUrlQuery, QUrl
|
||||
import qutebrowser
|
||||
from qutebrowser.config import config, configdata, configexc, configdiff
|
||||
from qutebrowser.utils import (version, utils, jinja, log, message, docutils,
|
||||
objreg, usertypes, qtutils)
|
||||
objreg)
|
||||
from qutebrowser.misc import objects
|
||||
|
||||
|
||||
@ -224,50 +223,13 @@ def qute_history(url):
|
||||
|
||||
return 'text/html', json.dumps(history_data(start_time, offset))
|
||||
else:
|
||||
if (
|
||||
config.val.content.javascript.enabled and
|
||||
(objects.backend == usertypes.Backend.QtWebEngine or
|
||||
qtutils.is_qtwebkit_ng())
|
||||
):
|
||||
if not config.val.content.javascript.enabled:
|
||||
return 'text/plain', b'JavaScript is required for qute://history'
|
||||
return 'text/html', jinja.render(
|
||||
'history.html',
|
||||
title='History',
|
||||
gap_interval=config.val.history_gap_interval
|
||||
)
|
||||
else:
|
||||
# Get current date from query parameter, if not given choose today.
|
||||
curr_date = datetime.date.today()
|
||||
try:
|
||||
query_date = QUrlQuery(url).queryItemValue("date")
|
||||
if query_date:
|
||||
curr_date = datetime.datetime.strptime(query_date,
|
||||
"%Y-%m-%d").date()
|
||||
except ValueError:
|
||||
log.misc.debug("Invalid date passed to qute:history: " +
|
||||
query_date)
|
||||
|
||||
one_day = datetime.timedelta(days=1)
|
||||
next_date = curr_date + one_day
|
||||
prev_date = curr_date - one_day
|
||||
|
||||
# start_time is the last second of curr_date
|
||||
start_time = time.mktime(next_date.timetuple()) - 1
|
||||
history = [
|
||||
(i["url"], i["title"],
|
||||
datetime.datetime.fromtimestamp(i["time"]),
|
||||
QUrl(i["url"]).host())
|
||||
for i in history_data(start_time)
|
||||
]
|
||||
|
||||
return 'text/html', jinja.render(
|
||||
'history_nojs.html',
|
||||
title='History',
|
||||
history=history,
|
||||
curr_date=curr_date,
|
||||
next_date=next_date,
|
||||
prev_date=prev_date,
|
||||
today=datetime.date.today(),
|
||||
)
|
||||
|
||||
|
||||
@add_handler('javascript')
|
||||
|
@ -23,6 +23,7 @@ import html
|
||||
|
||||
from qutebrowser.config import config
|
||||
from qutebrowser.utils import usertypes, message, log, objreg, jinja
|
||||
from qutebrowser.mainwindow import mainwindow
|
||||
|
||||
|
||||
class CallSuper(Exception):
|
||||
@ -234,7 +235,6 @@ def get_tab(win_id, target):
|
||||
elif target == usertypes.ClickTarget.window:
|
||||
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||
window=win_id)
|
||||
from qutebrowser.mainwindow import mainwindow
|
||||
window = mainwindow.MainWindow(private=tabbed_browser.private)
|
||||
window.show()
|
||||
win_id = window.win_id
|
||||
|
@ -31,6 +31,7 @@ from PyQt5.QtGui import QMouseEvent
|
||||
|
||||
from qutebrowser.config import config
|
||||
from qutebrowser.keyinput import modeman
|
||||
from qutebrowser.mainwindow import mainwindow
|
||||
from qutebrowser.utils import log, usertypes, utils, qtutils, objreg
|
||||
|
||||
|
||||
@ -372,7 +373,6 @@ class AbstractWebElement(collections.abc.MutableMapping):
|
||||
background = click_target == usertypes.ClickTarget.tab_bg
|
||||
tabbed_browser.tabopen(url, background=background)
|
||||
elif click_target == usertypes.ClickTarget.window:
|
||||
from qutebrowser.mainwindow import mainwindow
|
||||
window = mainwindow.MainWindow(private=tabbed_browser.private)
|
||||
window.show()
|
||||
window.tabbed_browser.tabopen(url)
|
||||
|
@ -49,7 +49,6 @@ class DiskCache(QNetworkDiskCache):
|
||||
if size is None:
|
||||
size = 1024 * 1024 * 50 # default from QNetworkDiskCachePrivate
|
||||
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-59909
|
||||
if (qtutils.version_check('5.7.1') and
|
||||
not qtutils.version_check('5.9')): # pragma: no cover
|
||||
if not qtutils.version_check('5.9'): # pragma: no cover
|
||||
size = 0
|
||||
self.setMaximumCacheSize(size)
|
||||
|
@ -38,12 +38,7 @@ class CertificateErrorWrapper(usertypes.AbstractCertificateErrorWrapper):
|
||||
string=str(self))
|
||||
|
||||
def __hash__(self):
|
||||
try:
|
||||
# Qt >= 5.4
|
||||
return hash(self._error)
|
||||
except TypeError:
|
||||
return hash((self._error.certificate().toDer(),
|
||||
self._error.error()))
|
||||
|
||||
def __eq__(self, other):
|
||||
return self._error == other._error # pylint: disable=protected-access
|
||||
|
@ -24,13 +24,12 @@ import collections
|
||||
import netrc
|
||||
import html
|
||||
|
||||
from PyQt5.QtCore import (pyqtSlot, pyqtSignal, PYQT_VERSION, QCoreApplication,
|
||||
QUrl, QByteArray)
|
||||
from PyQt5.QtCore import (pyqtSlot, pyqtSignal, QCoreApplication, QUrl,
|
||||
QByteArray)
|
||||
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply, QSslSocket
|
||||
|
||||
from qutebrowser.config import config
|
||||
from qutebrowser.utils import (message, log, usertypes, utils, objreg, qtutils,
|
||||
urlutils)
|
||||
from qutebrowser.utils import message, log, usertypes, utils, objreg, urlutils
|
||||
from qutebrowser.browser import shared
|
||||
from qutebrowser.browser.webkit import certificateerror
|
||||
from qutebrowser.browser.webkit.network import (webkitqutescheme, networkreply,
|
||||
@ -88,15 +87,9 @@ def _is_secure_cipher(cipher):
|
||||
|
||||
def init():
|
||||
"""Disable insecure SSL ciphers on old Qt versions."""
|
||||
if qtutils.version_check('5.3.0'):
|
||||
default_ciphers = QSslSocket.defaultCiphers()
|
||||
log.init.debug("Default Qt ciphers: {}".format(
|
||||
', '.join(c.name() for c in default_ciphers)))
|
||||
else:
|
||||
# https://codereview.qt-project.org/#/c/75943/
|
||||
default_ciphers = QSslSocket.supportedCiphers()
|
||||
log.init.debug("Supported Qt ciphers: {}".format(
|
||||
', '.join(c.name() for c in default_ciphers)))
|
||||
|
||||
good_ciphers = []
|
||||
bad_ciphers = []
|
||||
@ -409,24 +402,11 @@ class NetworkManager(QNetworkAccessManager):
|
||||
tab = objreg.get('tab', scope='tab', window=self._win_id,
|
||||
tab=self._tab_id)
|
||||
current_url = tab.url()
|
||||
except (KeyError, RuntimeError, TypeError):
|
||||
except (KeyError, RuntimeError):
|
||||
# https://github.com/qutebrowser/qutebrowser/issues/889
|
||||
# Catching RuntimeError and TypeError because we could be in
|
||||
# the middle of the webpage shutdown here.
|
||||
# Catching RuntimeError because we could be in the middle of
|
||||
# the webpage shutdown here.
|
||||
current_url = QUrl()
|
||||
|
||||
self.set_referer(req, current_url)
|
||||
|
||||
if PYQT_VERSION < 0x050301:
|
||||
# WORKAROUND (remove this when we bump the requirements to 5.3.1)
|
||||
#
|
||||
# If we don't disable our message handler, we get a freeze if a
|
||||
# warning is printed due to a PyQt bug, e.g. when clicking a
|
||||
# currency on http://ch.mouser.com/localsites/
|
||||
#
|
||||
# See http://www.riverbankcomputing.com/pipermail/pyqt/2014-June/034420.html
|
||||
with log.disable_qt_msghandler():
|
||||
reply = super().createRequest(op, req, outgoing_data)
|
||||
else:
|
||||
reply = super().createRequest(op, req, outgoing_data)
|
||||
return reply
|
||||
return super().createRequest(op, req, outgoing_data)
|
||||
|
@ -25,13 +25,7 @@ from PyQt5.QtCore import QByteArray, QDataStream, QIODevice, QUrl
|
||||
from qutebrowser.utils import qtutils
|
||||
|
||||
|
||||
def _encode_url(url):
|
||||
"""Encode a QUrl suitable to pass to QWebHistory."""
|
||||
data = bytes(QUrl.toPercentEncoding(url.toString(), b':/#?&+=@%*'))
|
||||
return data.decode('ascii')
|
||||
|
||||
|
||||
def _serialize_ng(items, current_idx, stream):
|
||||
def _serialize_items(items, current_idx, stream):
|
||||
# {'currentItemIndex': 0,
|
||||
# 'history': [{'children': [],
|
||||
# 'documentSequenceNumber': 1485030525573123,
|
||||
@ -47,13 +41,13 @@ def _serialize_ng(items, current_idx, stream):
|
||||
# 'urlString': 'about:blank'}]}
|
||||
data = {'currentItemIndex': current_idx, 'history': []}
|
||||
for item in items:
|
||||
data['history'].append(_serialize_item_ng(item))
|
||||
data['history'].append(_serialize_item(item))
|
||||
|
||||
stream.writeInt(3) # history stream version
|
||||
stream.writeQVariantMap(data)
|
||||
|
||||
|
||||
def _serialize_item_ng(item):
|
||||
def _serialize_item(item):
|
||||
data = {
|
||||
'originalURLString': item.original_url.toString(QUrl.FullyEncoded),
|
||||
'scrollPosition': {'x': 0, 'y': 0},
|
||||
@ -68,82 +62,6 @@ def _serialize_item_ng(item):
|
||||
return data
|
||||
|
||||
|
||||
def _serialize_old(items, current_idx, stream):
|
||||
### Source/WebKit/qt/Api/qwebhistory.cpp operator<<
|
||||
stream.writeInt(2) # history stream version
|
||||
stream.writeInt(len(items))
|
||||
stream.writeInt(current_idx)
|
||||
|
||||
for i, item in enumerate(items):
|
||||
_serialize_item_old(i, item, stream)
|
||||
|
||||
|
||||
def _serialize_item_old(i, item, stream):
|
||||
"""Serialize a single WebHistoryItem into a QDataStream.
|
||||
|
||||
Args:
|
||||
i: The index of the current item.
|
||||
item: The WebHistoryItem to write.
|
||||
stream: The QDataStream to write to.
|
||||
"""
|
||||
### Source/WebCore/history/qt/HistoryItemQt.cpp restoreState
|
||||
## urlString
|
||||
stream.writeQString(_encode_url(item.url))
|
||||
## title
|
||||
stream.writeQString(item.title)
|
||||
## originalURLString
|
||||
stream.writeQString(_encode_url(item.original_url))
|
||||
|
||||
### Source/WebCore/history/HistoryItem.cpp decodeBackForwardTree
|
||||
## backForwardTreeEncodingVersion
|
||||
stream.writeUInt32(2)
|
||||
## size (recursion stack)
|
||||
stream.writeUInt64(0)
|
||||
## node->m_documentSequenceNumber
|
||||
# If two HistoryItems have the same document sequence number, then they
|
||||
# refer to the same instance of a document. Traversing history from one
|
||||
# such HistoryItem to another preserves the document.
|
||||
stream.writeInt64(i + 1)
|
||||
## size (node->m_documentState)
|
||||
stream.writeUInt64(0)
|
||||
## node->m_formContentType
|
||||
# info used to repost form data
|
||||
stream.writeQString(None)
|
||||
## hasFormData
|
||||
stream.writeBool(False)
|
||||
## node->m_itemSequenceNumber
|
||||
# If two HistoryItems have the same item sequence number, then they are
|
||||
# clones of one another. Traversing history from one such HistoryItem to
|
||||
# another is a no-op. HistoryItem clones are created for parent and
|
||||
# sibling frames when only a subframe navigates.
|
||||
stream.writeInt64(i + 1)
|
||||
## node->m_referrer
|
||||
stream.writeQString(None)
|
||||
## node->m_scrollPoint (x)
|
||||
try:
|
||||
stream.writeInt32(item.user_data['scroll-pos'].x())
|
||||
except (KeyError, TypeError):
|
||||
stream.writeInt32(0)
|
||||
## node->m_scrollPoint (y)
|
||||
try:
|
||||
stream.writeInt32(item.user_data['scroll-pos'].y())
|
||||
except (KeyError, TypeError):
|
||||
stream.writeInt32(0)
|
||||
## node->m_pageScaleFactor
|
||||
stream.writeFloat(1)
|
||||
## hasStateObject
|
||||
# Support for HTML5 History
|
||||
stream.writeBool(False)
|
||||
## node->m_target
|
||||
stream.writeQString(None)
|
||||
|
||||
### Source/WebCore/history/qt/HistoryItemQt.cpp restoreState
|
||||
## validUserData
|
||||
# We could restore the user data here, but we prefer to use the
|
||||
# QWebHistoryItem API for that.
|
||||
stream.writeBool(False)
|
||||
|
||||
|
||||
def serialize(items):
|
||||
"""Serialize a list of QWebHistoryItems to a data stream.
|
||||
|
||||
@ -180,10 +98,7 @@ def serialize(items):
|
||||
else:
|
||||
current_idx = 0
|
||||
|
||||
if qtutils.is_qtwebkit_ng():
|
||||
_serialize_ng(items, current_idx, stream)
|
||||
else:
|
||||
_serialize_old(items, current_idx, stream)
|
||||
_serialize_items(items, current_idx, stream)
|
||||
|
||||
user_data += [item.user_data for item in items]
|
||||
|
||||
|
@ -292,9 +292,6 @@ class WebKitElement(webelem.AbstractWebElement):
|
||||
elem = elem._parent() # pylint: disable=protected-access
|
||||
|
||||
def _move_text_cursor(self):
|
||||
if self is None:
|
||||
# old PyQt versions call the slot after the element is deleted.
|
||||
return
|
||||
if self.is_text_input() and self.is_editable():
|
||||
self._tab.caret.move_to_end_of_document()
|
||||
|
||||
|
@ -33,7 +33,7 @@ from PyQt5.QtGui import QFont
|
||||
from PyQt5.QtWebKit import QWebSettings
|
||||
|
||||
from qutebrowser.config import config, websettings
|
||||
from qutebrowser.utils import standarddir, urlutils, qtutils
|
||||
from qutebrowser.utils import standarddir, urlutils
|
||||
from qutebrowser.browser import shared
|
||||
|
||||
|
||||
@ -131,13 +131,6 @@ def init(_args):
|
||||
QWebSettings.setOfflineStoragePath(
|
||||
os.path.join(data_path, 'offline-storage'))
|
||||
|
||||
if (config.val.content.private_browsing and
|
||||
not qtutils.version_check('5.4.2')):
|
||||
# WORKAROUND for https://codereview.qt-project.org/#/c/108936/
|
||||
# Won't work when private browsing is not enabled globally, but that's
|
||||
# the best we can do...
|
||||
QWebSettings.setIconDatabasePath('')
|
||||
|
||||
websettings.init_mappings(MAPPINGS)
|
||||
_set_user_stylesheet()
|
||||
config.instance.changed.connect(_update_settings)
|
||||
|
@ -55,20 +55,14 @@ class WebKitPrinting(browsertab.AbstractPrinting):
|
||||
|
||||
"""QtWebKit implementations related to printing."""
|
||||
|
||||
def _do_check(self):
|
||||
if not qtutils.check_print_compat():
|
||||
# WORKAROUND (remove this when we bump the requirements to 5.3.0)
|
||||
raise browsertab.WebTabError(
|
||||
"Printing on Qt < 5.3.0 on Windows is broken, please upgrade!")
|
||||
|
||||
def check_pdf_support(self):
|
||||
self._do_check()
|
||||
pass
|
||||
|
||||
def check_printer_support(self):
|
||||
self._do_check()
|
||||
pass
|
||||
|
||||
def check_preview_support(self):
|
||||
self._do_check()
|
||||
pass
|
||||
|
||||
def to_pdf(self, filename):
|
||||
printer = QPrinter()
|
||||
|
@ -22,7 +22,7 @@
|
||||
import html
|
||||
import functools
|
||||
|
||||
from PyQt5.QtCore import pyqtSlot, pyqtSignal, PYQT_VERSION, Qt, QUrl, QPoint
|
||||
from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QUrl, QPoint
|
||||
from PyQt5.QtGui import QDesktopServices
|
||||
from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest
|
||||
from PyQt5.QtWidgets import QFileDialog
|
||||
@ -33,8 +33,8 @@ from qutebrowser.config import config
|
||||
from qutebrowser.browser import pdfjs, shared
|
||||
from qutebrowser.browser.webkit import http
|
||||
from qutebrowser.browser.webkit.network import networkmanager
|
||||
from qutebrowser.utils import (message, usertypes, log, jinja, qtutils, utils,
|
||||
objreg, debug, urlutils)
|
||||
from qutebrowser.utils import (message, usertypes, log, jinja, objreg, debug,
|
||||
urlutils)
|
||||
|
||||
|
||||
class BrowserPage(QWebPage):
|
||||
@ -87,12 +87,6 @@ class BrowserPage(QWebPage):
|
||||
self.restoreFrameStateRequested.connect(
|
||||
self.on_restore_frame_state_requested)
|
||||
|
||||
if PYQT_VERSION > 0x050300:
|
||||
# WORKAROUND (remove this when we bump the requirements to 5.3.1)
|
||||
# We can't override javaScriptPrompt with older PyQt-versions because
|
||||
# of a bug in PyQt.
|
||||
# See http://www.riverbankcomputing.com/pipermail/pyqt/2014-June/034385.html
|
||||
|
||||
def javaScriptPrompt(self, frame, js_msg, default):
|
||||
"""Override javaScriptPrompt to use qutebrowser prompts."""
|
||||
if self._is_shutting_down:
|
||||
@ -225,10 +219,6 @@ class BrowserPage(QWebPage):
|
||||
|
||||
def on_print_requested(self, frame):
|
||||
"""Handle printing when requested via javascript."""
|
||||
if not qtutils.check_print_compat():
|
||||
message.error("Printing on Qt < 5.3.0 on Windows is broken, "
|
||||
"please upgrade!")
|
||||
return
|
||||
printdiag = QPrintDialog()
|
||||
printdiag.setAttribute(Qt.WA_DeleteOnClose)
|
||||
printdiag.open(lambda: frame.print(printdiag.printer()))
|
||||
@ -350,16 +340,8 @@ class BrowserPage(QWebPage):
|
||||
frame: The QWebFrame which gets saved.
|
||||
item: The QWebHistoryItem to be saved.
|
||||
"""
|
||||
try:
|
||||
if frame != self.mainFrame():
|
||||
return
|
||||
except RuntimeError:
|
||||
# With Qt 5.2.1 (Ubuntu Trusty) we get this when closing a tab:
|
||||
# RuntimeError: wrapped C/C++ object of type BrowserPage has
|
||||
# been deleted
|
||||
# Since the information here isn't that important for closing web
|
||||
# views anyways, we ignore this error.
|
||||
return
|
||||
data = {
|
||||
'zoom': frame.zoomFactor(),
|
||||
'scroll-pos': frame.scrollPosition(),
|
||||
@ -401,9 +383,6 @@ class BrowserPage(QWebPage):
|
||||
"""
|
||||
return ext in self._extension_handlers
|
||||
|
||||
# WORKAROUND for:
|
||||
# http://www.riverbankcomputing.com/pipermail/pyqt/2014-August/034722.html
|
||||
@utils.prevent_exceptions(False, PYQT_VERSION < 0x50302)
|
||||
def extension(self, ext, opt, out):
|
||||
"""Override QWebPage::extension to provide error pages.
|
||||
|
||||
|
@ -29,7 +29,7 @@ from PyQt5.QtWebKitWidgets import QWebView, QWebPage
|
||||
|
||||
from qutebrowser.config import config
|
||||
from qutebrowser.keyinput import modeman
|
||||
from qutebrowser.utils import log, usertypes, utils, qtutils, objreg, debug
|
||||
from qutebrowser.utils import log, usertypes, utils, objreg, debug
|
||||
from qutebrowser.browser.webkit import webpage
|
||||
|
||||
|
||||
@ -57,7 +57,7 @@ class WebView(QWebView):
|
||||
|
||||
def __init__(self, *, win_id, tab_id, tab, private, parent=None):
|
||||
super().__init__(parent)
|
||||
if sys.platform == 'darwin' and qtutils.version_check('5.4'):
|
||||
if sys.platform == 'darwin':
|
||||
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-42948
|
||||
# See https://github.com/qutebrowser/qutebrowser/issues/462
|
||||
self.setStyle(QStyleFactory.create('Fusion'))
|
||||
@ -74,13 +74,9 @@ class WebView(QWebView):
|
||||
page = webpage.BrowserPage(win_id=self.win_id, tab_id=self._tab_id,
|
||||
tabdata=tab.data, private=private,
|
||||
parent=self)
|
||||
|
||||
try:
|
||||
page.setVisibilityState(
|
||||
QWebPage.VisibilityStateVisible if self.isVisible()
|
||||
else QWebPage.VisibilityStateHidden)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
self.setPage(page)
|
||||
|
||||
@ -240,12 +236,8 @@ class WebView(QWebView):
|
||||
Return:
|
||||
The superclass event return value.
|
||||
"""
|
||||
try:
|
||||
self.page().setVisibilityState(QWebPage.VisibilityStateVisible)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
super().showEvent(e)
|
||||
self.page().setVisibilityState(QWebPage.VisibilityStateVisible)
|
||||
|
||||
def hideEvent(self, e):
|
||||
"""Extend hideEvent to set the page visibility state to hidden.
|
||||
@ -256,12 +248,8 @@ class WebView(QWebView):
|
||||
Return:
|
||||
The superclass event return value.
|
||||
"""
|
||||
try:
|
||||
self.page().setVisibilityState(QWebPage.VisibilityStateHidden)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
super().hideEvent(e)
|
||||
self.page().setVisibilityState(QWebPage.VisibilityStateHidden)
|
||||
|
||||
def mousePressEvent(self, e):
|
||||
"""Set the tabdata ClickTarget on a mousepress.
|
||||
|
@ -22,10 +22,11 @@
|
||||
import inspect
|
||||
import collections
|
||||
import traceback
|
||||
import typing
|
||||
|
||||
from qutebrowser.commands import cmdexc, argparser
|
||||
from qutebrowser.utils import (log, utils, message, docutils, objreg,
|
||||
usertypes, typing)
|
||||
usertypes)
|
||||
from qutebrowser.utils import debug as debug_utils
|
||||
from qutebrowser.misc import objects
|
||||
|
||||
@ -415,9 +416,6 @@ class Command:
|
||||
# We also can't use isinstance here because typing.Union doesn't
|
||||
# support that.
|
||||
# pylint: disable=no-member,useless-suppression
|
||||
try:
|
||||
types = list(typ.__union_params__)
|
||||
except AttributeError:
|
||||
types = list(typ.__args__)
|
||||
# pylint: enable=no-member,useless-suppression
|
||||
if param.default is not inspect.Parameter.empty:
|
||||
|
@ -28,9 +28,10 @@ from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QUrl
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
|
||||
from qutebrowser.config import configdata, configexc, configtypes, configfiles
|
||||
from qutebrowser.utils import utils, objreg, message, log, usertypes
|
||||
from qutebrowser.misc import objects, msgbox
|
||||
from qutebrowser.commands import cmdexc, cmdutils
|
||||
from qutebrowser.utils import (utils, objreg, message, log, usertypes, jinja,
|
||||
qtutils)
|
||||
from qutebrowser.misc import objects, msgbox, earlyinit
|
||||
from qutebrowser.commands import cmdexc, cmdutils, runners
|
||||
from qutebrowser.completion.models import configmodel
|
||||
|
||||
# An easy way to access the config from other code via config.val.foo
|
||||
@ -176,8 +177,6 @@ class KeyConfig:
|
||||
|
||||
def bind(self, key, command, *, mode, force=False, save_yaml=False):
|
||||
"""Add a new binding from key to command."""
|
||||
# Doing this here to work around a Python 3.4 circular import
|
||||
from qutebrowser.commands import runners
|
||||
key = self._prepare(key, mode)
|
||||
|
||||
parser = runners.CommandParser()
|
||||
@ -395,7 +394,7 @@ class Config(QObject):
|
||||
|
||||
def _set_value(self, opt, value):
|
||||
"""Set the given option to the given value."""
|
||||
if objects.backend is not None:
|
||||
if not isinstance(objects.backend, objects.NoBackend):
|
||||
if objects.backend not in opt.backends:
|
||||
raise configexc.BackendError(objects.backend)
|
||||
|
||||
@ -594,8 +593,6 @@ def set_register_stylesheet(obj, *, stylesheet=None, update=True):
|
||||
@functools.lru_cache()
|
||||
def _render_stylesheet(stylesheet):
|
||||
"""Render the given stylesheet jinja template."""
|
||||
# Imported here to avoid a Python 3.4 circular import
|
||||
from qutebrowser.utils import jinja
|
||||
with jinja.environment.no_autoescape():
|
||||
template = jinja.environment.from_string(stylesheet)
|
||||
return template.render(conf=val)
|
||||
@ -645,7 +642,7 @@ class StyleSheetObserver(QObject):
|
||||
instance.changed.connect(self._update_stylesheet)
|
||||
|
||||
|
||||
def early_init():
|
||||
def early_init(args):
|
||||
"""Initialize the part of the config which works without a QApplication."""
|
||||
configdata.init()
|
||||
|
||||
@ -690,6 +687,33 @@ def early_init():
|
||||
|
||||
configfiles.init()
|
||||
|
||||
objects.backend = get_backend(args)
|
||||
earlyinit.init_with_backend(objects.backend)
|
||||
|
||||
|
||||
def get_backend(args):
|
||||
"""Find out what backend to use based on available libraries."""
|
||||
try:
|
||||
import PyQt5.QtWebKit # pylint: disable=unused-variable
|
||||
except ImportError:
|
||||
webkit_available = False
|
||||
else:
|
||||
webkit_available = qtutils.is_new_qtwebkit()
|
||||
|
||||
str_to_backend = {
|
||||
'webkit': usertypes.Backend.QtWebKit,
|
||||
'webengine': usertypes.Backend.QtWebEngine,
|
||||
}
|
||||
|
||||
if args.backend is not None:
|
||||
return str_to_backend[args.backend]
|
||||
elif val.backend != 'auto':
|
||||
return str_to_backend[val.backend]
|
||||
elif webkit_available:
|
||||
return usertypes.Backend.QtWebKit
|
||||
else:
|
||||
return usertypes.Backend.QtWebEngine
|
||||
|
||||
|
||||
def late_init(save_manager):
|
||||
"""Initialize the rest of the config after the QApplication is created."""
|
||||
|
@ -101,6 +101,28 @@ qt_args:
|
||||
https://peter.sh/experiments/chromium-command-line-switches/ for a list)
|
||||
will work.
|
||||
|
||||
backend:
|
||||
type:
|
||||
name: String
|
||||
valid_values:
|
||||
- auto: Automatically select either QtWebEngine or QtWebKit
|
||||
- webkit: Force QtWebKit
|
||||
- webengine: Force QtWebEngine
|
||||
default: auto
|
||||
desc: >-
|
||||
The backend to use to display websites.
|
||||
|
||||
qutebrowser supports two different web rendering engines / backends,
|
||||
QtWebKit and QtWebEngine.
|
||||
|
||||
QtWebKit is based on WebKit (similar to Safari). It was discontinued by the
|
||||
Qt project with Qt 5.6, but picked up as a well maintained fork:
|
||||
https://github.com/annulen/webkit/wiki - qutebrowser only supports the fork.
|
||||
|
||||
QtWebEngine is Qt's official successor to QtWebKit and based on the Chromium
|
||||
project. It's slightly more resource hungry that QtWebKit and has a couple
|
||||
of missing features in qutebrowser, but is generally the preferred choice.
|
||||
|
||||
## auto_save
|
||||
|
||||
auto_save.interval:
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
"""Exceptions related to config parsing."""
|
||||
|
||||
from qutebrowser.utils import utils
|
||||
from qutebrowser.utils import utils, jinja
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
@ -108,7 +108,6 @@ class ConfigFileErrors(Error):
|
||||
|
||||
def to_html(self):
|
||||
"""Get the error texts as a HTML snippet."""
|
||||
from qutebrowser.utils import jinja
|
||||
template = jinja.environment.from_string("""
|
||||
Errors occurred while reading {{ basename }}:
|
||||
|
||||
|
@ -30,7 +30,7 @@ import yaml
|
||||
from PyQt5.QtCore import QSettings
|
||||
|
||||
import qutebrowser
|
||||
from qutebrowser.config import configexc
|
||||
from qutebrowser.config import configexc, config
|
||||
from qutebrowser.utils import standarddir, utils, qtutils
|
||||
|
||||
|
||||
@ -51,8 +51,10 @@ class StateConfig(configparser.ConfigParser):
|
||||
self.add_section(sect)
|
||||
except configparser.DuplicateSectionError:
|
||||
pass
|
||||
# See commit a98060e020a4ba83b663813a4b9404edb47f28ad.
|
||||
self['general'].pop('fooled', None)
|
||||
|
||||
deleted_keys = ['fooled', 'backend-warning-shown']
|
||||
for key in deleted_keys:
|
||||
self['general'].pop(key, None)
|
||||
|
||||
def init_save_manager(self, save_manager):
|
||||
"""Make sure the config gets saved properly.
|
||||
@ -152,8 +154,8 @@ class ConfigAPI:
|
||||
errors: Errors which occurred while setting options.
|
||||
"""
|
||||
|
||||
def __init__(self, config, keyconfig):
|
||||
self._config = config
|
||||
def __init__(self, conf, keyconfig):
|
||||
self._config = conf
|
||||
self._keyconfig = keyconfig
|
||||
self.load_autoconfig = True
|
||||
self.errors = []
|
||||
@ -189,7 +191,6 @@ class ConfigAPI:
|
||||
|
||||
def read_config_py(filename=None):
|
||||
"""Read a config.py file."""
|
||||
from qutebrowser.config import config
|
||||
api = ConfigAPI(config.instance, config.key_instance)
|
||||
|
||||
if filename is None:
|
||||
|
@ -59,9 +59,9 @@ from PyQt5.QtCore import QUrl, Qt
|
||||
from PyQt5.QtGui import QColor, QFont
|
||||
from PyQt5.QtWidgets import QTabWidget, QTabBar
|
||||
|
||||
from qutebrowser.commands import cmdutils
|
||||
from qutebrowser.commands import cmdutils, runners, cmdexc
|
||||
from qutebrowser.config import configexc
|
||||
from qutebrowser.utils import standarddir, utils, qtutils
|
||||
from qutebrowser.utils import standarddir, utils, qtutils, urlutils
|
||||
|
||||
|
||||
SYSTEM_PROXY = object() # Return value for Proxy type
|
||||
@ -791,7 +791,6 @@ class Command(BaseType):
|
||||
if not Command.unvalidated:
|
||||
Command.unvalidated = True
|
||||
try:
|
||||
from qutebrowser.commands import runners, cmdexc
|
||||
parser = runners.CommandParser()
|
||||
try:
|
||||
parser.parse_all(value)
|
||||
@ -1287,7 +1286,6 @@ class Proxy(BaseType):
|
||||
('none', "Don't use any proxy"))
|
||||
|
||||
def to_py(self, value):
|
||||
from qutebrowser.utils import urlutils
|
||||
self._basic_py_validation(value, str)
|
||||
if not value:
|
||||
return None
|
||||
@ -1352,7 +1350,6 @@ class FuzzyUrl(BaseType):
|
||||
"""A URL which gets interpreted as search if needed."""
|
||||
|
||||
def to_py(self, value):
|
||||
from qutebrowser.utils import urlutils
|
||||
self._basic_py_validation(value, str)
|
||||
if not value:
|
||||
return None
|
||||
|
@ -1,100 +0,0 @@
|
||||
{% extends "styled.html" %}
|
||||
|
||||
{% block style %}
|
||||
{{super()}}
|
||||
.note {
|
||||
font-size: smaller;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
.mono {
|
||||
font-family: monospace;
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Legacy QtWebKit backend</h1>
|
||||
|
||||
<span class="note">Note this warning will only appear once. Use <span class="mono">:open
|
||||
qute://backend-warning</span> to show it again at a later time.</span>
|
||||
|
||||
<p>
|
||||
You're using qutebrowser with the legacy QtWebKit backend. It's still the
|
||||
default until a few remaining issues are sorted out. If you can, it's
|
||||
strongly suggested to switch earlier, as legacy QtWebKit has known security
|
||||
issues and also breaks things on various websites.
|
||||
</p>
|
||||
|
||||
<h2>Using QtWebEngine instead</h2>
|
||||
|
||||
<span class="note">This is usually the better choice if you aren't using Nouveau graphics, and
|
||||
don't need any features which are currently unavailable with QtWebEngine (like
|
||||
the <span class="mono">qute://settings</span> page or caret browsing).</span>
|
||||
|
||||
{% macro install_webengine(package) -%}
|
||||
You should be able to install <span class="mono">{{ package }}</span> and start qutebrowser with <span class="mono">--backend webengine</span> to use the new backend.
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro please_open_issue() -%}
|
||||
If you know more, please <a href="https://github.com/qutebrowser/qutebrowser/issues/new">open an issue</a>!
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro unknown_system() -%}
|
||||
There's no information available for your system. {{ please_open_issue() }}
|
||||
{%- endmacro %}
|
||||
|
||||
<p>
|
||||
{% if distribution.parsed == Distribution.ubuntu %}
|
||||
{% if distribution.version == none %}
|
||||
{{ unknown_system() }}
|
||||
{% elif distribution.version >= version('17.04') %}
|
||||
{{ install_webengine('python3-pyqt5.qtwebengine') }}
|
||||
{% elif distribution.version >= version('16.04') %}
|
||||
QtWebEngine is only available in Ubuntu's repositories since 17.04, but you can <a href="https://github.com/qutebrowser/qutebrowser/blob/master/doc/install.asciidoc#installing-qutebrowser-with-tox">install qutebrowser via tox</a> with <span class="mono">tox -e mkvenv-pypi</span> to use the new backend.
|
||||
{% else %}
|
||||
Unfortunately, no easy way is known to install QtWebEngine on Ubuntu < 16.04. {{ please_open_issue() }}
|
||||
{% endif %}
|
||||
{% elif distribution.parsed == Distribution.debian %}
|
||||
{% if distribution.version == none %}
|
||||
{{ unknown_system() }}
|
||||
{% elif distribution.version >= version('9') %}
|
||||
{{ install_webengine('python3-pyqt5.qtwebengine') }}
|
||||
{% else %}
|
||||
Unfortunately, no easy way is known to install QtWebEngine on Debian < 9. {{ please_open_issue() }}
|
||||
{% endif %}
|
||||
{% elif distribution.parsed in [Distribution.arch, Distribution.manjaro] %}
|
||||
{{ install_webengine('qt5-webengine') }}
|
||||
{% elif distribution.parsed == Distribution.void %}
|
||||
{{ install_webengine('python-PyQt5-webengine') }}
|
||||
{% elif distribution.parsed == Distribution.fedora %}
|
||||
{{ install_webengine('qt5-qtwebengine') }}
|
||||
{% elif distribution.parsed == Distribution.opensuse %}
|
||||
{{ install_webengine('libqt5-qtwebengine') }}
|
||||
{% elif distribution.parsed == Distribution.gentoo %}
|
||||
{{ install_webengine('dev-qt/qtwebengine') }}
|
||||
{% else %}
|
||||
{{ unknown_system() }}
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
<h2>Using QtWebKit-NG instead</h2>
|
||||
|
||||
<span class="note">This is a drop-in replacement for legacy QtWebKit.</span>
|
||||
|
||||
<p>
|
||||
{% if distribution.parsed == Distribution.debian and distribution.version != none and distribution.version >= version('9') %}
|
||||
There are unofficial QtWebKit-NG packages <a href="http://repo.paretje.be/unstable/">available</a>.
|
||||
{% elif distribution.parsed in [Distribution.ubuntu, Distribution.debian] %}
|
||||
No easy way is known to install QtWebKit-NG on your system.
|
||||
There are unofficial QtWebKit-NG packages <a href="http://repo.paretje.be/unstable/">available</a>, but they are intended for Debian Unstable.
|
||||
{{ please_open_issue() }}
|
||||
{% elif distribution.parsed in [Distribution.arch, Distribution.manjaro] %}
|
||||
With an updated <span class="mono">qt5-webkit</span> package, you should already get QtWebKit-NG.
|
||||
{% elif distribution.parsed == Distribution.gentoo %}
|
||||
There's an unofficial <a href="https://gist.github.com/annulen/309569fb61e5d64a703c055c1e726f71">ebuild</a> available.
|
||||
{% else %}
|
||||
{{ unknown_system() }}
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
{% endblock %}
|
@ -1,61 +0,0 @@
|
||||
{% extends "styled.html" %}
|
||||
|
||||
{% block style %}
|
||||
{{super()}}
|
||||
body {
|
||||
max-width: 1440px;
|
||||
}
|
||||
|
||||
td.title {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
td.time {
|
||||
color: #555;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
table {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.date {
|
||||
color: #555;
|
||||
font-size: 12pt;
|
||||
padding-bottom: 15px;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.pagination-link {
|
||||
color: #555;
|
||||
font-weight: bold;
|
||||
margn-bottom: 15px;
|
||||
text-decoration: none;
|
||||
}
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<h1>Browsing history</h1>
|
||||
|
||||
<table>
|
||||
<caption class="date">{{curr_date.strftime("%a, %d %B %Y")}}</caption>
|
||||
<tbody>
|
||||
{% for url, title, time, host in history %}
|
||||
<tr>
|
||||
<td class="title">
|
||||
<a href="{{url}}">{{title}}</a>
|
||||
<span class="hostname">{{host}}</span>
|
||||
</td>
|
||||
<td class="time">{{time.strftime("%X")}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<span class="pagination-link"><a href="qute://history/?date={{prev_date.strftime("%Y-%m-%d")}}" rel="prev">Previous</a></span>
|
||||
{% if today >= next_date %}
|
||||
<span class="pagination-link"><a href="qute://history/?date={{next_date.strftime("%Y-%m-%d")}}" rel="next">Next</a></span>
|
||||
{% endif %}
|
||||
{% endblock %}
|
@ -31,8 +31,7 @@ from qutebrowser.commands import runners, cmdutils
|
||||
from qutebrowser.config import config, configfiles
|
||||
from qutebrowser.utils import (message, log, usertypes, qtutils, objreg, utils,
|
||||
jinja, debug)
|
||||
from qutebrowser.mainwindow import tabbedbrowser, messageview, prompt
|
||||
from qutebrowser.mainwindow.statusbar import bar
|
||||
from qutebrowser.mainwindow import messageview, prompt
|
||||
from qutebrowser.completion import completionwidget, completer
|
||||
from qutebrowser.keyinput import modeman
|
||||
from qutebrowser.browser import (commands, downloadview, hints,
|
||||
@ -140,6 +139,11 @@ class MainWindow(QWidget):
|
||||
parent: The parent the window should get.
|
||||
"""
|
||||
super().__init__(parent)
|
||||
# Late import to avoid a circular dependency
|
||||
# - browsertab -> hints -> webelem -> mainwindow -> bar -> browsertab
|
||||
from qutebrowser.mainwindow import tabbedbrowser
|
||||
from qutebrowser.mainwindow.statusbar import bar
|
||||
|
||||
self.setAttribute(Qt.WA_DeleteOnClose)
|
||||
self._commandrunner = None
|
||||
self._overlays = []
|
||||
|
@ -61,10 +61,6 @@ class Progress(QProgressBar):
|
||||
|
||||
def on_tab_changed(self, tab):
|
||||
"""Set the correct value when the current tab changed."""
|
||||
if self is None: # pragma: no branch
|
||||
# This should never happen, but for some weird reason it does
|
||||
# sometimes.
|
||||
return # pragma: no cover
|
||||
self.setValue(tab.progress())
|
||||
if tab.load_status() == usertypes.LoadStatus.loading:
|
||||
self.show()
|
||||
|
@ -28,7 +28,7 @@ from PyQt5.QtGui import QIcon
|
||||
|
||||
from qutebrowser.config import config
|
||||
from qutebrowser.keyinput import modeman
|
||||
from qutebrowser.mainwindow import tabwidget
|
||||
from qutebrowser.mainwindow import tabwidget, mainwindow
|
||||
from qutebrowser.browser import signalfilter, browsertab
|
||||
from qutebrowser.utils import (log, usertypes, utils, qtutils, objreg,
|
||||
urlutils, message, jinja)
|
||||
@ -432,7 +432,6 @@ class TabbedBrowser(tabwidget.TabWidget):
|
||||
|
||||
if (config.val.tabs.tabs_are_windows and self.count() > 0 and
|
||||
not ignore_tabs_are_windows):
|
||||
from qutebrowser.mainwindow import mainwindow
|
||||
window = mainwindow.MainWindow(private=self.private)
|
||||
window.show()
|
||||
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||
|
@ -70,9 +70,6 @@ class TabWidget(QTabWidget):
|
||||
@config.change_filter('tabs')
|
||||
def _init_config(self):
|
||||
"""Initialize attributes based on the config."""
|
||||
if self is None: # pragma: no cover
|
||||
# WORKAROUND for PyQt 5.2
|
||||
return
|
||||
tabbar = self.tabBar()
|
||||
self.setMovable(True)
|
||||
self.setTabsClosable(False)
|
||||
@ -744,24 +741,17 @@ class TabBarStyle(QCommonStyle):
|
||||
log.misc.warning("Could not get layouts for tab!")
|
||||
return QRect()
|
||||
return layouts.text
|
||||
elif sr == QStyle.SE_TabWidgetTabBar:
|
||||
elif sr in [QStyle.SE_TabWidgetTabBar,
|
||||
QStyle.SE_TabBarScrollLeftButton]:
|
||||
# Handling SE_TabBarScrollLeftButton so the left scroll button is
|
||||
# aligned properly. Otherwise, empty space will be shown after the
|
||||
# last tab even though the button width is set to 0
|
||||
#
|
||||
# Need to use super() because we also use super() to render
|
||||
# element in drawControl(); otherwise, we may get bit by
|
||||
# style differences...
|
||||
rct = super().subElementRect(sr, opt, widget)
|
||||
return rct
|
||||
else:
|
||||
try:
|
||||
# We need this so the left scroll button is aligned properly.
|
||||
# Otherwise, empty space will be shown after the last tab even
|
||||
# though the button width is set to 0
|
||||
#
|
||||
# QStyle.SE_TabBarScrollLeftButton was added in Qt 5.7
|
||||
if sr == QStyle.SE_TabBarScrollLeftButton:
|
||||
return super().subElementRect(sr, opt, widget)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
else:
|
||||
return self._style.subElementRect(sr, opt, widget)
|
||||
|
||||
def _tab_layout(self, opt):
|
||||
|
@ -43,12 +43,12 @@ except ImportError: # pragma: no cover
|
||||
# to stderr.
|
||||
def check_python_version():
|
||||
"""Check if correct python version is run."""
|
||||
if sys.hexversion < 0x03040000:
|
||||
if sys.hexversion < 0x03050000:
|
||||
# We don't use .format() and print_function here just in case someone
|
||||
# still has < 2.6 installed.
|
||||
# pylint: disable=bad-builtin
|
||||
version_str = '.'.join(map(str, sys.version_info[:3]))
|
||||
text = ("At least Python 3.4 is required to run qutebrowser, but " +
|
||||
text = ("At least Python 3.5 is required to run qutebrowser, but " +
|
||||
version_str + " is installed!\n")
|
||||
if Tk and '--no-err-windows' not in sys.argv: # pragma: no cover
|
||||
root = Tk()
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
"""Things which need to be done really early (e.g. before importing Qt).
|
||||
|
||||
At this point we can be sure we have all python 3.4 features available.
|
||||
At this point we can be sure we have all python 3.5 features available.
|
||||
"""
|
||||
|
||||
try:
|
||||
@ -29,7 +29,6 @@ try:
|
||||
except ImportError:
|
||||
hunter = None
|
||||
|
||||
import os
|
||||
import sys
|
||||
import faulthandler
|
||||
import traceback
|
||||
@ -41,8 +40,6 @@ try:
|
||||
except ImportError:
|
||||
tkinter = None
|
||||
|
||||
import pkg_resources
|
||||
|
||||
# NOTE: No qutebrowser or PyQt import should be done here, as some early
|
||||
# initialization needs to take place before that!
|
||||
|
||||
@ -140,73 +137,6 @@ def init_faulthandler(fileobj=sys.__stderr__):
|
||||
faulthandler.register(signal.SIGUSR1)
|
||||
|
||||
|
||||
def _qt_version():
|
||||
"""Get the running Qt version.
|
||||
|
||||
Needs to be in a function so we can do a local import easily (to not import
|
||||
from QtCore too early) but can patch this out easily for tests.
|
||||
"""
|
||||
from PyQt5.QtCore import qVersion
|
||||
return pkg_resources.parse_version(qVersion())
|
||||
|
||||
|
||||
def fix_harfbuzz(args):
|
||||
"""Fix harfbuzz issues.
|
||||
|
||||
This switches to the most stable harfbuzz font rendering engine available
|
||||
on the platform instead of using the system wide one.
|
||||
|
||||
This fixes crashes on various sites.
|
||||
|
||||
- On Qt 5.2 (and probably earlier) the new engine probably has more
|
||||
crashes and is also experimental.
|
||||
|
||||
e.g. https://bugreports.qt.io/browse/QTBUG-36099
|
||||
|
||||
- On Qt 5.3.0 there's a bug that affects a lot of websites:
|
||||
https://bugreports.qt.io/browse/QTBUG-39278
|
||||
So the new engine will be more stable.
|
||||
|
||||
- On Qt 5.3.1 this bug is fixed and the old engine will be the more stable
|
||||
one again.
|
||||
|
||||
- On Qt 5.4 the new engine is the default and most bugs are taken care of.
|
||||
|
||||
IMPORTANT: This needs to be done before QWidgets is imported in any way!
|
||||
|
||||
WORKAROUND (remove this when we bump the requirements to 5.3.1)
|
||||
|
||||
Args:
|
||||
args: The argparse namespace.
|
||||
"""
|
||||
from qutebrowser.utils import log
|
||||
if 'PyQt5.QtWidgets' in sys.modules:
|
||||
msg = "Harfbuzz fix attempted but QtWidgets is already imported!"
|
||||
if getattr(sys, 'frozen', False):
|
||||
log.init.debug(msg)
|
||||
else:
|
||||
log.init.warning(msg)
|
||||
if sys.platform.startswith('linux') and args.harfbuzz == 'auto':
|
||||
if _qt_version() == pkg_resources.parse_version('5.3.0'):
|
||||
log.init.debug("Using new harfbuzz engine (auto)")
|
||||
os.environ['QT_HARFBUZZ'] = 'new'
|
||||
elif _qt_version() < pkg_resources.parse_version('5.4.0'):
|
||||
log.init.debug("Using old harfbuzz engine (auto)")
|
||||
os.environ['QT_HARFBUZZ'] = 'old'
|
||||
else:
|
||||
log.init.debug("Using system harfbuzz engine (auto)")
|
||||
elif args.harfbuzz in ['old', 'new']:
|
||||
# forced harfbuzz variant
|
||||
# FIXME looking at the Qt code, 'new' isn't a valid value, but leaving
|
||||
# it empty and using new yields different behavior...
|
||||
# (probably irrelevant when workaround gets removed)
|
||||
log.init.debug("Using {} harfbuzz engine (forced)".format(
|
||||
args.harfbuzz))
|
||||
os.environ['QT_HARFBUZZ'] = args.harfbuzz
|
||||
else:
|
||||
log.init.debug("Using system harfbuzz engine")
|
||||
|
||||
|
||||
def check_pyqt_core():
|
||||
"""Check if PyQt core is installed."""
|
||||
try:
|
||||
@ -233,26 +163,6 @@ def check_pyqt_core():
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def get_backend(args):
|
||||
"""Find out what backend to use based on available libraries.
|
||||
|
||||
Note this function returns the backend as a string so we don't have to
|
||||
import qutebrowser.utils.usertypes yet.
|
||||
"""
|
||||
try:
|
||||
import PyQt5.QtWebKit # pylint: disable=unused-variable
|
||||
webkit_available = True
|
||||
except ImportError:
|
||||
webkit_available = False
|
||||
|
||||
if args.backend is not None:
|
||||
return args.backend
|
||||
elif webkit_available:
|
||||
return 'webkit'
|
||||
else:
|
||||
return 'webengine'
|
||||
|
||||
|
||||
def qt_version(qversion=None, qt_version_str=None):
|
||||
"""Get a Qt version string based on the runtime/compiled versions."""
|
||||
if qversion is None:
|
||||
@ -268,50 +178,65 @@ def qt_version(qversion=None, qt_version_str=None):
|
||||
return qversion
|
||||
|
||||
|
||||
def check_qt_version(backend):
|
||||
def check_qt_version():
|
||||
"""Check if the Qt version is recent enough."""
|
||||
from PyQt5.QtCore import PYQT_VERSION, PYQT_VERSION_STR
|
||||
from qutebrowser.utils import qtutils
|
||||
if (not qtutils.version_check('5.2.0', strict=True) or
|
||||
if (not qtutils.version_check('5.7.1', strict=True) or
|
||||
PYQT_VERSION < 0x050200):
|
||||
text = ("Fatal error: Qt and PyQt >= 5.2.0 are required, but Qt {} / "
|
||||
"PyQt {} is installed.".format(qt_version(),
|
||||
text = ("Fatal error: Qt >= 5.7.1 and PyQt >= 5.7 are required, "
|
||||
"but Qt {} / PyQt {} is installed.".format(qt_version(),
|
||||
PYQT_VERSION_STR))
|
||||
_die(text)
|
||||
elif (backend == 'webengine' and (
|
||||
not qtutils.version_check('5.7.1', strict=True) or
|
||||
PYQT_VERSION < 0x050700)):
|
||||
text = ("Fatal error: Qt >= 5.7.1 and PyQt >= 5.7 are required for "
|
||||
"QtWebEngine support, but Qt {} / PyQt {} is installed."
|
||||
.format(qt_version(), PYQT_VERSION_STR))
|
||||
_die(text)
|
||||
|
||||
|
||||
def check_ssl_support(backend):
|
||||
def check_ssl_support():
|
||||
"""Check if SSL support is available."""
|
||||
from qutebrowser.utils import log
|
||||
|
||||
# pylint: disable=unused-variable
|
||||
try:
|
||||
from PyQt5.QtNetwork import QSslSocket
|
||||
except ImportError:
|
||||
_die("Fatal error: Your Qt is built without SSL support.")
|
||||
|
||||
|
||||
def check_backend_ssl_support(backend):
|
||||
"""Check for full SSL availability when we know the backend."""
|
||||
from PyQt5.QtNetwork import QSslSocket
|
||||
from qutebrowser.utils import log, usertypes
|
||||
text = ("Could not initialize QtNetwork SSL support. If you use "
|
||||
"OpenSSL 1.1 with a PyQt package from PyPI (e.g. on Archlinux "
|
||||
"or Debian Stretch), you need to set LD_LIBRARY_PATH to the path "
|
||||
"of OpenSSL 1.0.")
|
||||
if backend == 'webengine':
|
||||
text += " This only affects downloads."
|
||||
"of OpenSSL 1.0. This only affects downloads.")
|
||||
|
||||
if not QSslSocket.supportsSsl():
|
||||
if backend == 'webkit':
|
||||
if backend == usertypes.Backend.QtWebKit:
|
||||
_die("Could not initialize SSL support.")
|
||||
else:
|
||||
assert backend == 'webengine'
|
||||
assert backend == usertypes.Backend.QtWebEngine
|
||||
log.init.warning(text)
|
||||
|
||||
|
||||
def check_libraries(backend):
|
||||
def _check_modules(modules):
|
||||
"""Make sure the given modules are available."""
|
||||
from qutebrowser.utils import log
|
||||
|
||||
for name, text in modules.items():
|
||||
try:
|
||||
# https://github.com/pallets/jinja/pull/628
|
||||
# https://bitbucket.org/birkenfeld/pygments-main/issues/1314/
|
||||
# https://github.com/pallets/jinja/issues/646
|
||||
# https://bitbucket.org/fdik/pypeg/commits/dd15ca462b532019c0a3be1d39b8ee2f3fa32f4e
|
||||
messages = ['invalid escape sequence',
|
||||
'Flags not at the start of the expression']
|
||||
with log.ignore_py_warnings(
|
||||
category=DeprecationWarning,
|
||||
message=r'({})'.format('|'.join(messages))):
|
||||
importlib.import_module(name)
|
||||
except ImportError as e:
|
||||
_die(text, e)
|
||||
|
||||
|
||||
def check_libraries():
|
||||
"""Check if all needed Python libraries are installed."""
|
||||
modules = {
|
||||
'pkg_resources':
|
||||
@ -339,32 +264,37 @@ def check_libraries(backend):
|
||||
'PyQt5.QtQml': _missing_str("PyQt5.QtQml"),
|
||||
'PyQt5.QtSql': _missing_str("PyQt5.QtSql"),
|
||||
}
|
||||
if backend == 'webengine':
|
||||
modules['PyQt5.QtWebEngineWidgets'] = _missing_str("QtWebEngine",
|
||||
webengine=True)
|
||||
modules['PyQt5.QtOpenGL'] = _missing_str("PyQt5.QtOpenGL")
|
||||
_check_modules(modules)
|
||||
|
||||
|
||||
def check_backend_libraries(backend):
|
||||
"""Make sure the libraries needed by the given backend are available.
|
||||
|
||||
Args:
|
||||
backend: The backend as usertypes.Backend member.
|
||||
"""
|
||||
from qutebrowser.utils import usertypes
|
||||
if backend == usertypes.Backend.QtWebEngine:
|
||||
modules = {
|
||||
'PyQt5.QtWebEngineWidgets':
|
||||
_missing_str("QtWebEngine", webengine=True),
|
||||
'PyQt5.QtOpenGL': _missing_str("PyQt5.QtOpenGL"),
|
||||
}
|
||||
else:
|
||||
assert backend == 'webkit'
|
||||
modules['PyQt5.QtWebKit'] = _missing_str("PyQt5.QtWebKit")
|
||||
modules['PyQt5.QtWebKitWidgets'] = _missing_str(
|
||||
"PyQt5.QtWebKitWidgets")
|
||||
assert backend == usertypes.Backend.QtWebKit, backend
|
||||
modules = {
|
||||
'PyQt5.QtWebKit': _missing_str("PyQt5.QtWebKit"),
|
||||
'PyQt5.QtWebKitWidgets': _missing_str("PyQt5.QtWebKitWidgets"),
|
||||
}
|
||||
_check_modules(modules)
|
||||
|
||||
from qutebrowser.utils import log
|
||||
|
||||
for name, text in modules.items():
|
||||
try:
|
||||
# https://github.com/pallets/jinja/pull/628
|
||||
# https://bitbucket.org/birkenfeld/pygments-main/issues/1314/
|
||||
# https://github.com/pallets/jinja/issues/646
|
||||
# https://bitbucket.org/fdik/pypeg/commits/dd15ca462b532019c0a3be1d39b8ee2f3fa32f4e
|
||||
messages = ['invalid escape sequence',
|
||||
'Flags not at the start of the expression']
|
||||
with log.ignore_py_warnings(
|
||||
category=DeprecationWarning,
|
||||
message=r'({})'.format('|'.join(messages))):
|
||||
importlib.import_module(name)
|
||||
except ImportError as e:
|
||||
_die(text, e)
|
||||
def check_new_webkit(backend):
|
||||
"""Make sure we use QtWebEngine or a new QtWebKit."""
|
||||
from qutebrowser.utils import usertypes, qtutils
|
||||
if backend == usertypes.Backend.QtWebKit and not qtutils.is_new_qtwebkit():
|
||||
_die("qutebrowser does not support legacy QtWebKit versions anymore, "
|
||||
"see the installation docs for details.")
|
||||
|
||||
|
||||
def remove_inputhook():
|
||||
@ -395,18 +325,7 @@ def check_optimize_flag():
|
||||
"unexpected behavior may occur.")
|
||||
|
||||
|
||||
def set_backend(backend):
|
||||
"""Set the objects.backend global to the given backend (as string)."""
|
||||
from qutebrowser.misc import objects
|
||||
from qutebrowser.utils import usertypes
|
||||
backends = {
|
||||
'webkit': usertypes.Backend.QtWebKit,
|
||||
'webengine': usertypes.Backend.QtWebEngine,
|
||||
}
|
||||
objects.backend = backends[backend]
|
||||
|
||||
|
||||
def earlyinit(args):
|
||||
def early_init(args):
|
||||
"""Do all needed early initialization.
|
||||
|
||||
Note that it's vital the other earlyinit functions get called in the right
|
||||
@ -423,15 +342,23 @@ def earlyinit(args):
|
||||
check_pyqt_core()
|
||||
# Init logging as early as possible
|
||||
init_log(args)
|
||||
# Now the faulthandler is enabled we fix the Qt harfbuzzing library, before
|
||||
# importing QtWidgets.
|
||||
fix_harfbuzz(args)
|
||||
# Now we can be sure QtCore is available, so we can print dialogs on
|
||||
# errors, so people only using the GUI notice them as well.
|
||||
backend = get_backend(args)
|
||||
check_qt_version(backend)
|
||||
check_libraries()
|
||||
check_qt_version()
|
||||
remove_inputhook()
|
||||
check_libraries(backend)
|
||||
check_ssl_support(backend)
|
||||
check_ssl_support()
|
||||
check_optimize_flag()
|
||||
set_backend(backend)
|
||||
|
||||
|
||||
def init_with_backend(backend):
|
||||
"""Do later stages of init when we know the backend.
|
||||
|
||||
Args:
|
||||
backend: The backend as usertypes.Backend member.
|
||||
"""
|
||||
assert not isinstance(backend, str), backend
|
||||
assert backend is not None
|
||||
check_backend_libraries(backend)
|
||||
check_backend_ssl_support(backend)
|
||||
check_new_webkit(backend)
|
||||
|
@ -22,5 +22,14 @@
|
||||
# NOTE: We need to be careful with imports here, as this is imported from
|
||||
# earlyinit.
|
||||
|
||||
|
||||
class NoBackend:
|
||||
|
||||
"""Special object when there's no backend set so we notice that."""
|
||||
|
||||
def __eq__(self, other):
|
||||
raise AssertionError("No backend set!")
|
||||
|
||||
|
||||
# A usertypes.Backend member
|
||||
backend = None
|
||||
backend = NoBackend()
|
||||
|
@ -32,6 +32,7 @@ from qutebrowser.utils import (standarddir, objreg, qtutils, log, message,
|
||||
from qutebrowser.commands import cmdexc, cmdutils
|
||||
from qutebrowser.config import config, configfiles
|
||||
from qutebrowser.completion.models import miscmodels
|
||||
from qutebrowser.mainwindow import mainwindow
|
||||
|
||||
|
||||
default = object() # Sentinel value
|
||||
@ -371,7 +372,6 @@ class SessionManager(QObject):
|
||||
name: The name of the session to load.
|
||||
temp: If given, don't set the current session.
|
||||
"""
|
||||
from qutebrowser.mainwindow import mainwindow
|
||||
path = self._get_session_path(name, check_exists=True)
|
||||
try:
|
||||
with open(path, encoding='utf-8') as f:
|
||||
|
@ -95,9 +95,6 @@ def get_argparser():
|
||||
action='store_false', dest='color')
|
||||
debug.add_argument('--force-color', help="Force colored logging",
|
||||
action='store_true')
|
||||
debug.add_argument('--harfbuzz', choices=['old', 'new', 'system', 'auto'],
|
||||
default='auto', help="HarfBuzz engine version to use. "
|
||||
"Default: auto.")
|
||||
debug.add_argument('--relaxed-config', action='store_true',
|
||||
help="Silently remove unknown config options.")
|
||||
debug.add_argument('--nowindow', action='store_true', help="Don't show "
|
||||
@ -170,8 +167,8 @@ def main():
|
||||
# from json.
|
||||
data = json.loads(args.json_args)
|
||||
args = argparse.Namespace(**data)
|
||||
earlyinit.earlyinit(args)
|
||||
earlyinit.early_init(args)
|
||||
# We do this imports late as earlyinit needs to be run first (because of
|
||||
# the harfbuzz fix and version checking).
|
||||
# version checking and other early initialization)
|
||||
from qutebrowser import app
|
||||
return app.run(args)
|
||||
|
@ -345,7 +345,7 @@ def qt_message_handler(msg_type, context, msg):
|
||||
try:
|
||||
qt_to_logging[QtCore.QtInfoMsg] = logging.INFO
|
||||
except AttributeError:
|
||||
# Qt < 5.5
|
||||
# While we don't support Qt < 5.5 anymore, logging still needs to work
|
||||
pass
|
||||
|
||||
# Change levels of some well-known messages to debug so they don't get
|
||||
|
@ -105,13 +105,9 @@ class ObjectRegistry(collections.UserDict):
|
||||
func = partial_objs[name]
|
||||
try:
|
||||
self[name].destroyed.disconnect(func)
|
||||
except (RuntimeError, TypeError):
|
||||
except RuntimeError:
|
||||
# If C++ has deleted the object, the slot is already
|
||||
# disconnected.
|
||||
#
|
||||
# With older PyQt-versions (5.2.1) we'll get a "TypeError:
|
||||
# pyqtSignal must be bound to a QObject" instead:
|
||||
# https://github.com/qutebrowser/qutebrowser/issues/257
|
||||
pass
|
||||
del partial_objs[name]
|
||||
|
||||
@ -145,7 +141,7 @@ class ObjectRegistry(collections.UserDict):
|
||||
for name, obj in self.data.items():
|
||||
try:
|
||||
obj_repr = repr(obj)
|
||||
except (RuntimeError, TypeError):
|
||||
except RuntimeError:
|
||||
# Underlying object deleted probably
|
||||
obj_repr = '<deleted>'
|
||||
lines.append("{}: {}".format(name, obj_repr))
|
||||
|
@ -28,10 +28,10 @@ Module attributes:
|
||||
|
||||
|
||||
import io
|
||||
import os
|
||||
import operator
|
||||
import contextlib
|
||||
|
||||
import pkg_resources
|
||||
from PyQt5.QtCore import (qVersion, QEventLoop, QDataStream, QByteArray,
|
||||
QIODevice, QSaveFile, QT_VERSION_STR)
|
||||
try:
|
||||
@ -39,17 +39,6 @@ try:
|
||||
except ImportError: # pragma: no cover
|
||||
qWebKitVersion = None
|
||||
|
||||
from qutebrowser.utils import log
|
||||
|
||||
with log.ignore_py_warnings(category=PendingDeprecationWarning, module='imp'):
|
||||
with log.ignore_py_warnings(category=ImportWarning):
|
||||
# This imports 'imp' and gives us a PendingDeprecationWarning on
|
||||
# Debian Jessie.
|
||||
#
|
||||
# On Archlinux, we get ImportWarning from
|
||||
# importlib/_bootstrap_external.py for modules with missing __init__.
|
||||
import pkg_resources
|
||||
|
||||
|
||||
MAXVALS = {
|
||||
'int': 2 ** 31 - 1,
|
||||
@ -102,8 +91,8 @@ def version_check(version, exact=False, strict=False):
|
||||
return result
|
||||
|
||||
|
||||
def is_qtwebkit_ng():
|
||||
"""Check if the given version is QtWebKit-NG."""
|
||||
def is_new_qtwebkit():
|
||||
"""Check if the given version is a new QtWebKit."""
|
||||
assert qWebKitVersion is not None
|
||||
return (pkg_resources.parse_version(qWebKitVersion()) >
|
||||
pkg_resources.parse_version('538.1'))
|
||||
@ -137,15 +126,6 @@ def check_overflow(arg, ctype, fatal=True):
|
||||
return arg
|
||||
|
||||
|
||||
def check_print_compat():
|
||||
"""Check if printing should work in the given Qt version."""
|
||||
# WORKAROUND (remove this when we bump the requirements to 5.3.0)
|
||||
if os.name == 'nt':
|
||||
return version_check('5.3')
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def ensure_valid(obj):
|
||||
"""Ensure a Qt object with an .isValid() method is valid."""
|
||||
if not obj.isValid():
|
||||
|
@ -1,68 +0,0 @@
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2016-2017 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
# qutebrowser is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# qutebrowser is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# pylint: disable=unused-import,bad-mcs-method-argument
|
||||
|
||||
"""Wrapper for Python 3.5's typing module.
|
||||
|
||||
This wrapper is needed as both Python 3.5 and typing for PyPI isn't commonly
|
||||
packaged yet. As we don't actually need anything from the typing module at
|
||||
runtime, we instead mock the typing classes (using objects to make things
|
||||
easier) so the typing module isn't a hard dependency.
|
||||
"""
|
||||
|
||||
# Those are defined here to make them testable easily
|
||||
|
||||
|
||||
class FakeTypingMeta(type):
|
||||
|
||||
"""Fake typing metaclass like typing.TypingMeta."""
|
||||
|
||||
def __init__(self, *args, # pylint: disable=super-init-not-called
|
||||
**_kwds):
|
||||
pass
|
||||
|
||||
|
||||
class FakeUnionMeta(FakeTypingMeta):
|
||||
|
||||
"""Fake union metaclass metaclass like typing.UnionMeta."""
|
||||
|
||||
def __new__(cls, name, bases, namespace, parameters=None):
|
||||
if parameters is None:
|
||||
return super().__new__(cls, name, bases, namespace)
|
||||
self = super().__new__(cls, name, bases, {})
|
||||
self.__union_params__ = tuple(parameters)
|
||||
return self
|
||||
|
||||
def __getitem__(self, parameters):
|
||||
return self.__class__(self.__name__, self.__bases__,
|
||||
dict(self.__dict__), parameters=parameters)
|
||||
|
||||
|
||||
class FakeUnion(metaclass=FakeUnionMeta):
|
||||
|
||||
"""Fake Union type like typing.Union."""
|
||||
|
||||
__union_params__ = None
|
||||
|
||||
|
||||
try:
|
||||
from typing import Union
|
||||
except ImportError: # pragma: no cover
|
||||
Union = FakeUnion
|
@ -355,14 +355,8 @@ class Question(QObject):
|
||||
log.misc.debug("Question was already aborted")
|
||||
return
|
||||
self.is_aborted = True
|
||||
try:
|
||||
self.aborted.emit()
|
||||
self.completed.emit()
|
||||
except TypeError:
|
||||
# WORKAROUND
|
||||
# We seem to get "pyqtSignal must be bound to a QObject, not
|
||||
# 'Question' here, which makes no sense at all..."
|
||||
log.misc.exception("Error while aborting question")
|
||||
|
||||
|
||||
class Timer(QTimer):
|
||||
|
@ -42,7 +42,7 @@ except ImportError: # pragma: no cover
|
||||
from yaml import SafeLoader as YamlLoader, SafeDumper as YamlDumper
|
||||
|
||||
import qutebrowser
|
||||
from qutebrowser.utils import qtutils, log
|
||||
from qutebrowser.utils import qtutils, log, debug
|
||||
|
||||
|
||||
fake_clipboard = None
|
||||
@ -425,14 +425,13 @@ class KeyInfo:
|
||||
self.text = text
|
||||
|
||||
def __repr__(self):
|
||||
# Meh, dependency cycle...
|
||||
from qutebrowser.utils.debug import qenum_key
|
||||
if self.modifiers is None:
|
||||
modifiers = None
|
||||
else:
|
||||
#modifiers = qflags_key(Qt, self.modifiers)
|
||||
modifiers = hex(int(self.modifiers))
|
||||
return get_repr(self, constructor=True, key=qenum_key(Qt, self.key),
|
||||
return get_repr(self, constructor=True,
|
||||
key=debug.qenum_key(Qt, self.key),
|
||||
modifiers=modifiers, text=self.text)
|
||||
|
||||
def __eq__(self, other):
|
||||
@ -814,10 +813,12 @@ def open_file(filename, cmdline=None):
|
||||
the filename is appended to the cmdline.
|
||||
"""
|
||||
# Import late to avoid circular imports:
|
||||
# utils -> config -> configdata -> configtypes -> cmdutils -> command ->
|
||||
# utils
|
||||
from qutebrowser.misc import guiprocess
|
||||
# - usertypes -> utils -> guiprocess -> message -> usertypes
|
||||
# - usertypes -> utils -> config -> configdata -> configtypes ->
|
||||
# cmdutils -> command -> message -> usertypes
|
||||
from qutebrowser.config import config
|
||||
from qutebrowser.misc import guiprocess
|
||||
|
||||
# the default program to open downloads with - will be empty string
|
||||
# if we want to use the default
|
||||
override = config.val.downloads.open_dispatcher
|
||||
|
@ -44,7 +44,7 @@ except ImportError: # pragma: no cover
|
||||
QWebEngineProfile = None
|
||||
|
||||
import qutebrowser
|
||||
from qutebrowser.utils import log, utils, standarddir, usertypes, qtutils
|
||||
from qutebrowser.utils import log, utils, standarddir, usertypes
|
||||
from qutebrowser.misc import objects, earlyinit, sql
|
||||
from qutebrowser.browser import pdfjs
|
||||
|
||||
@ -190,7 +190,6 @@ def _module_versions():
|
||||
('pygments', ['__version__']),
|
||||
('yaml', ['__version__']),
|
||||
('cssutils', ['__version__']),
|
||||
('typing', []),
|
||||
('PyQt5.QtWebEngineWidgets', []),
|
||||
('PyQt5.QtWebKitWidgets', []),
|
||||
])
|
||||
@ -304,9 +303,7 @@ def _chromium_version():
|
||||
def _backend():
|
||||
"""Get the backend line with relevant information."""
|
||||
if objects.backend == usertypes.Backend.QtWebKit:
|
||||
return '{} (WebKit {})'.format(
|
||||
'QtWebKit-NG' if qtutils.is_qtwebkit_ng() else 'legacy QtWebKit',
|
||||
qWebKitVersion())
|
||||
return 'new QtWebKit (WebKit {})'.format(qWebKitVersion())
|
||||
else:
|
||||
webengine = usertypes.Backend.QtWebEngine
|
||||
assert objects.backend == webengine, objects.backend
|
||||
|
@ -149,8 +149,6 @@ PERFECT_FILES = [
|
||||
'utils/jinja.py'),
|
||||
('tests/unit/utils/test_error.py',
|
||||
'utils/error.py'),
|
||||
('tests/unit/utils/test_typing.py',
|
||||
'utils/typing.py'),
|
||||
('tests/unit/utils/test_javascript.py',
|
||||
'utils/javascript.py'),
|
||||
|
||||
@ -291,7 +289,7 @@ def main_check_all():
|
||||
tests.
|
||||
|
||||
This runs pytest with the used executable, so check_coverage.py should be
|
||||
called with something like ./.tox/py34/bin/python.
|
||||
called with something like ./.tox/py36/bin/python.
|
||||
"""
|
||||
for test_file, src_file in PERFECT_FILES:
|
||||
if test_file is None:
|
||||
|
@ -5,10 +5,6 @@
|
||||
#
|
||||
|
||||
case $TESTENV in
|
||||
py34-cov)
|
||||
exe=/usr/bin/python3.4
|
||||
full=full
|
||||
;;
|
||||
py3*-pyqt*)
|
||||
exe=$(readlink -f .tox/$TESTENV/bin/python)
|
||||
full=
|
||||
|
@ -121,7 +121,8 @@ setupdata = {
|
||||
'Operating System :: Microsoft :: Windows :: Windows 7',
|
||||
'Operating System :: POSIX :: Linux',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Topic :: Internet',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Internet :: WWW/HTTP :: Browsers',
|
||||
|
@ -62,7 +62,6 @@ def _apply_platform_markers(config, item):
|
||||
('no_ci', 'CI' in os.environ, "Skipped on CI."),
|
||||
('issue2478', os.name == 'nt' and config.webengine,
|
||||
"Broken with QtWebEngine on Windows"),
|
||||
('qt55', not qtutils.version_check('5.5'), "Requires Qt 5.5 or newer"),
|
||||
]
|
||||
|
||||
for searched_marker, condition, default_reason in markers:
|
||||
@ -128,12 +127,9 @@ def pytest_collection_modifyitems(config, items):
|
||||
item.add_marker(pytest.mark.xfail(run=False))
|
||||
if item.get_marker('js_prompt'):
|
||||
if config.webengine:
|
||||
js_prompt_pyqt_version = 0x050700
|
||||
else:
|
||||
js_prompt_pyqt_version = 0x050300
|
||||
item.add_marker(pytest.mark.skipif(
|
||||
PYQT_VERSION <= js_prompt_pyqt_version,
|
||||
reason='JS prompts are not supported with this PyQt version'))
|
||||
PYQT_VERSION <= 0x050700,
|
||||
reason='JS prompts are not supported with PyQt 5.7'))
|
||||
|
||||
if deselected:
|
||||
deselected_items.append(item)
|
||||
|
@ -109,8 +109,6 @@ def _get_backend_tag(tag):
|
||||
'qtwebengine_todo': pytest.mark.qtwebengine_todo,
|
||||
'qtwebengine_skip': pytest.mark.qtwebengine_skip,
|
||||
'qtwebkit_skip': pytest.mark.qtwebkit_skip,
|
||||
'qtwebkit_ng_xfail': pytest.mark.qtwebkit_ng_xfail,
|
||||
'qtwebkit_ng_skip': pytest.mark.qtwebkit_ng_skip,
|
||||
}
|
||||
if not any(tag.startswith(t + ':') for t in pytest_marks):
|
||||
return None
|
||||
@ -143,10 +141,6 @@ def pytest_collection_modifyitems(config, items):
|
||||
config.webengine),
|
||||
('qtwebkit_skip', 'Skipped with QtWebKit', pytest.mark.skipif,
|
||||
not config.webengine),
|
||||
('qtwebkit_ng_xfail', 'Failing with QtWebKit-NG', pytest.mark.xfail,
|
||||
not config.webengine and qtutils.is_qtwebkit_ng()),
|
||||
('qtwebkit_ng_skip', 'Skipped with QtWebKit-NG', pytest.mark.skipif,
|
||||
not config.webengine and qtutils.is_qtwebkit_ng()),
|
||||
('qtwebengine_flaky', 'Flaky with QtWebEngine', pytest.mark.skipif,
|
||||
config.webengine),
|
||||
('qtwebengine_mac_xfail', 'Fails on macOS with QtWebEngine',
|
||||
|
@ -6,12 +6,8 @@
|
||||
var my_window;
|
||||
|
||||
function open_modal() {
|
||||
if (window.showModalDialog) {
|
||||
window.showModalDialog();
|
||||
} else {
|
||||
window.open('about:blank', 'window', 'modal');
|
||||
}
|
||||
}
|
||||
|
||||
function open_normal() {
|
||||
my_window = window.open('about:blank', 'my_window');
|
||||
|
@ -104,12 +104,3 @@ Feature: Page history
|
||||
And I wait until qute://history is loaded
|
||||
Then the page should contain the plaintext "3.txt"
|
||||
Then the page should contain the plaintext "4.txt"
|
||||
|
||||
## Bugs
|
||||
|
||||
@qtwebengine_skip @qtwebkit_ng_skip
|
||||
Scenario: Opening a valid URL which turns out invalid
|
||||
When I set url.auto_search to naive
|
||||
And I run :open http://foo%40bar@baz
|
||||
Then "QFSFileEngine::open: No file name specified" should be logged
|
||||
And "Error while loading : Host not found" should be logged
|
||||
|
@ -17,7 +17,7 @@ Feature: Javascript stuff
|
||||
And I run :click-element id close-normal
|
||||
Then "Focus object changed: *" should be logged
|
||||
|
||||
@qtwebkit_ng_skip
|
||||
@qtwebkit_skip
|
||||
Scenario: Opening/closing a modal window via JS
|
||||
When I open data/javascript/window_open.html
|
||||
And I run :tab-only
|
||||
@ -26,7 +26,6 @@ Feature: Javascript stuff
|
||||
And I run :tab-focus 1
|
||||
And I run :click-element id close-normal
|
||||
Then "Focus object changed: *" should be logged
|
||||
# WebModalDialog with QtWebKit, WebDialog with QtWebEngine
|
||||
And "Web*Dialog requested, but we don't support that!" should be logged
|
||||
|
||||
# https://github.com/qutebrowser/qutebrowser/issues/906
|
||||
|
@ -42,7 +42,6 @@ Feature: Using private browsing
|
||||
|
||||
## https://github.com/qutebrowser/qutebrowser/issues/1219
|
||||
|
||||
@qtwebkit_ng_skip: private browsing is not implemented yet
|
||||
Scenario: Sharing cookies with private browsing
|
||||
When I open cookies/set?qute-test=42 without waiting in a private window
|
||||
And I wait until cookies is loaded
|
||||
|
@ -21,8 +21,6 @@ import pytest
|
||||
|
||||
import pytest_bdd as bdd
|
||||
|
||||
from PyQt5.QtCore import PYQT_VERSION
|
||||
|
||||
|
||||
bdd.scenarios('yankpaste.feature')
|
||||
|
||||
@ -34,10 +32,6 @@ def init_fake_clipboard(quteproc):
|
||||
|
||||
|
||||
@bdd.when(bdd.parsers.parse('I insert "{value}" into the text field'))
|
||||
def set_text_field(request, quteproc, value):
|
||||
if request.config.webengine and PYQT_VERSION >= 0x50700:
|
||||
cmd = ":jseval --world=0 set_text('{}')".format(value)
|
||||
else:
|
||||
cmd = ":jseval set_text('{}')".format(value)
|
||||
quteproc.send_cmd(cmd)
|
||||
def set_text_field(quteproc, value):
|
||||
quteproc.send_cmd(":jseval --world=0 set_text('{}')".format(value))
|
||||
quteproc.wait_for_js('textarea set to: ' + value)
|
||||
|
@ -29,8 +29,6 @@ import pytest
|
||||
|
||||
from PyQt5.QtCore import QProcess
|
||||
|
||||
from qutebrowser.utils import qtutils
|
||||
|
||||
|
||||
def _base_args(config):
|
||||
"""Get the arguments to pass with every invocation."""
|
||||
@ -188,8 +186,6 @@ def test_version(request):
|
||||
assert re.search(r'^qutebrowser\s+v\d+(\.\d+)', stdout) is not None
|
||||
|
||||
|
||||
@pytest.mark.skipif(not qtutils.version_check('5.3'),
|
||||
reason="Does not work on Qt 5.2")
|
||||
def test_qt_arg(request, quteproc_new, tmpdir):
|
||||
"""Test --qt-arg."""
|
||||
args = (['--temp-basedir', '--qt-arg', 'stylesheet',
|
||||
@ -269,7 +265,7 @@ def test_launching_with_python2():
|
||||
pytest.skip("python2 not found")
|
||||
_stdout, stderr = proc.communicate()
|
||||
assert proc.returncode == 1
|
||||
error = "At least Python 3.4 is required to run qutebrowser"
|
||||
error = "At least Python 3.5 is required to run qutebrowser"
|
||||
assert stderr.decode('ascii').startswith(error)
|
||||
|
||||
|
||||
|
@ -43,7 +43,7 @@ from qutebrowser.browser.webkit import cookies
|
||||
from qutebrowser.misc import savemanager, sql
|
||||
from qutebrowser.keyinput import modeman
|
||||
|
||||
from PyQt5.QtCore import PYQT_VERSION, pyqtSignal, QEvent, QSize, Qt, QObject
|
||||
from PyQt5.QtCore import pyqtSignal, QEvent, QSize, Qt, QObject
|
||||
from PyQt5.QtGui import QKeyEvent
|
||||
from PyQt5.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout
|
||||
from PyQt5.QtNetwork import QNetworkCookieJar
|
||||
@ -156,8 +156,6 @@ def tab_registry(win_registry):
|
||||
@pytest.fixture
|
||||
def fake_web_tab(stubs, tab_registry, mode_manager, qapp):
|
||||
"""Fixture providing the FakeWebTab *class*."""
|
||||
if PYQT_VERSION < 0x050600:
|
||||
pytest.skip('Causes segfaults, see #1638')
|
||||
return stubs.FakeWebTab
|
||||
|
||||
|
||||
@ -417,8 +415,9 @@ def fake_save_manager():
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def fake_args():
|
||||
def fake_args(request):
|
||||
ns = types.SimpleNamespace()
|
||||
ns.backend = 'webengine' if request.config.webengine else 'webkit'
|
||||
objreg.register('args', ns)
|
||||
yield ns
|
||||
objreg.delete('args')
|
||||
|
@ -19,8 +19,6 @@
|
||||
|
||||
import pytest
|
||||
|
||||
from PyQt5.QtCore import PYQT_VERSION
|
||||
|
||||
from qutebrowser.browser import browsertab
|
||||
|
||||
pytestmark = pytest.mark.usefixtures('redirect_webengine_data')
|
||||
@ -48,9 +46,6 @@ def view(qtbot, config_stub, request):
|
||||
|
||||
@pytest.fixture(params=['webkit', 'webengine'])
|
||||
def tab(request, qtbot, tab_registry, cookiejar_and_cache, mode_manager):
|
||||
if PYQT_VERSION < 0x050600:
|
||||
pytest.skip('Causes segfaults, see #1638')
|
||||
|
||||
if request.param == 'webkit':
|
||||
webkittab = pytest.importorskip('qutebrowser.browser.webkit.webkittab')
|
||||
tab_class = webkittab.WebKitTab
|
||||
|
@ -20,10 +20,9 @@
|
||||
import http.server
|
||||
import threading
|
||||
import logging
|
||||
import sys
|
||||
import pytest
|
||||
|
||||
from PyQt5.QtCore import QUrl, QT_VERSION_STR
|
||||
from PyQt5.QtCore import QUrl
|
||||
from PyQt5.QtNetwork import (QNetworkProxy, QNetworkProxyQuery, QHostInfo,
|
||||
QHostAddress)
|
||||
|
||||
@ -206,14 +205,6 @@ def test_secret_url(url, has_secret, from_file):
|
||||
res.resolve(QNetworkProxyQuery(QUrl(url)), from_file=from_file)
|
||||
|
||||
|
||||
# See https://github.com/qutebrowser/qutebrowser/pull/1891#issuecomment-259222615
|
||||
|
||||
try:
|
||||
from PyQt5 import QtWebEngineWidgets
|
||||
except ImportError:
|
||||
QtWebEngineWidgets = None
|
||||
|
||||
|
||||
def fetcher_test(test_str):
|
||||
class PACHandler(http.server.BaseHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
@ -244,10 +235,6 @@ def fetcher_test(test_str):
|
||||
return fetcher
|
||||
|
||||
|
||||
@pytest.mark.skipif(QT_VERSION_STR.startswith('5.7') and
|
||||
QtWebEngineWidgets is not None and
|
||||
sys.platform == "linux",
|
||||
reason="Segfaults when run with QtWebEngine tests on Linux")
|
||||
def test_fetch_success():
|
||||
test_str = """
|
||||
function FindProxyForURL(domain, host) {
|
||||
@ -260,10 +247,6 @@ def test_fetch_success():
|
||||
assert len(proxies) == 3
|
||||
|
||||
|
||||
@pytest.mark.skipif(QT_VERSION_STR.startswith('5.7') and
|
||||
QtWebEngineWidgets is not None and
|
||||
sys.platform == "linux",
|
||||
reason="Segfaults when run with QtWebEngine tests on Linux")
|
||||
def test_fetch_evalerror(caplog):
|
||||
test_str = """
|
||||
function FindProxyForURL(domain, host) {
|
||||
|
@ -24,11 +24,12 @@
|
||||
import sys
|
||||
import logging
|
||||
import types
|
||||
import typing
|
||||
|
||||
import pytest
|
||||
|
||||
from qutebrowser.commands import cmdutils, cmdexc, argparser, command
|
||||
from qutebrowser.utils import usertypes, typing
|
||||
from qutebrowser.utils import usertypes
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
|
@ -877,6 +877,9 @@ def init_patch(qapp, fake_save_manager, monkeypatch, config_tmpdir,
|
||||
monkeypatch.setattr(config, 'key_instance', None)
|
||||
monkeypatch.setattr(config, '_change_filters', [])
|
||||
monkeypatch.setattr(config, '_init_errors', [])
|
||||
# Make sure we get no SSL warning
|
||||
monkeypatch.setattr(config.earlyinit, 'check_backend_ssl_support',
|
||||
lambda _backend: None)
|
||||
yield
|
||||
try:
|
||||
objreg.delete('config-commands')
|
||||
@ -888,7 +891,7 @@ def init_patch(qapp, fake_save_manager, monkeypatch, config_tmpdir,
|
||||
@pytest.mark.parametrize('config_py', [True, 'error', False])
|
||||
@pytest.mark.parametrize('invalid_yaml', ['42', 'unknown', False])
|
||||
# pylint: disable=too-many-branches
|
||||
def test_early_init(init_patch, config_tmpdir, caplog,
|
||||
def test_early_init(init_patch, config_tmpdir, caplog, fake_args,
|
||||
load_autoconfig, config_py, invalid_yaml):
|
||||
# Prepare files
|
||||
autoconfig_file = config_tmpdir / 'autoconfig.yml'
|
||||
@ -914,7 +917,7 @@ def test_early_init(init_patch, config_tmpdir, caplog,
|
||||
'utf-8', ensure=True)
|
||||
|
||||
with caplog.at_level(logging.ERROR):
|
||||
config.early_init()
|
||||
config.early_init(fake_args)
|
||||
|
||||
# Check error messages
|
||||
expected_errors = []
|
||||
@ -954,15 +957,16 @@ def test_early_init(init_patch, config_tmpdir, caplog,
|
||||
assert config.instance._values == {'colors.hints.fg': 'magenta'}
|
||||
|
||||
|
||||
def test_early_init_invalid_change_filter(init_patch):
|
||||
def test_early_init_invalid_change_filter(init_patch, fake_args):
|
||||
config.change_filter('foobar')
|
||||
with pytest.raises(configexc.NoOptionError):
|
||||
config.early_init()
|
||||
config.early_init(fake_args)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('errors', [True, False])
|
||||
def test_late_init(init_patch, monkeypatch, fake_save_manager, mocker, errors):
|
||||
config.early_init()
|
||||
def test_late_init(init_patch, monkeypatch, fake_save_manager, fake_args,
|
||||
mocker, errors):
|
||||
config.early_init(fake_args)
|
||||
if errors:
|
||||
err = configexc.ConfigErrorDesc("Error text", Exception("Exception"))
|
||||
errs = configexc.ConfigFileErrors("config.py", [err])
|
||||
|
@ -28,7 +28,7 @@ import pytest
|
||||
from qutebrowser.misc import checkpyver
|
||||
|
||||
|
||||
TEXT = (r"At least Python 3.4 is required to run qutebrowser, but "
|
||||
TEXT = (r"At least Python 3.5 is required to run qutebrowser, but "
|
||||
r"\d+\.\d+\.\d+ is installed!\n")
|
||||
|
||||
|
||||
@ -60,7 +60,7 @@ def test_patched_no_errwindow(capfd, monkeypatch):
|
||||
"""Test with a patched sys.hexversion and --no-err-windows."""
|
||||
monkeypatch.setattr(checkpyver.sys, 'argv',
|
||||
[sys.argv[0], '--no-err-windows'])
|
||||
monkeypatch.setattr(checkpyver.sys, 'hexversion', 0x03000000)
|
||||
monkeypatch.setattr(checkpyver.sys, 'hexversion', 0x03040000)
|
||||
monkeypatch.setattr(checkpyver.sys, 'exit', lambda status: None)
|
||||
checkpyver.check_python_version()
|
||||
stdout, stderr = capfd.readouterr()
|
||||
@ -70,7 +70,7 @@ def test_patched_no_errwindow(capfd, monkeypatch):
|
||||
|
||||
def test_patched_errwindow(capfd, mocker, monkeypatch):
|
||||
"""Test with a patched sys.hexversion and a fake Tk."""
|
||||
monkeypatch.setattr(checkpyver.sys, 'hexversion', 0x03000000)
|
||||
monkeypatch.setattr(checkpyver.sys, 'hexversion', 0x03040000)
|
||||
monkeypatch.setattr(checkpyver.sys, 'exit', lambda status: None)
|
||||
|
||||
try:
|
||||
|
@ -19,11 +19,7 @@
|
||||
|
||||
"""Test qutebrowser.misc.earlyinit."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import types
|
||||
import logging
|
||||
import pkg_resources
|
||||
|
||||
import pytest
|
||||
|
||||
@ -35,94 +31,3 @@ def test_init_faulthandler_stderr_none(monkeypatch, attr):
|
||||
"""Make sure init_faulthandler works when sys.stderr/__stderr__ is None."""
|
||||
monkeypatch.setattr(sys, attr, None)
|
||||
earlyinit.init_faulthandler()
|
||||
|
||||
|
||||
class TestFixHarfbuzz:
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def clear_harfbuzz(self):
|
||||
"""Clear QT_HARFBUZZ before/after tests."""
|
||||
old_harfbuzz = os.environ.pop('QT_HARFBUZZ', None)
|
||||
yield
|
||||
if old_harfbuzz is None:
|
||||
os.environ.pop('QT_HARFBUZZ', None)
|
||||
else:
|
||||
os.environ['QT_HARFBUZZ'] = old_harfbuzz
|
||||
|
||||
@pytest.fixture
|
||||
def args(self):
|
||||
"""Get a fake argparse namespace."""
|
||||
return types.SimpleNamespace(harfbuzz='auto')
|
||||
|
||||
@pytest.mark.parametrize('harfbuzz, qt_version, platform, expected', [
|
||||
('auto', '5.2.1', 'linux', 'old'),
|
||||
('auto', '5.3.0', 'linux', 'new'),
|
||||
('auto', '5.3.2', 'linux', 'old'),
|
||||
('auto', '5.4.0', 'linux', None),
|
||||
|
||||
('auto', '5.2.1', 'windows', None),
|
||||
|
||||
('old', '5.3.0', 'linux', 'old'),
|
||||
('old', '5.4.0', 'linux', 'old'),
|
||||
|
||||
('new', '5.2.1', 'linux', 'new'),
|
||||
('new', '5.3.2', 'linux', 'new'),
|
||||
('new', '5.4.0', 'linux', 'new'),
|
||||
])
|
||||
def test_fix_harfbuzz(self, clear_harfbuzz, args, monkeypatch, caplog,
|
||||
harfbuzz, qt_version, platform, expected):
|
||||
"""Check the QT_HARFBUZZ env var."""
|
||||
args.harfbuzz = harfbuzz
|
||||
monkeypatch.setattr(earlyinit, '_qt_version',
|
||||
lambda: pkg_resources.parse_version(qt_version))
|
||||
monkeypatch.setattr(earlyinit.sys, 'platform', platform)
|
||||
|
||||
with caplog.at_level(logging.WARNING):
|
||||
# Because QtWidgets is already imported
|
||||
earlyinit.fix_harfbuzz(args)
|
||||
|
||||
assert os.environ.get('QT_HARFBUZZ', None) == expected
|
||||
|
||||
@pytest.mark.parametrize('frozen, level', [
|
||||
(True, logging.DEBUG),
|
||||
(False, logging.WARNING),
|
||||
])
|
||||
def test_widgets_warning(self, args, monkeypatch, caplog, frozen, level):
|
||||
"""Make sure fix_harfbuzz warns when QtWidgets is imported."""
|
||||
# Make sure QtWidgets is in sys.modules
|
||||
from PyQt5 import QtWidgets # pylint: disable=unused-variable
|
||||
if frozen:
|
||||
monkeypatch.setattr(earlyinit.sys, 'frozen', True, raising=False)
|
||||
else:
|
||||
monkeypatch.delattr(earlyinit.sys, 'frozen', raising=False)
|
||||
|
||||
with caplog.at_level(level):
|
||||
earlyinit.fix_harfbuzz(args)
|
||||
|
||||
record = caplog.records[0]
|
||||
assert record.levelno == level
|
||||
msg = "Harfbuzz fix attempted but QtWidgets is already imported!"
|
||||
assert record.message == msg
|
||||
|
||||
def test_no_warning(self, args, monkeypatch):
|
||||
"""Without QtWidgets in sys.modules, no warning should be shown."""
|
||||
monkeypatch.setattr(earlyinit.sys, 'modules', {})
|
||||
earlyinit.fix_harfbuzz(args)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('same', [True, False])
|
||||
def test_qt_version(same):
|
||||
if same:
|
||||
qt_version_str = '5.4.0'
|
||||
expected = '5.4.0'
|
||||
else:
|
||||
qt_version_str = '5.3.0'
|
||||
expected = '5.4.0 (compiled 5.3.0)'
|
||||
actual = earlyinit.qt_version(qversion='5.4.0',
|
||||
qt_version_str=qt_version_str)
|
||||
assert actual == expected
|
||||
|
||||
|
||||
def test_qt_version_no_args():
|
||||
"""Make sure qt_version without arguments at least works."""
|
||||
earlyinit.qt_version()
|
||||
|
@ -35,7 +35,7 @@ from PyQt5.QtTest import QSignalSpy
|
||||
|
||||
import qutebrowser
|
||||
from qutebrowser.misc import ipc
|
||||
from qutebrowser.utils import objreg, qtutils, standarddir
|
||||
from qutebrowser.utils import objreg, standarddir
|
||||
from helpers import stubs
|
||||
|
||||
|
||||
@ -778,26 +778,7 @@ def test_connect_inexistent(qlocalsocket):
|
||||
assert qlocalsocket.error() == QLocalSocket.ServerNotFoundError
|
||||
|
||||
|
||||
def test_socket_options_listen_problem(qlocalserver, short_tmpdir):
|
||||
"""In earlier versions of Qt, listening fails when using socketOptions.
|
||||
|
||||
With this test, we verify that this bug exists in the Qt version/OS
|
||||
combinations we expect it to, and doesn't exist in other versions.
|
||||
"""
|
||||
servername = str(short_tmpdir / 'x')
|
||||
qlocalserver.setSocketOptions(QLocalServer.UserAccessOption)
|
||||
ok = qlocalserver.listen(servername)
|
||||
if os.name == 'nt' or qtutils.version_check('5.4'):
|
||||
assert ok
|
||||
else:
|
||||
assert not ok
|
||||
assert qlocalserver.serverError() == QAbstractSocket.HostNotFoundError
|
||||
assert qlocalserver.errorString() == 'QLocalServer::listen: Name error'
|
||||
|
||||
|
||||
@pytest.mark.posix
|
||||
@pytest.mark.skipif(not qtutils.version_check('5.4'),
|
||||
reason="setSocketOptions is even more broken on Qt < 5.4.")
|
||||
def test_socket_options_address_in_use_problem(qlocalserver, short_tmpdir):
|
||||
"""Qt seems to ignore AddressInUseError when using socketOptions.
|
||||
|
||||
|
33
tests/unit/misc/test_objects.py
Normal file
33
tests/unit/misc/test_objects.py
Normal file
@ -0,0 +1,33 @@
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2017 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
# qutebrowser is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# qutebrowser is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import pytest
|
||||
|
||||
from qutebrowser.misc import objects
|
||||
from qutebrowser.utils import usertypes
|
||||
|
||||
|
||||
@pytest.mark.parametrize('func', [
|
||||
lambda: objects.NoBackend() == usertypes.Backend.QtWebEngine,
|
||||
lambda: objects.NoBackend() != usertypes.Backend.QtWebEngine,
|
||||
lambda: objects.NoBackend() in [usertypes.Backend.QtWebEngine],
|
||||
])
|
||||
def test_no_backend(func):
|
||||
with pytest.raises(AssertionError, match='No backend set!'):
|
||||
func()
|
@ -19,13 +19,9 @@
|
||||
|
||||
"""Tests for qutebrowser.utils.javascript."""
|
||||
|
||||
import binascii
|
||||
import os.path
|
||||
|
||||
import pytest
|
||||
import hypothesis
|
||||
import hypothesis.strategies
|
||||
from PyQt5.QtCore import PYQT_VERSION
|
||||
|
||||
from qutebrowser.utils import javascript
|
||||
|
||||
@ -61,65 +57,22 @@ class TestStringEscape:
|
||||
"""Test javascript escaping with some expected outcomes."""
|
||||
assert javascript.string_escape(before) == after
|
||||
|
||||
def _test_escape(self, text, qtbot, webframe):
|
||||
"""Helper function for test_real_escape*."""
|
||||
try:
|
||||
self._test_escape_simple(text, webframe)
|
||||
except AssertionError:
|
||||
# Try another method if the simple method failed.
|
||||
#
|
||||
# See _test_escape_hexlified documentation on why this is
|
||||
# necessary.
|
||||
self._test_escape_hexlified(text, qtbot, webframe)
|
||||
|
||||
def _test_escape_hexlified(self, text, qtbot, webframe):
|
||||
"""Test conversion by hexlifying in javascript.
|
||||
|
||||
Since the conversion of QStrings to Python strings is broken in some
|
||||
older PyQt versions in some corner cases, we load an HTML file which
|
||||
generates an MD5 of the escaped text and use that for comparisons.
|
||||
"""
|
||||
escaped = javascript.string_escape(text)
|
||||
path = os.path.join(os.path.dirname(__file__),
|
||||
'test_javascript_string_escape.html')
|
||||
with open(path, encoding='utf-8') as f:
|
||||
html_source = f.read().replace('%INPUT%', escaped)
|
||||
|
||||
with qtbot.waitSignal(webframe.loadFinished) as blocker:
|
||||
webframe.setHtml(html_source)
|
||||
assert blocker.args == [True]
|
||||
|
||||
result = webframe.evaluateJavaScript('window.qute_test_result')
|
||||
assert result is not None
|
||||
assert '|' in result
|
||||
result_md5, result_text = result.split('|', maxsplit=1)
|
||||
text_md5 = binascii.hexlify(text.encode('utf-8')).decode('ascii')
|
||||
assert result_md5 == text_md5, result_text
|
||||
|
||||
def _test_escape_simple(self, text, webframe):
|
||||
def _test_escape(self, text, webframe):
|
||||
"""Test conversion by using evaluateJavaScript."""
|
||||
escaped = javascript.string_escape(text)
|
||||
result = webframe.evaluateJavaScript('"{}";'.format(escaped))
|
||||
assert result == text
|
||||
|
||||
@pytest.mark.parametrize('text', sorted(TESTS), ids=repr)
|
||||
def test_real_escape(self, webframe, qtbot, text):
|
||||
def test_real_escape(self, webframe, text):
|
||||
"""Test javascript escaping with a real QWebPage."""
|
||||
self._test_escape(text, qtbot, webframe)
|
||||
self._test_escape(text, webframe)
|
||||
|
||||
@pytest.mark.qt_log_ignore('^OpenType support missing for script')
|
||||
@hypothesis.given(hypothesis.strategies.text())
|
||||
def test_real_escape_hypothesis(self, webframe, qtbot, text):
|
||||
def test_real_escape_hypothesis(self, webframe, text):
|
||||
"""Test javascript escaping with a real QWebPage and hypothesis."""
|
||||
# We can't simply use self._test_escape because of this:
|
||||
# https://github.com/pytest-dev/pytest-qt/issues/69
|
||||
|
||||
# self._test_escape(text, qtbot, webframe)
|
||||
try:
|
||||
self._test_escape_simple(text, webframe)
|
||||
except AssertionError:
|
||||
if PYQT_VERSION >= 0x050300:
|
||||
self._test_escape_hexlified(text, qtbot, webframe)
|
||||
self._test_escape(text, webframe)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('arg, expected', [
|
||||
|
@ -1,68 +0,0 @@
|
||||
<!--
|
||||
Helper file for string_escape() in test_javascript.py.
|
||||
|
||||
Since the conversion from QStrings to Python strings is broken in some corner
|
||||
cases in PyQt < 5.4 we hexlify the string we got in javascript here and test
|
||||
that in the test.
|
||||
-->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
|
||||
/*
|
||||
* hexlify() and str2rstr_utf8() are based on:
|
||||
*
|
||||
* JavaScript MD5 1.0.1
|
||||
* https://github.com/blueimp/JavaScript-MD5
|
||||
*
|
||||
* Copyright 2011, Sebastian Tschan
|
||||
* https://blueimp.net
|
||||
*
|
||||
* Licensed under the MIT license:
|
||||
* http://www.opensource.org/licenses/MIT
|
||||
*
|
||||
* Based on
|
||||
* A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
|
||||
* Digest Algorithm, as defined in RFC 1321.
|
||||
* Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
|
||||
* Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
|
||||
* Distributed under the BSD License
|
||||
* See http://pajhome.org.uk/crypt/md5 for more info.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
function hexlify(input) {
|
||||
var hex_tab = '0123456789abcdef';
|
||||
var output = '';
|
||||
var x;
|
||||
var i;
|
||||
|
||||
for (i = 0; i < input.length; i += 1) {
|
||||
x = input.charCodeAt(i);
|
||||
output += hex_tab.charAt((x >>> 4) & 0x0F) + hex_tab.charAt(x & 0x0F);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
function encode_utf8(input) {
|
||||
return unescape(encodeURIComponent(input));
|
||||
}
|
||||
|
||||
function set_text() {
|
||||
var elems = document.getElementsByTagName("p");
|
||||
var hexlified = hexlify(encode_utf8("%INPUT%"));
|
||||
var result = hexlified + "|" + "%INPUT%";
|
||||
elems[0].innerHTML = result
|
||||
window.qute_test_result = result;
|
||||
}
|
||||
|
||||
//]]>
|
||||
</script>
|
||||
</head>
|
||||
<body onload="set_text()">
|
||||
<p>set_text() not called...</p>
|
||||
</html>
|
@ -82,14 +82,14 @@ def test_version_check(monkeypatch, qversion, compiled, version, exact,
|
||||
assert qtutils.version_check(version, exact, strict=strict) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize('version, ng', [
|
||||
@pytest.mark.parametrize('version, is_new', [
|
||||
('537.21', False), # QtWebKit 5.1
|
||||
('538.1', False), # Qt 5.8
|
||||
('602.1', True) # QtWebKit-NG TP5
|
||||
('602.1', True) # new QtWebKit TP5, 5.212 Alpha
|
||||
])
|
||||
def test_is_qtwebkit_ng(monkeypatch, version, ng):
|
||||
def test_is_new_qtwebkit(monkeypatch, version, is_new):
|
||||
monkeypatch.setattr(qtutils, 'qWebKitVersion', lambda: version)
|
||||
assert qtutils.is_qtwebkit_ng() == ng
|
||||
assert qtutils.is_new_qtwebkit() == is_new
|
||||
|
||||
|
||||
class TestCheckOverflow:
|
||||
@ -118,26 +118,6 @@ class TestCheckOverflow:
|
||||
assert newval == repl
|
||||
|
||||
|
||||
@pytest.mark.parametrize('os_name, qversion, expected', [
|
||||
('linux', '5.2.1', True), # unaffected OS
|
||||
('linux', '5.4.1', True), # unaffected OS
|
||||
('nt', '5.2.1', False),
|
||||
('nt', '5.3.0', True), # unaffected Qt version
|
||||
('nt', '5.4.1', True), # unaffected Qt version
|
||||
])
|
||||
def test_check_print_compat(os_name, qversion, expected, monkeypatch):
|
||||
"""Test check_print_compat.
|
||||
|
||||
Args:
|
||||
os_name: The fake os.name to set.
|
||||
qversion: The fake qVersion() to set.
|
||||
expected: The expected return value.
|
||||
"""
|
||||
monkeypatch.setattr(qtutils.os, 'name', os_name)
|
||||
monkeypatch.setattr(qtutils, 'qVersion', lambda: qversion)
|
||||
assert qtutils.check_print_compat() == expected
|
||||
|
||||
|
||||
class QtObject:
|
||||
|
||||
"""Fake Qt object for test_ensure."""
|
||||
|
@ -87,8 +87,6 @@ def test_fake_mac_config(tmpdir, monkeypatch):
|
||||
assert standarddir.config() == expected
|
||||
|
||||
|
||||
# FIXME:conf needs AppDataLocation
|
||||
@pytest.mark.qt55
|
||||
@pytest.mark.parametrize('what', ['data', 'config', 'cache'])
|
||||
@pytest.mark.not_mac
|
||||
def test_fake_windows(tmpdir, monkeypatch, what):
|
||||
@ -354,8 +352,6 @@ class TestSystemData:
|
||||
assert standarddir.data(system=True) == standarddir.data()
|
||||
|
||||
|
||||
# FIXME:conf needs AppDataLocation
|
||||
@pytest.mark.qt55
|
||||
class TestMoveWindowsAndMacOS:
|
||||
|
||||
"""Test other invocations of _move_data."""
|
||||
|
@ -1,54 +0,0 @@
|
||||
# Copyright 2016-2017 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
#
|
||||
# qutebrowser is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# qutebrowser is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Tests for qutebrowser.utils.typing."""
|
||||
|
||||
import pytest
|
||||
|
||||
from qutebrowser.utils import typing
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pytyping():
|
||||
"""A fixture to get the python 3.5+ typing module."""
|
||||
pytyping = pytest.importorskip('typing')
|
||||
return pytyping
|
||||
|
||||
|
||||
class TestUnion:
|
||||
|
||||
def test_python_subclass(self, pytyping):
|
||||
assert (type(pytyping.Union[str, int]) is # flake8: disable=E721
|
||||
type(pytyping.Union))
|
||||
|
||||
def test_qute_subclass(self):
|
||||
assert (type(typing.FakeUnion[str, int]) is # flake8: disable=E721
|
||||
type(typing.FakeUnion))
|
||||
|
||||
def test_python_params(self, pytyping):
|
||||
union = pytyping.Union[str, int]
|
||||
try:
|
||||
assert union.__union_params__ == (str, int)
|
||||
except AttributeError:
|
||||
assert union.__args__ == (str, int)
|
||||
|
||||
def test_qute_params(self):
|
||||
union = typing.FakeUnion[str, int]
|
||||
try:
|
||||
assert union.__union_params__ == (str, int)
|
||||
except AttributeError:
|
||||
assert union.__args__ == (str, int)
|
@ -328,9 +328,8 @@ def test_get_search_url_invalid(url):
|
||||
(False, True, True, 'deadbeef'),
|
||||
(False, True, True, 'hello.'),
|
||||
(False, True, False, 'site:cookies.com oatmeal raisin'),
|
||||
# no DNS because bogus-IP
|
||||
(False, True, False, '31c3'),
|
||||
(False, True, False, 'foo::bar'), # no DNS because of no host
|
||||
# no DNS because there is no host
|
||||
(False, True, False, 'foo::bar'),
|
||||
# Valid search term with autosearch
|
||||
(False, False, False, 'test foo'),
|
||||
# autosearch = False
|
||||
@ -350,11 +349,6 @@ def test_is_url(config_stub, fake_dns,
|
||||
url: The URL to test, as a string.
|
||||
auto_search: With which auto_search setting to test
|
||||
"""
|
||||
if (url == '31c3' and
|
||||
auto_search == 'dns' and
|
||||
qtutils.version_check('5.6.1')):
|
||||
pytest.xfail("Qt behavior changed")
|
||||
|
||||
config_stub.val.url.auto_search = auto_search
|
||||
if auto_search == 'dns':
|
||||
if uses_dns:
|
||||
|
@ -35,7 +35,7 @@ import pkg_resources
|
||||
import pytest
|
||||
|
||||
import qutebrowser
|
||||
from qutebrowser.utils import version, usertypes, qtutils
|
||||
from qutebrowser.utils import version, usertypes
|
||||
from qutebrowser.browser import pdfjs
|
||||
|
||||
|
||||
@ -515,12 +515,10 @@ class ImportFake:
|
||||
('pygments', True),
|
||||
('yaml', True),
|
||||
('cssutils', True),
|
||||
('typing', True),
|
||||
('PyQt5.QtWebEngineWidgets', True),
|
||||
('PyQt5.QtWebKitWidgets', True),
|
||||
])
|
||||
self.no_version_attribute = ['sip', 'typing',
|
||||
'PyQt5.QtWebEngineWidgets',
|
||||
self.no_version_attribute = ['sip', 'PyQt5.QtWebEngineWidgets',
|
||||
'PyQt5.QtWebKitWidgets']
|
||||
self.version_attribute = '__version__'
|
||||
self.version = '1.2.3'
|
||||
@ -588,7 +586,6 @@ class TestModuleVersions:
|
||||
@pytest.mark.parametrize('module, idx, expected', [
|
||||
('colorama', 1, 'colorama: no'),
|
||||
('cssutils', 6, 'cssutils: no'),
|
||||
('typing', 7, 'typing: no'),
|
||||
])
|
||||
def test_missing_module(self, module, idx, expected, import_fake):
|
||||
"""Test with a module missing.
|
||||
@ -839,7 +836,6 @@ class VersionParams:
|
||||
VersionParams('frozen', frozen=True),
|
||||
VersionParams('no-style', style=False),
|
||||
VersionParams('no-webkit', with_webkit=False),
|
||||
VersionParams('webkit-ng', with_webkit='ng'),
|
||||
VersionParams('unknown-dist', known_distribution=False),
|
||||
VersionParams('no-ssl', ssl_support=False),
|
||||
], ids=lambda param: param.name)
|
||||
@ -884,13 +880,7 @@ def test_version_output(params, stubs, monkeypatch):
|
||||
patches['qWebKitVersion'] = lambda: 'WEBKIT VERSION'
|
||||
patches['objects.backend'] = usertypes.Backend.QtWebKit
|
||||
patches['QWebEngineProfile'] = None
|
||||
if params.with_webkit == 'ng':
|
||||
backend = 'QtWebKit-NG'
|
||||
patches['qtutils.is_qtwebkit_ng'] = lambda: True
|
||||
else:
|
||||
backend = 'legacy QtWebKit'
|
||||
patches['qtutils.is_qtwebkit_ng'] = lambda: False
|
||||
substitutions['backend'] = backend + ' (WebKit WEBKIT VERSION)'
|
||||
substitutions['backend'] = 'new QtWebKit (WebKit WEBKIT VERSION)'
|
||||
else:
|
||||
monkeypatch.delattr(version, 'qtutils.qWebKitVersion', raising=False)
|
||||
patches['objects.backend'] = usertypes.Backend.QtWebEngine
|
||||
@ -946,8 +936,6 @@ def test_version_output(params, stubs, monkeypatch):
|
||||
assert version.version() == expected
|
||||
|
||||
|
||||
@pytest.mark.skipif(not qtutils.version_check('5.4'),
|
||||
reason="Needs Qt >= 5.4.")
|
||||
def test_opengl_vendor():
|
||||
"""Simply call version.opengl_vendor() and see if it doesn't crash."""
|
||||
pytest.importorskip("PyQt5.QtOpenGL")
|
||||
|
@ -19,8 +19,6 @@
|
||||
|
||||
"""Tests for usertypes.Question."""
|
||||
|
||||
import logging
|
||||
|
||||
import pytest
|
||||
|
||||
from qutebrowser.utils import usertypes
|
||||
@ -82,15 +80,6 @@ def test_abort(question, qtbot):
|
||||
assert question.is_aborted
|
||||
|
||||
|
||||
def test_abort_typeerror(question, qtbot, mocker, caplog):
|
||||
"""Test Question.abort() with .emit() raising a TypeError."""
|
||||
signal_mock = mocker.patch('qutebrowser.utils.usertypes.Question.aborted')
|
||||
signal_mock.emit.side_effect = TypeError
|
||||
with caplog.at_level(logging.ERROR, 'misc'):
|
||||
question.abort()
|
||||
assert caplog.records[0].message == 'Error while aborting question'
|
||||
|
||||
|
||||
def test_abort_twice(question, qtbot):
|
||||
"""Abort a question twice."""
|
||||
with qtbot.wait_signal(question.aborted):
|
||||
|
Loading…
Reference in New Issue
Block a user