parent
2b4304908a
commit
852baaa8c3
@ -102,8 +102,7 @@ The following software and libraries are required to run qutebrowser:
|
|||||||
* http://www.python.org/[Python] 3.4 or newer (3.6 recommended) - note that
|
* http://www.python.org/[Python] 3.4 or newer (3.6 recommended) - note that
|
||||||
support for Python 3.4
|
support for Python 3.4
|
||||||
https://github.com/qutebrowser/qutebrowser/issues/2742[will be dropped soon].
|
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
|
* http://qt.io/[Qt] 5.7.1 or newer with the following modules:
|
||||||
< 5.7.1 will be dropped soon) with the following modules:
|
|
||||||
- QtCore / qtbase
|
- QtCore / qtbase
|
||||||
- QtQuick (part of qtbase in some distributions)
|
- QtQuick (part of qtbase in some distributions)
|
||||||
- QtSQL (part of qtbase in some distributions)
|
- QtSQL (part of qtbase in some distributions)
|
||||||
@ -111,9 +110,8 @@ The following software and libraries are required to run qutebrowser:
|
|||||||
- QtWebKit (old or link:https://github.com/annulen/webkit/wiki[reloaded]/NG).
|
- QtWebKit (old or link:https://github.com/annulen/webkit/wiki[reloaded]/NG).
|
||||||
Note that support for legacy QtWebKit (before 5.212) will be
|
Note that support for legacy QtWebKit (before 5.212) will be
|
||||||
dropped soon.
|
dropped soon.
|
||||||
* http://www.riverbankcomputing.com/software/pyqt/intro[PyQt] 5.2.0 or newer
|
* http://www.riverbankcomputing.com/software/pyqt/intro[PyQt] 5.7.0 or newer
|
||||||
(5.9 recommended) for Python 3. Note that support for PyQt < 5.7 will be
|
(5.9 recommended) for Python 3.
|
||||||
dropped soon.
|
|
||||||
* https://pypi.python.org/pypi/setuptools/[pkg_resources/setuptools]
|
* https://pypi.python.org/pypi/setuptools/[pkg_resources/setuptools]
|
||||||
* http://fdik.org/pyPEG/[pyPEG2]
|
* http://fdik.org/pyPEG/[pyPEG2]
|
||||||
* http://jinja.pocoo.org/[jinja2]
|
* http://jinja.pocoo.org/[jinja2]
|
||||||
|
@ -23,7 +23,7 @@ Breaking changes
|
|||||||
- (TODO) Support for legacy QtWebKit (before 5.212 which is distributed
|
- (TODO) Support for legacy QtWebKit (before 5.212 which is distributed
|
||||||
independently from Qt) is dropped.
|
independently from Qt) is dropped.
|
||||||
- (TODO) Support for Python 3.4 is dropped.
|
- (TODO) Support for Python 3.4 is dropped.
|
||||||
- (TODO) Support for Qt before 5.7 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) New dependency on ruamel.yaml; dropped PyYAML dependency.
|
||||||
- (TODO) The QtWebEngine backend is now used by default if available.
|
- (TODO) The QtWebEngine backend is now used by default if available.
|
||||||
- New dependency on the QtSql module and Qt sqlite support.
|
- New dependency on the QtSql module and Qt sqlite support.
|
||||||
@ -36,6 +36,7 @@ Breaking changes
|
|||||||
work properly anymore.
|
work properly anymore.
|
||||||
- Various documentation files got moved to the doc/ subfolder,
|
- Various documentation files got moved to the doc/ subfolder,
|
||||||
`qutebrowser.desktop` got moved to misc/.
|
`qutebrowser.desktop` got moved to misc/.
|
||||||
|
- The `--harfbuzz` commandline argument got dropped
|
||||||
|
|
||||||
Major changes
|
Major changes
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
@ -205,26 +205,6 @@ Experiencing freezing on sites like duckduckgo and youtube.::
|
|||||||
See https://github.com/qutebrowser/qutebrowser/issues/357[Issue #357]
|
See https://github.com/qutebrowser/qutebrowser/issues/357[Issue #357]
|
||||||
for more details.
|
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::
|
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 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. +
|
As a workaround, you can disable the nullpointer check optimization by adding the -fno-delete-null-pointer-checks flag while compiling. +
|
||||||
|
@ -3,39 +3,50 @@ Installing qutebrowser
|
|||||||
|
|
||||||
toc::[]
|
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
|
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
|
Debian Jessie / Ubuntu 14.04 LTS / Linux Mint < 18
|
||||||
* Ubuntu Trusty (14.04 LTS) or newer
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
* Any other distribution based on these (e.g. Linux Mint 17+)
|
|
||||||
|
|
||||||
Unfortunately there is no Debian package in the official repos yet, but installing qutebrowser is
|
Those distributions only have Python 3.4 and a too old Qt version available. A
|
||||||
still relatively easy!
|
newer Qt isn't easily installable on Python 3.4, unfortunately.
|
||||||
|
|
||||||
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>>
|
If you get qutebrowser running on those distributions, please
|
||||||
instead in order to be able to use the new QtWebEngine backend. Newer versions
|
https://github.com/qutebrowser/qutebrowser/blob/master/doc/contributing.asciidoc[contribute]
|
||||||
have a QtWebEngine package in the repositories.
|
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:
|
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
|
Get the qutebrowser package from the
|
||||||
https://github.com/qutebrowser/qutebrowser/releases[release page] and download
|
https://github.com/qutebrowser/qutebrowser/releases[release page] and download
|
||||||
the https://qutebrowser.org/python3-pypeg2_2.15.2-1_all.deb[PyPEG2 package].
|
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
|
# dpkg -i qutebrowser_*_all.deb
|
||||||
----
|
----
|
||||||
|
|
||||||
Build it from git
|
Some additional hints:
|
||||||
~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Install the dependencies via apt-get:
|
|
||||||
|
|
||||||
|
- 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
|
# apt-get install --no-install-recommends asciidoc source-highlight
|
||||||
----
|
|
||||||
|
|
||||||
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
|
|
||||||
$ python3 scripts/asciidoc2html.py
|
$ 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}
|
# apt-get install gstreamer1.0-plugins-{bad,base,good,ugly}
|
||||||
----
|
----
|
||||||
|
|
||||||
Then <<tox,install qutebrowser via tox>>.
|
|
||||||
|
|
||||||
On Fedora
|
On Fedora
|
||||||
---------
|
---------
|
||||||
|
|
||||||
@ -116,7 +119,7 @@ $ rm -r qutebrowser-git
|
|||||||
|
|
||||||
or you could use an AUR helper, e.g. `yaourt -S 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
|
# 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
|
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:
|
A version of qutebrowser is available in the main repository and can be installed with:
|
||||||
|
|
||||||
----
|
----
|
||||||
|
@ -84,9 +84,6 @@ show it.
|
|||||||
*--force-color*::
|
*--force-color*::
|
||||||
Force colored logging
|
Force colored logging
|
||||||
|
|
||||||
*--harfbuzz* '{old,new,system,auto}'::
|
|
||||||
HarfBuzz engine version to use. Default: auto.
|
|
||||||
|
|
||||||
*--relaxed-config*::
|
*--relaxed-config*::
|
||||||
Silently remove unknown config options.
|
Silently remove unknown config options.
|
||||||
|
|
||||||
|
@ -27,7 +27,6 @@ markers =
|
|||||||
this: Used to mark tests during development
|
this: Used to mark tests during development
|
||||||
no_invalid_lines: Don't fail on unparseable lines in end2end tests
|
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
|
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_level_fail = WARNING
|
||||||
qt_log_ignore =
|
qt_log_ignore =
|
||||||
^SpellCheck: .*
|
^SpellCheck: .*
|
||||||
|
@ -19,9 +19,6 @@
|
|||||||
|
|
||||||
"""The ListView to display downloads in."""
|
"""The ListView to display downloads in."""
|
||||||
|
|
||||||
import functools
|
|
||||||
|
|
||||||
import sip
|
|
||||||
from PyQt5.QtCore import pyqtSlot, QSize, Qt, QTimer
|
from PyQt5.QtCore import pyqtSlot, QSize, Qt, QTimer
|
||||||
from PyQt5.QtWidgets import QListView, QSizePolicy, QMenu, QStyleFactory
|
from PyQt5.QtWidgets import QListView, QSizePolicy, QMenu, QStyleFactory
|
||||||
|
|
||||||
@ -30,29 +27,6 @@ from qutebrowser.config import config
|
|||||||
from qutebrowser.utils import qtutils, utils, objreg
|
from qutebrowser.utils import qtutils, utils, objreg
|
||||||
|
|
||||||
|
|
||||||
def update_geometry(obj):
|
|
||||||
"""Weird WORKAROUND for some weird PyQt bug (probably).
|
|
||||||
|
|
||||||
This actually should be a method of DownloadView, but for some reason the
|
|
||||||
rowsInserted/rowsRemoved signals don't get disconnected from this method
|
|
||||||
when the DownloadView is deleted from Qt (e.g. by closing a window).
|
|
||||||
|
|
||||||
Here we check if obj ("self") was deleted and just ignore the event if so.
|
|
||||||
|
|
||||||
Original bug: https://github.com/qutebrowser/qutebrowser/issues/167
|
|
||||||
Workaround bug: https://github.com/qutebrowser/qutebrowser/issues/171
|
|
||||||
"""
|
|
||||||
def _update_geometry():
|
|
||||||
"""Actually update the geometry if the object still exists."""
|
|
||||||
if sip.isdeleted(obj):
|
|
||||||
return
|
|
||||||
obj.updateGeometry()
|
|
||||||
|
|
||||||
# If we don't use a singleShot QTimer, the geometry isn't updated correctly
|
|
||||||
# and won't include the new item.
|
|
||||||
QTimer.singleShot(0, _update_geometry)
|
|
||||||
|
|
||||||
|
|
||||||
class DownloadView(QListView):
|
class DownloadView(QListView):
|
||||||
|
|
||||||
"""QListView which shows currently running downloads as a bar.
|
"""QListView which shows currently running downloads as a bar.
|
||||||
@ -85,9 +59,12 @@ class DownloadView(QListView):
|
|||||||
self.setSpacing(1)
|
self.setSpacing(1)
|
||||||
self._menu = None
|
self._menu = None
|
||||||
model = objreg.get('download-model', scope='window', window=win_id)
|
model = objreg.get('download-model', scope='window', window=win_id)
|
||||||
model.rowsInserted.connect(functools.partial(update_geometry, self))
|
model.rowsInserted.connect(lambda:
|
||||||
model.rowsRemoved.connect(functools.partial(update_geometry, self))
|
QTimer.singleShot(0, self.updateGeometry))
|
||||||
model.dataChanged.connect(functools.partial(update_geometry, self))
|
model.rowsRemoved.connect(lambda:
|
||||||
|
QTimer.singleShot(0, self.updateGeometry))
|
||||||
|
model.dataChanged.connect(lambda:
|
||||||
|
QTimer.singleShot(0, self.updateGeometry))
|
||||||
self.setModel(model)
|
self.setModel(model)
|
||||||
self.setWrapping(True)
|
self.setWrapping(True)
|
||||||
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||||
|
@ -49,7 +49,6 @@ class DiskCache(QNetworkDiskCache):
|
|||||||
if size is None:
|
if size is None:
|
||||||
size = 1024 * 1024 * 50 # default from QNetworkDiskCachePrivate
|
size = 1024 * 1024 * 50 # default from QNetworkDiskCachePrivate
|
||||||
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-59909
|
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-59909
|
||||||
if (qtutils.version_check('5.7.1') and
|
if not qtutils.version_check('5.9'): # pragma: no cover
|
||||||
not qtutils.version_check('5.9')): # pragma: no cover
|
|
||||||
size = 0
|
size = 0
|
||||||
self.setMaximumCacheSize(size)
|
self.setMaximumCacheSize(size)
|
||||||
|
@ -37,14 +37,6 @@ class CertificateErrorWrapper(usertypes.AbstractCertificateErrorWrapper):
|
|||||||
self, error=debug.qenum_key(QSslError, self._error.error()),
|
self, error=debug.qenum_key(QSslError, self._error.error()),
|
||||||
string=str(self))
|
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):
|
def __eq__(self, other):
|
||||||
return self._error == other._error # pylint: disable=protected-access
|
return self._error == other._error # pylint: disable=protected-access
|
||||||
|
|
||||||
|
@ -24,13 +24,12 @@ import collections
|
|||||||
import netrc
|
import netrc
|
||||||
import html
|
import html
|
||||||
|
|
||||||
from PyQt5.QtCore import (pyqtSlot, pyqtSignal, PYQT_VERSION, QCoreApplication,
|
from PyQt5.QtCore import (pyqtSlot, pyqtSignal, QCoreApplication, QUrl,
|
||||||
QUrl, QByteArray)
|
QByteArray)
|
||||||
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply, QSslSocket
|
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply, QSslSocket
|
||||||
|
|
||||||
from qutebrowser.config import config
|
from qutebrowser.config import config
|
||||||
from qutebrowser.utils import (message, log, usertypes, utils, objreg, qtutils,
|
from qutebrowser.utils import message, log, usertypes, utils, objreg, urlutils
|
||||||
urlutils)
|
|
||||||
from qutebrowser.browser import shared
|
from qutebrowser.browser import shared
|
||||||
from qutebrowser.browser.webkit import certificateerror
|
from qutebrowser.browser.webkit import certificateerror
|
||||||
from qutebrowser.browser.webkit.network import (webkitqutescheme, networkreply,
|
from qutebrowser.browser.webkit.network import (webkitqutescheme, networkreply,
|
||||||
@ -88,15 +87,9 @@ def _is_secure_cipher(cipher):
|
|||||||
|
|
||||||
def init():
|
def init():
|
||||||
"""Disable insecure SSL ciphers on old Qt versions."""
|
"""Disable insecure SSL ciphers on old Qt versions."""
|
||||||
if qtutils.version_check('5.3.0'):
|
default_ciphers = QSslSocket.defaultCiphers()
|
||||||
default_ciphers = QSslSocket.defaultCiphers()
|
log.init.debug("Default Qt ciphers: {}".format(
|
||||||
log.init.debug("Default Qt ciphers: {}".format(
|
', '.join(c.name() for c in default_ciphers)))
|
||||||
', '.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 = []
|
good_ciphers = []
|
||||||
bad_ciphers = []
|
bad_ciphers = []
|
||||||
@ -409,24 +402,11 @@ class NetworkManager(QNetworkAccessManager):
|
|||||||
tab = objreg.get('tab', scope='tab', window=self._win_id,
|
tab = objreg.get('tab', scope='tab', window=self._win_id,
|
||||||
tab=self._tab_id)
|
tab=self._tab_id)
|
||||||
current_url = tab.url()
|
current_url = tab.url()
|
||||||
except (KeyError, RuntimeError, TypeError):
|
except (KeyError, RuntimeError):
|
||||||
# https://github.com/qutebrowser/qutebrowser/issues/889
|
# https://github.com/qutebrowser/qutebrowser/issues/889
|
||||||
# Catching RuntimeError and TypeError because we could be in
|
# Catching RuntimeError because we could be in the middle of the
|
||||||
# the middle of the webpage shutdown here.
|
# webpage shutdown here.
|
||||||
current_url = QUrl()
|
current_url = QUrl()
|
||||||
|
|
||||||
self.set_referer(req, current_url)
|
self.set_referer(req, current_url)
|
||||||
|
return super().createRequest(op, req, outgoing_data)
|
||||||
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
|
|
||||||
|
@ -292,9 +292,6 @@ class WebKitElement(webelem.AbstractWebElement):
|
|||||||
elem = elem._parent() # pylint: disable=protected-access
|
elem = elem._parent() # pylint: disable=protected-access
|
||||||
|
|
||||||
def _move_text_cursor(self):
|
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():
|
if self.is_text_input() and self.is_editable():
|
||||||
self._tab.caret.move_to_end_of_document()
|
self._tab.caret.move_to_end_of_document()
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ from PyQt5.QtGui import QFont
|
|||||||
from PyQt5.QtWebKit import QWebSettings
|
from PyQt5.QtWebKit import QWebSettings
|
||||||
|
|
||||||
from qutebrowser.config import config, websettings
|
from qutebrowser.config import config, websettings
|
||||||
from qutebrowser.utils import standarddir, urlutils, qtutils
|
from qutebrowser.utils import standarddir, urlutils
|
||||||
from qutebrowser.browser import shared
|
from qutebrowser.browser import shared
|
||||||
|
|
||||||
|
|
||||||
@ -131,13 +131,6 @@ def init(_args):
|
|||||||
QWebSettings.setOfflineStoragePath(
|
QWebSettings.setOfflineStoragePath(
|
||||||
os.path.join(data_path, 'offline-storage'))
|
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)
|
websettings.init_mappings(MAPPINGS)
|
||||||
_set_user_stylesheet()
|
_set_user_stylesheet()
|
||||||
config.instance.changed.connect(_update_settings)
|
config.instance.changed.connect(_update_settings)
|
||||||
|
@ -55,20 +55,14 @@ class WebKitPrinting(browsertab.AbstractPrinting):
|
|||||||
|
|
||||||
"""QtWebKit implementations related to printing."""
|
"""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):
|
def check_pdf_support(self):
|
||||||
self._do_check()
|
pass
|
||||||
|
|
||||||
def check_printer_support(self):
|
def check_printer_support(self):
|
||||||
self._do_check()
|
pass
|
||||||
|
|
||||||
def check_preview_support(self):
|
def check_preview_support(self):
|
||||||
self._do_check()
|
pass
|
||||||
|
|
||||||
def to_pdf(self, filename):
|
def to_pdf(self, filename):
|
||||||
printer = QPrinter()
|
printer = QPrinter()
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
import html
|
import html
|
||||||
import functools
|
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.QtGui import QDesktopServices
|
||||||
from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest
|
from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest
|
||||||
from PyQt5.QtWidgets import QFileDialog
|
from PyQt5.QtWidgets import QFileDialog
|
||||||
@ -33,8 +33,8 @@ from qutebrowser.config import config
|
|||||||
from qutebrowser.browser import pdfjs, shared
|
from qutebrowser.browser import pdfjs, shared
|
||||||
from qutebrowser.browser.webkit import http
|
from qutebrowser.browser.webkit import http
|
||||||
from qutebrowser.browser.webkit.network import networkmanager
|
from qutebrowser.browser.webkit.network import networkmanager
|
||||||
from qutebrowser.utils import (message, usertypes, log, jinja, qtutils, utils,
|
from qutebrowser.utils import (message, usertypes, log, jinja, objreg, debug,
|
||||||
objreg, debug, urlutils)
|
urlutils)
|
||||||
|
|
||||||
|
|
||||||
class BrowserPage(QWebPage):
|
class BrowserPage(QWebPage):
|
||||||
@ -87,22 +87,16 @@ class BrowserPage(QWebPage):
|
|||||||
self.restoreFrameStateRequested.connect(
|
self.restoreFrameStateRequested.connect(
|
||||||
self.on_restore_frame_state_requested)
|
self.on_restore_frame_state_requested)
|
||||||
|
|
||||||
if PYQT_VERSION > 0x050300:
|
def javaScriptPrompt(self, frame, js_msg, default):
|
||||||
# WORKAROUND (remove this when we bump the requirements to 5.3.1)
|
"""Override javaScriptPrompt to use qutebrowser prompts."""
|
||||||
# We can't override javaScriptPrompt with older PyQt-versions because
|
if self._is_shutting_down:
|
||||||
# of a bug in PyQt.
|
return (False, "")
|
||||||
# See http://www.riverbankcomputing.com/pipermail/pyqt/2014-June/034385.html
|
try:
|
||||||
|
return shared.javascript_prompt(frame.url(), js_msg, default,
|
||||||
def javaScriptPrompt(self, frame, js_msg, default):
|
abort_on=[self.loadStarted,
|
||||||
"""Override javaScriptPrompt to use qutebrowser prompts."""
|
self.shutting_down])
|
||||||
if self._is_shutting_down:
|
except shared.CallSuper:
|
||||||
return (False, "")
|
return super().javaScriptPrompt(frame, js_msg, default)
|
||||||
try:
|
|
||||||
return shared.javascript_prompt(frame.url(), js_msg, default,
|
|
||||||
abort_on=[self.loadStarted,
|
|
||||||
self.shutting_down])
|
|
||||||
except shared.CallSuper:
|
|
||||||
return super().javaScriptPrompt(frame, js_msg, default)
|
|
||||||
|
|
||||||
def _handle_errorpage(self, info, errpage):
|
def _handle_errorpage(self, info, errpage):
|
||||||
"""Display an error page if needed.
|
"""Display an error page if needed.
|
||||||
@ -225,10 +219,6 @@ class BrowserPage(QWebPage):
|
|||||||
|
|
||||||
def on_print_requested(self, frame):
|
def on_print_requested(self, frame):
|
||||||
"""Handle printing when requested via javascript."""
|
"""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 = QPrintDialog()
|
||||||
printdiag.setAttribute(Qt.WA_DeleteOnClose)
|
printdiag.setAttribute(Qt.WA_DeleteOnClose)
|
||||||
printdiag.open(lambda: frame.print(printdiag.printer()))
|
printdiag.open(lambda: frame.print(printdiag.printer()))
|
||||||
@ -350,15 +340,7 @@ class BrowserPage(QWebPage):
|
|||||||
frame: The QWebFrame which gets saved.
|
frame: The QWebFrame which gets saved.
|
||||||
item: The QWebHistoryItem to be saved.
|
item: The QWebHistoryItem to be saved.
|
||||||
"""
|
"""
|
||||||
try:
|
if frame != self.mainFrame():
|
||||||
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
|
return
|
||||||
data = {
|
data = {
|
||||||
'zoom': frame.zoomFactor(),
|
'zoom': frame.zoomFactor(),
|
||||||
@ -401,9 +383,6 @@ class BrowserPage(QWebPage):
|
|||||||
"""
|
"""
|
||||||
return ext in self._extension_handlers
|
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):
|
def extension(self, ext, opt, out):
|
||||||
"""Override QWebPage::extension to provide error pages.
|
"""Override QWebPage::extension to provide error pages.
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ from PyQt5.QtWebKitWidgets import QWebView, QWebPage
|
|||||||
|
|
||||||
from qutebrowser.config import config
|
from qutebrowser.config import config
|
||||||
from qutebrowser.keyinput import modeman
|
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
|
from qutebrowser.browser.webkit import webpage
|
||||||
|
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ class WebView(QWebView):
|
|||||||
|
|
||||||
def __init__(self, *, win_id, tab_id, tab, private, parent=None):
|
def __init__(self, *, win_id, tab_id, tab, private, parent=None):
|
||||||
super().__init__(parent)
|
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
|
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-42948
|
||||||
# See https://github.com/qutebrowser/qutebrowser/issues/462
|
# See https://github.com/qutebrowser/qutebrowser/issues/462
|
||||||
self.setStyle(QStyleFactory.create('Fusion'))
|
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,
|
page = webpage.BrowserPage(win_id=self.win_id, tab_id=self._tab_id,
|
||||||
tabdata=tab.data, private=private,
|
tabdata=tab.data, private=private,
|
||||||
parent=self)
|
parent=self)
|
||||||
|
page.setVisibilityState(
|
||||||
try:
|
QWebPage.VisibilityStateVisible if self.isVisible()
|
||||||
page.setVisibilityState(
|
else QWebPage.VisibilityStateHidden)
|
||||||
QWebPage.VisibilityStateVisible if self.isVisible()
|
|
||||||
else QWebPage.VisibilityStateHidden)
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
self.setPage(page)
|
self.setPage(page)
|
||||||
|
|
||||||
@ -240,12 +236,8 @@ class WebView(QWebView):
|
|||||||
Return:
|
Return:
|
||||||
The superclass event return value.
|
The superclass event return value.
|
||||||
"""
|
"""
|
||||||
try:
|
|
||||||
self.page().setVisibilityState(QWebPage.VisibilityStateVisible)
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
super().showEvent(e)
|
super().showEvent(e)
|
||||||
|
self.page().setVisibilityState(QWebPage.VisibilityStateVisible)
|
||||||
|
|
||||||
def hideEvent(self, e):
|
def hideEvent(self, e):
|
||||||
"""Extend hideEvent to set the page visibility state to hidden.
|
"""Extend hideEvent to set the page visibility state to hidden.
|
||||||
@ -256,12 +248,8 @@ class WebView(QWebView):
|
|||||||
Return:
|
Return:
|
||||||
The superclass event return value.
|
The superclass event return value.
|
||||||
"""
|
"""
|
||||||
try:
|
|
||||||
self.page().setVisibilityState(QWebPage.VisibilityStateHidden)
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
super().hideEvent(e)
|
super().hideEvent(e)
|
||||||
|
self.page().setVisibilityState(QWebPage.VisibilityStateHidden)
|
||||||
|
|
||||||
def mousePressEvent(self, e):
|
def mousePressEvent(self, e):
|
||||||
"""Set the tabdata ClickTarget on a mousepress.
|
"""Set the tabdata ClickTarget on a mousepress.
|
||||||
|
@ -61,10 +61,6 @@ class Progress(QProgressBar):
|
|||||||
|
|
||||||
def on_tab_changed(self, tab):
|
def on_tab_changed(self, tab):
|
||||||
"""Set the correct value when the current tab changed."""
|
"""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())
|
self.setValue(tab.progress())
|
||||||
if tab.load_status() == usertypes.LoadStatus.loading:
|
if tab.load_status() == usertypes.LoadStatus.loading:
|
||||||
self.show()
|
self.show()
|
||||||
|
@ -70,9 +70,6 @@ class TabWidget(QTabWidget):
|
|||||||
@config.change_filter('tabs')
|
@config.change_filter('tabs')
|
||||||
def _init_config(self):
|
def _init_config(self):
|
||||||
"""Initialize attributes based on the config."""
|
"""Initialize attributes based on the config."""
|
||||||
if self is None: # pragma: no cover
|
|
||||||
# WORKAROUND for PyQt 5.2
|
|
||||||
return
|
|
||||||
tabbar = self.tabBar()
|
tabbar = self.tabBar()
|
||||||
self.setMovable(True)
|
self.setMovable(True)
|
||||||
self.setTabsClosable(False)
|
self.setTabsClosable(False)
|
||||||
@ -744,24 +741,17 @@ class TabBarStyle(QCommonStyle):
|
|||||||
log.misc.warning("Could not get layouts for tab!")
|
log.misc.warning("Could not get layouts for tab!")
|
||||||
return QRect()
|
return QRect()
|
||||||
return layouts.text
|
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
|
# Need to use super() because we also use super() to render
|
||||||
# element in drawControl(); otherwise, we may get bit by
|
# element in drawControl(); otherwise, we may get bit by
|
||||||
# style differences...
|
# style differences...
|
||||||
rct = super().subElementRect(sr, opt, widget)
|
return super().subElementRect(sr, opt, widget)
|
||||||
return rct
|
|
||||||
else:
|
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
|
|
||||||
|
|
||||||
return self._style.subElementRect(sr, opt, widget)
|
return self._style.subElementRect(sr, opt, widget)
|
||||||
|
|
||||||
def _tab_layout(self, opt):
|
def _tab_layout(self, opt):
|
||||||
|
@ -29,7 +29,6 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
hunter = None
|
hunter = None
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
import sys
|
||||||
import faulthandler
|
import faulthandler
|
||||||
import traceback
|
import traceback
|
||||||
@ -41,8 +40,6 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
tkinter = None
|
tkinter = None
|
||||||
|
|
||||||
import pkg_resources
|
|
||||||
|
|
||||||
# NOTE: No qutebrowser or PyQt import should be done here, as some early
|
# NOTE: No qutebrowser or PyQt import should be done here, as some early
|
||||||
# initialization needs to take place before that!
|
# initialization needs to take place before that!
|
||||||
|
|
||||||
@ -140,73 +137,6 @@ def init_faulthandler(fileobj=sys.__stderr__):
|
|||||||
faulthandler.register(signal.SIGUSR1)
|
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():
|
def check_pyqt_core():
|
||||||
"""Check if PyQt core is installed."""
|
"""Check if PyQt core is installed."""
|
||||||
try:
|
try:
|
||||||
@ -268,22 +198,15 @@ def qt_version(qversion=None, qt_version_str=None):
|
|||||||
return qversion
|
return qversion
|
||||||
|
|
||||||
|
|
||||||
def check_qt_version(backend):
|
def check_qt_version():
|
||||||
"""Check if the Qt version is recent enough."""
|
"""Check if the Qt version is recent enough."""
|
||||||
from PyQt5.QtCore import PYQT_VERSION, PYQT_VERSION_STR
|
from PyQt5.QtCore import PYQT_VERSION, PYQT_VERSION_STR
|
||||||
from qutebrowser.utils import qtutils
|
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):
|
PYQT_VERSION < 0x050200):
|
||||||
text = ("Fatal error: Qt and PyQt >= 5.2.0 are required, but Qt {} / "
|
text = ("Fatal error: Qt >= 5.7.1 and PyQt >= 5.7 are required, "
|
||||||
"PyQt {} is installed.".format(qt_version(),
|
"but Qt {} / PyQt {} is installed.".format(qt_version(),
|
||||||
PYQT_VERSION_STR))
|
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)
|
_die(text)
|
||||||
|
|
||||||
|
|
||||||
@ -423,13 +346,10 @@ def earlyinit(args):
|
|||||||
check_pyqt_core()
|
check_pyqt_core()
|
||||||
# Init logging as early as possible
|
# Init logging as early as possible
|
||||||
init_log(args)
|
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
|
# 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.
|
# errors, so people only using the GUI notice them as well.
|
||||||
backend = get_backend(args)
|
backend = get_backend(args)
|
||||||
check_qt_version(backend)
|
check_qt_version()
|
||||||
remove_inputhook()
|
remove_inputhook()
|
||||||
check_libraries(backend)
|
check_libraries(backend)
|
||||||
check_ssl_support(backend)
|
check_ssl_support(backend)
|
||||||
|
@ -139,8 +139,6 @@ class IPCServer(QObject):
|
|||||||
_server: A QLocalServer to accept new connections.
|
_server: A QLocalServer to accept new connections.
|
||||||
_socket: The QLocalSocket we're currently connected to.
|
_socket: The QLocalSocket we're currently connected to.
|
||||||
_socketname: The socketname to use.
|
_socketname: The socketname to use.
|
||||||
_socketopts_ok: Set if using setSocketOptions is working with this
|
|
||||||
OS/Qt version.
|
|
||||||
_atime_timer: Timer to update the atime of the socket regularly.
|
_atime_timer: Timer to update the atime of the socket regularly.
|
||||||
|
|
||||||
Signals:
|
Signals:
|
||||||
@ -182,14 +180,6 @@ class IPCServer(QObject):
|
|||||||
|
|
||||||
self._socket = None
|
self._socket = None
|
||||||
self._old_socket = None
|
self._old_socket = None
|
||||||
self._socketopts_ok = os.name == 'nt'
|
|
||||||
if self._socketopts_ok: # pragma: no cover
|
|
||||||
# If we use setSocketOptions on Unix with Qt < 5.4, we get a
|
|
||||||
# NameError while listening...
|
|
||||||
log.ipc.debug("Calling setSocketOptions")
|
|
||||||
self._server.setSocketOptions(QLocalServer.UserAccessOption)
|
|
||||||
else: # pragma: no cover
|
|
||||||
log.ipc.debug("Not calling setSocketOptions")
|
|
||||||
|
|
||||||
def _remove_server(self):
|
def _remove_server(self):
|
||||||
"""Remove an existing server."""
|
"""Remove an existing server."""
|
||||||
@ -210,22 +200,21 @@ class IPCServer(QObject):
|
|||||||
raise AddressInUseError(self._server)
|
raise AddressInUseError(self._server)
|
||||||
else:
|
else:
|
||||||
raise ListenError(self._server)
|
raise ListenError(self._server)
|
||||||
if not self._socketopts_ok: # pragma: no cover
|
|
||||||
# If we use setSocketOptions on Unix with Qt < 5.4, we get a
|
# If we use setSocketOptions on Unix with Qt < 5.4, we get a NameError
|
||||||
# NameError while listening.
|
# while listening. (see b135569d5c6e68c735ea83f42e4baf51f7972281)
|
||||||
# (see b135569d5c6e68c735ea83f42e4baf51f7972281)
|
#
|
||||||
#
|
# Also, we don't get an AddressInUseError with Qt 5.5:
|
||||||
# Also, we don't get an AddressInUseError with Qt 5.5:
|
# https://bugreports.qt.io/browse/QTBUG-48635
|
||||||
# https://bugreports.qt.io/browse/QTBUG-48635
|
#
|
||||||
#
|
# This means we don't use it at all.
|
||||||
# This means we only use setSocketOption on Windows...
|
try:
|
||||||
try:
|
os.chmod(self._server.fullServerName(), 0o700)
|
||||||
os.chmod(self._server.fullServerName(), 0o700)
|
except FileNotFoundError:
|
||||||
except FileNotFoundError:
|
# https://github.com/qutebrowser/qutebrowser/issues/1530
|
||||||
# https://github.com/qutebrowser/qutebrowser/issues/1530
|
# The server doesn't actually exist even if ok was reported as
|
||||||
# The server doesn't actually exist even if ok was reported as
|
# True, so report this as an error.
|
||||||
# True, so report this as an error.
|
raise ListenError(self._server)
|
||||||
raise ListenError(self._server)
|
|
||||||
|
|
||||||
@pyqtSlot('QLocalSocket::LocalSocketError')
|
@pyqtSlot('QLocalSocket::LocalSocketError')
|
||||||
def on_error(self, err):
|
def on_error(self, err):
|
||||||
|
@ -95,9 +95,6 @@ def get_argparser():
|
|||||||
action='store_false', dest='color')
|
action='store_false', dest='color')
|
||||||
debug.add_argument('--force-color', help="Force colored logging",
|
debug.add_argument('--force-color', help="Force colored logging",
|
||||||
action='store_true')
|
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',
|
debug.add_argument('--relaxed-config', action='store_true',
|
||||||
help="Silently remove unknown config options.")
|
help="Silently remove unknown config options.")
|
||||||
debug.add_argument('--nowindow', action='store_true', help="Don't show "
|
debug.add_argument('--nowindow', action='store_true', help="Don't show "
|
||||||
@ -172,6 +169,6 @@ def main():
|
|||||||
args = argparse.Namespace(**data)
|
args = argparse.Namespace(**data)
|
||||||
earlyinit.earlyinit(args)
|
earlyinit.earlyinit(args)
|
||||||
# We do this imports late as earlyinit needs to be run first (because of
|
# 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
|
from qutebrowser import app
|
||||||
return app.run(args)
|
return app.run(args)
|
||||||
|
@ -345,7 +345,7 @@ def qt_message_handler(msg_type, context, msg):
|
|||||||
try:
|
try:
|
||||||
qt_to_logging[QtCore.QtInfoMsg] = logging.INFO
|
qt_to_logging[QtCore.QtInfoMsg] = logging.INFO
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# Qt < 5.5
|
# While we don't support Qt < 5.5 anymore, logging still needs to work
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Change levels of some well-known messages to debug so they don't get
|
# 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]
|
func = partial_objs[name]
|
||||||
try:
|
try:
|
||||||
self[name].destroyed.disconnect(func)
|
self[name].destroyed.disconnect(func)
|
||||||
except (RuntimeError, TypeError):
|
except RuntimeError:
|
||||||
# If C++ has deleted the object, the slot is already
|
# If C++ has deleted the object, the slot is already
|
||||||
# disconnected.
|
# 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
|
pass
|
||||||
del partial_objs[name]
|
del partial_objs[name]
|
||||||
|
|
||||||
@ -145,7 +141,7 @@ class ObjectRegistry(collections.UserDict):
|
|||||||
for name, obj in self.data.items():
|
for name, obj in self.data.items():
|
||||||
try:
|
try:
|
||||||
obj_repr = repr(obj)
|
obj_repr = repr(obj)
|
||||||
except (RuntimeError, TypeError):
|
except RuntimeError:
|
||||||
# Underlying object deleted probably
|
# Underlying object deleted probably
|
||||||
obj_repr = '<deleted>'
|
obj_repr = '<deleted>'
|
||||||
lines.append("{}: {}".format(name, obj_repr))
|
lines.append("{}: {}".format(name, obj_repr))
|
||||||
|
@ -28,7 +28,6 @@ Module attributes:
|
|||||||
|
|
||||||
|
|
||||||
import io
|
import io
|
||||||
import os
|
|
||||||
import operator
|
import operator
|
||||||
import contextlib
|
import contextlib
|
||||||
|
|
||||||
@ -137,15 +136,6 @@ def check_overflow(arg, ctype, fatal=True):
|
|||||||
return arg
|
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):
|
def ensure_valid(obj):
|
||||||
"""Ensure a Qt object with an .isValid() method is valid."""
|
"""Ensure a Qt object with an .isValid() method is valid."""
|
||||||
if not obj.isValid():
|
if not obj.isValid():
|
||||||
|
@ -355,14 +355,8 @@ class Question(QObject):
|
|||||||
log.misc.debug("Question was already aborted")
|
log.misc.debug("Question was already aborted")
|
||||||
return
|
return
|
||||||
self.is_aborted = True
|
self.is_aborted = True
|
||||||
try:
|
self.aborted.emit()
|
||||||
self.aborted.emit()
|
self.completed.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):
|
class Timer(QTimer):
|
||||||
|
@ -62,7 +62,6 @@ def _apply_platform_markers(config, item):
|
|||||||
('no_ci', 'CI' in os.environ, "Skipped on CI."),
|
('no_ci', 'CI' in os.environ, "Skipped on CI."),
|
||||||
('issue2478', os.name == 'nt' and config.webengine,
|
('issue2478', os.name == 'nt' and config.webengine,
|
||||||
"Broken with QtWebEngine on Windows"),
|
"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:
|
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))
|
item.add_marker(pytest.mark.xfail(run=False))
|
||||||
if item.get_marker('js_prompt'):
|
if item.get_marker('js_prompt'):
|
||||||
if config.webengine:
|
if config.webengine:
|
||||||
js_prompt_pyqt_version = 0x050700
|
item.add_marker(pytest.mark.skipif(
|
||||||
else:
|
PYQT_VERSION <= 0x050700,
|
||||||
js_prompt_pyqt_version = 0x050300
|
reason='JS prompts are not supported with PyQt 5.7'))
|
||||||
item.add_marker(pytest.mark.skipif(
|
|
||||||
PYQT_VERSION <= js_prompt_pyqt_version,
|
|
||||||
reason='JS prompts are not supported with this PyQt version'))
|
|
||||||
|
|
||||||
if deselected:
|
if deselected:
|
||||||
deselected_items.append(item)
|
deselected_items.append(item)
|
||||||
|
@ -21,8 +21,6 @@ import pytest
|
|||||||
|
|
||||||
import pytest_bdd as bdd
|
import pytest_bdd as bdd
|
||||||
|
|
||||||
from PyQt5.QtCore import PYQT_VERSION
|
|
||||||
|
|
||||||
|
|
||||||
bdd.scenarios('yankpaste.feature')
|
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'))
|
@bdd.when(bdd.parsers.parse('I insert "{value}" into the text field'))
|
||||||
def set_text_field(request, quteproc, value):
|
def set_text_field(quteproc, value):
|
||||||
if request.config.webengine and PYQT_VERSION >= 0x50700:
|
quteproc.send_cmd(":jseval --world=0 set_text('{}')".format(value))
|
||||||
cmd = ":jseval --world=0 set_text('{}')".format(value)
|
|
||||||
else:
|
|
||||||
cmd = ":jseval set_text('{}')".format(value)
|
|
||||||
quteproc.send_cmd(cmd)
|
|
||||||
quteproc.wait_for_js('textarea set to: ' + value)
|
quteproc.wait_for_js('textarea set to: ' + value)
|
||||||
|
@ -29,8 +29,6 @@ import pytest
|
|||||||
|
|
||||||
from PyQt5.QtCore import QProcess
|
from PyQt5.QtCore import QProcess
|
||||||
|
|
||||||
from qutebrowser.utils import qtutils
|
|
||||||
|
|
||||||
|
|
||||||
def _base_args(config):
|
def _base_args(config):
|
||||||
"""Get the arguments to pass with every invocation."""
|
"""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
|
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):
|
def test_qt_arg(request, quteproc_new, tmpdir):
|
||||||
"""Test --qt-arg."""
|
"""Test --qt-arg."""
|
||||||
args = (['--temp-basedir', '--qt-arg', 'stylesheet',
|
args = (['--temp-basedir', '--qt-arg', 'stylesheet',
|
||||||
|
@ -43,7 +43,7 @@ from qutebrowser.browser.webkit import cookies
|
|||||||
from qutebrowser.misc import savemanager, sql
|
from qutebrowser.misc import savemanager, sql
|
||||||
from qutebrowser.keyinput import modeman
|
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.QtGui import QKeyEvent
|
||||||
from PyQt5.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout
|
from PyQt5.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout
|
||||||
from PyQt5.QtNetwork import QNetworkCookieJar
|
from PyQt5.QtNetwork import QNetworkCookieJar
|
||||||
@ -156,8 +156,6 @@ def tab_registry(win_registry):
|
|||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def fake_web_tab(stubs, tab_registry, mode_manager, qapp):
|
def fake_web_tab(stubs, tab_registry, mode_manager, qapp):
|
||||||
"""Fixture providing the FakeWebTab *class*."""
|
"""Fixture providing the FakeWebTab *class*."""
|
||||||
if PYQT_VERSION < 0x050600:
|
|
||||||
pytest.skip('Causes segfaults, see #1638')
|
|
||||||
return stubs.FakeWebTab
|
return stubs.FakeWebTab
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,8 +19,6 @@
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PyQt5.QtCore import PYQT_VERSION
|
|
||||||
|
|
||||||
from qutebrowser.browser import browsertab
|
from qutebrowser.browser import browsertab
|
||||||
|
|
||||||
pytestmark = pytest.mark.usefixtures('redirect_webengine_data')
|
pytestmark = pytest.mark.usefixtures('redirect_webengine_data')
|
||||||
@ -48,9 +46,6 @@ def view(qtbot, config_stub, request):
|
|||||||
|
|
||||||
@pytest.fixture(params=['webkit', 'webengine'])
|
@pytest.fixture(params=['webkit', 'webengine'])
|
||||||
def tab(request, qtbot, tab_registry, cookiejar_and_cache, mode_manager):
|
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':
|
if request.param == 'webkit':
|
||||||
webkittab = pytest.importorskip('qutebrowser.browser.webkit.webkittab')
|
webkittab = pytest.importorskip('qutebrowser.browser.webkit.webkittab')
|
||||||
tab_class = webkittab.WebKitTab
|
tab_class = webkittab.WebKitTab
|
||||||
|
@ -20,10 +20,9 @@
|
|||||||
import http.server
|
import http.server
|
||||||
import threading
|
import threading
|
||||||
import logging
|
import logging
|
||||||
import sys
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PyQt5.QtCore import QUrl, QT_VERSION_STR
|
from PyQt5.QtCore import QUrl
|
||||||
from PyQt5.QtNetwork import (QNetworkProxy, QNetworkProxyQuery, QHostInfo,
|
from PyQt5.QtNetwork import (QNetworkProxy, QNetworkProxyQuery, QHostInfo,
|
||||||
QHostAddress)
|
QHostAddress)
|
||||||
|
|
||||||
@ -206,14 +205,6 @@ def test_secret_url(url, has_secret, from_file):
|
|||||||
res.resolve(QNetworkProxyQuery(QUrl(url)), from_file=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):
|
def fetcher_test(test_str):
|
||||||
class PACHandler(http.server.BaseHTTPRequestHandler):
|
class PACHandler(http.server.BaseHTTPRequestHandler):
|
||||||
def do_GET(self):
|
def do_GET(self):
|
||||||
@ -244,10 +235,6 @@ def fetcher_test(test_str):
|
|||||||
return fetcher
|
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():
|
def test_fetch_success():
|
||||||
test_str = """
|
test_str = """
|
||||||
function FindProxyForURL(domain, host) {
|
function FindProxyForURL(domain, host) {
|
||||||
@ -260,10 +247,6 @@ def test_fetch_success():
|
|||||||
assert len(proxies) == 3
|
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):
|
def test_fetch_evalerror(caplog):
|
||||||
test_str = """
|
test_str = """
|
||||||
function FindProxyForURL(domain, host) {
|
function FindProxyForURL(domain, host) {
|
||||||
|
@ -19,11 +19,7 @@
|
|||||||
|
|
||||||
"""Test qutebrowser.misc.earlyinit."""
|
"""Test qutebrowser.misc.earlyinit."""
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
import sys
|
||||||
import types
|
|
||||||
import logging
|
|
||||||
import pkg_resources
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@ -37,79 +33,6 @@ def test_init_faulthandler_stderr_none(monkeypatch, attr):
|
|||||||
earlyinit.init_faulthandler()
|
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])
|
@pytest.mark.parametrize('same', [True, False])
|
||||||
def test_qt_version(same):
|
def test_qt_version(same):
|
||||||
if same:
|
if same:
|
||||||
|
@ -35,7 +35,7 @@ from PyQt5.QtTest import QSignalSpy
|
|||||||
|
|
||||||
import qutebrowser
|
import qutebrowser
|
||||||
from qutebrowser.misc import ipc
|
from qutebrowser.misc import ipc
|
||||||
from qutebrowser.utils import objreg, qtutils, standarddir
|
from qutebrowser.utils import objreg, standarddir
|
||||||
from helpers import stubs
|
from helpers import stubs
|
||||||
|
|
||||||
|
|
||||||
@ -778,26 +778,7 @@ def test_connect_inexistent(qlocalsocket):
|
|||||||
assert qlocalsocket.error() == QLocalSocket.ServerNotFoundError
|
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.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):
|
def test_socket_options_address_in_use_problem(qlocalserver, short_tmpdir):
|
||||||
"""Qt seems to ignore AddressInUseError when using socketOptions.
|
"""Qt seems to ignore AddressInUseError when using socketOptions.
|
||||||
|
|
||||||
|
@ -19,13 +19,9 @@
|
|||||||
|
|
||||||
"""Tests for qutebrowser.utils.javascript."""
|
"""Tests for qutebrowser.utils.javascript."""
|
||||||
|
|
||||||
import binascii
|
|
||||||
import os.path
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import hypothesis
|
import hypothesis
|
||||||
import hypothesis.strategies
|
import hypothesis.strategies
|
||||||
from PyQt5.QtCore import PYQT_VERSION
|
|
||||||
|
|
||||||
from qutebrowser.utils import javascript
|
from qutebrowser.utils import javascript
|
||||||
|
|
||||||
@ -61,65 +57,22 @@ class TestStringEscape:
|
|||||||
"""Test javascript escaping with some expected outcomes."""
|
"""Test javascript escaping with some expected outcomes."""
|
||||||
assert javascript.string_escape(before) == after
|
assert javascript.string_escape(before) == after
|
||||||
|
|
||||||
def _test_escape(self, text, qtbot, webframe):
|
def _test_escape(self, text, 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):
|
|
||||||
"""Test conversion by using evaluateJavaScript."""
|
"""Test conversion by using evaluateJavaScript."""
|
||||||
escaped = javascript.string_escape(text)
|
escaped = javascript.string_escape(text)
|
||||||
result = webframe.evaluateJavaScript('"{}";'.format(escaped))
|
result = webframe.evaluateJavaScript('"{}";'.format(escaped))
|
||||||
assert result == text
|
assert result == text
|
||||||
|
|
||||||
@pytest.mark.parametrize('text', sorted(TESTS), ids=repr)
|
@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."""
|
"""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')
|
@pytest.mark.qt_log_ignore('^OpenType support missing for script')
|
||||||
@hypothesis.given(hypothesis.strategies.text())
|
@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."""
|
"""Test javascript escaping with a real QWebPage and hypothesis."""
|
||||||
# We can't simply use self._test_escape because of this:
|
self._test_escape(text, webframe)
|
||||||
# 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)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('arg, expected', [
|
@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>
|
|
@ -118,26 +118,6 @@ class TestCheckOverflow:
|
|||||||
assert newval == repl
|
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:
|
class QtObject:
|
||||||
|
|
||||||
"""Fake Qt object for test_ensure."""
|
"""Fake Qt object for test_ensure."""
|
||||||
|
@ -87,8 +87,6 @@ def test_fake_mac_config(tmpdir, monkeypatch):
|
|||||||
assert standarddir.config() == expected
|
assert standarddir.config() == expected
|
||||||
|
|
||||||
|
|
||||||
# FIXME:conf needs AppDataLocation
|
|
||||||
@pytest.mark.qt55
|
|
||||||
@pytest.mark.parametrize('what', ['data', 'config', 'cache'])
|
@pytest.mark.parametrize('what', ['data', 'config', 'cache'])
|
||||||
@pytest.mark.not_mac
|
@pytest.mark.not_mac
|
||||||
def test_fake_windows(tmpdir, monkeypatch, what):
|
def test_fake_windows(tmpdir, monkeypatch, what):
|
||||||
@ -354,8 +352,6 @@ class TestSystemData:
|
|||||||
assert standarddir.data(system=True) == standarddir.data()
|
assert standarddir.data(system=True) == standarddir.data()
|
||||||
|
|
||||||
|
|
||||||
# FIXME:conf needs AppDataLocation
|
|
||||||
@pytest.mark.qt55
|
|
||||||
class TestMoveWindowsAndMacOS:
|
class TestMoveWindowsAndMacOS:
|
||||||
|
|
||||||
"""Test other invocations of _move_data."""
|
"""Test other invocations of _move_data."""
|
||||||
|
@ -328,9 +328,8 @@ def test_get_search_url_invalid(url):
|
|||||||
(False, True, True, 'deadbeef'),
|
(False, True, True, 'deadbeef'),
|
||||||
(False, True, True, 'hello.'),
|
(False, True, True, 'hello.'),
|
||||||
(False, True, False, 'site:cookies.com oatmeal raisin'),
|
(False, True, False, 'site:cookies.com oatmeal raisin'),
|
||||||
# no DNS because bogus-IP
|
# no DNS because there is no host
|
||||||
(False, True, False, '31c3'),
|
(False, True, False, 'foo::bar'),
|
||||||
(False, True, False, 'foo::bar'), # no DNS because of no host
|
|
||||||
# Valid search term with autosearch
|
# Valid search term with autosearch
|
||||||
(False, False, False, 'test foo'),
|
(False, False, False, 'test foo'),
|
||||||
# autosearch = False
|
# autosearch = False
|
||||||
@ -350,11 +349,6 @@ def test_is_url(config_stub, fake_dns,
|
|||||||
url: The URL to test, as a string.
|
url: The URL to test, as a string.
|
||||||
auto_search: With which auto_search setting to test
|
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
|
config_stub.val.url.auto_search = auto_search
|
||||||
if auto_search == 'dns':
|
if auto_search == 'dns':
|
||||||
if uses_dns:
|
if uses_dns:
|
||||||
|
@ -35,7 +35,7 @@ import pkg_resources
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import qutebrowser
|
import qutebrowser
|
||||||
from qutebrowser.utils import version, usertypes, qtutils
|
from qutebrowser.utils import version, usertypes
|
||||||
from qutebrowser.browser import pdfjs
|
from qutebrowser.browser import pdfjs
|
||||||
|
|
||||||
|
|
||||||
@ -946,8 +946,6 @@ def test_version_output(params, stubs, monkeypatch):
|
|||||||
assert version.version() == expected
|
assert version.version() == expected
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(not qtutils.version_check('5.4'),
|
|
||||||
reason="Needs Qt >= 5.4.")
|
|
||||||
def test_opengl_vendor():
|
def test_opengl_vendor():
|
||||||
"""Simply call version.opengl_vendor() and see if it doesn't crash."""
|
"""Simply call version.opengl_vendor() and see if it doesn't crash."""
|
||||||
pytest.importorskip("PyQt5.QtOpenGL")
|
pytest.importorskip("PyQt5.QtOpenGL")
|
||||||
|
@ -19,8 +19,6 @@
|
|||||||
|
|
||||||
"""Tests for usertypes.Question."""
|
"""Tests for usertypes.Question."""
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from qutebrowser.utils import usertypes
|
from qutebrowser.utils import usertypes
|
||||||
@ -82,15 +80,6 @@ def test_abort(question, qtbot):
|
|||||||
assert question.is_aborted
|
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):
|
def test_abort_twice(question, qtbot):
|
||||||
"""Abort a question twice."""
|
"""Abort a question twice."""
|
||||||
with qtbot.wait_signal(question.aborted):
|
with qtbot.wait_signal(question.aborted):
|
||||||
|
Loading…
Reference in New Issue
Block a user