summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml6
-rw-r--r--.travis.yml19
-rw-r--r--README.asciidoc4
-rw-r--r--doc/changelog.asciidoc27
-rw-r--r--doc/contributing.asciidoc6
-rw-r--r--misc/requirements/requirements-pyqt-old.txt4
-rw-r--r--misc/requirements/requirements-pyqt-old.txt-raw2
-rw-r--r--misc/requirements/requirements-pyqt.txt4
-rw-r--r--misc/requirements/requirements-qutebrowser.txt-raw4
-rw-r--r--misc/requirements/requirements-tests-git.txt6
-rw-r--r--misc/requirements/requirements-tests.txt6
-rw-r--r--misc/requirements/requirements-tox.txt2
-rw-r--r--pytest.ini4
-rw-r--r--qutebrowser/browser/browsertab.py6
-rw-r--r--qutebrowser/browser/downloads.py2
-rw-r--r--qutebrowser/browser/downloadview.py2
-rw-r--r--qutebrowser/browser/greasemonkey.py3
-rw-r--r--qutebrowser/browser/qutescheme.py2
-rw-r--r--qutebrowser/browser/shared.py3
-rw-r--r--qutebrowser/browser/webengine/certificateerror.py7
-rw-r--r--qutebrowser/browser/webengine/webenginetab.py135
-rw-r--r--qutebrowser/browser/webengine/webview.py87
-rw-r--r--qutebrowser/browser/webkit/webkittab.py6
-rw-r--r--qutebrowser/browser/webkit/webpage.py5
-rw-r--r--qutebrowser/javascript/.eslintrc.yaml1
-rw-r--r--qutebrowser/keyinput/keyutils.py31
-rw-r--r--qutebrowser/mainwindow/prompt.py2
-rw-r--r--qutebrowser/misc/earlyinit.py2
-rw-r--r--qutebrowser/misc/sessions.py2
-rw-r--r--qutebrowser/misc/utilcmds.py2
-rw-r--r--qutebrowser/qt.py28
-rw-r--r--qutebrowser/utils/log.py5
-rw-r--r--qutebrowser/utils/version.py2
-rw-r--r--requirements.txt2
-rwxr-xr-xscripts/dev/build_release.py35
-rw-r--r--scripts/dev/ci/travis_install.sh14
-rw-r--r--tests/conftest.py2
-rw-r--r--tests/end2end/conftest.py4
-rw-r--r--tests/end2end/features/hints.feature4
-rw-r--r--tests/end2end/features/history.feature2
-rw-r--r--tests/end2end/features/keyinput.feature2
-rw-r--r--tests/end2end/features/test_prompts_bdd.py27
-rw-r--r--tests/end2end/fixtures/quteprocess.py4
-rw-r--r--tests/unit/browser/webengine/test_spell.py57
-rw-r--r--tests/unit/config/test_config.py4
-rw-r--r--tests/unit/keyinput/key_data.py61
-rw-r--r--tests/unit/utils/test_qtutils.py25
-rw-r--r--tests/unit/utils/test_standarddir.py7
-rw-r--r--tests/unit/utils/test_version.py2
-rw-r--r--tox.ini15
50 files changed, 415 insertions, 279 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index f2424fc94..92a20c0bd 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -5,15 +5,15 @@ cache:
build: off
environment:
PYTHONUNBUFFERED: 1
- PYTHON: C:\Python36\python.exe
+ PYTHON: C:\Python36-x64\python.exe
matrix:
- - TESTENV: py36-pyqt510
+ - TESTENV: py36-pyqt511
- TESTENV: pylint
install:
- '%PYTHON% -m pip install -U pip'
- '%PYTHON% -m pip install -r misc\requirements\requirements-tox.txt'
- - 'set PATH=%PATH%;C:\Python36'
+ - 'set PATH=C:\Python36-x64;%PATH'
test_script:
- '%PYTHON% -m tox -e %TESTENV%'
diff --git a/.travis.yml b/.travis.yml
index 143938aef..81a5d6fbb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -20,18 +20,17 @@ matrix:
- os: linux
env: TESTENV=py36-pyqt59
- os: linux
- env: TESTENV=py36-pyqt510-cov
- # We need a newer Xvfb as a WORKAROUND for:
- # https://bugreports.qt.io/browse/QTBUG-64928
+ env: TESTENV=py36-pyqt510
+ - os: linux
+ env: TESTENV=py36-pyqt511-cov
+ # https://github.com/travis-ci/travis-ci/issues/9069
+ - os: linux
+ python: 3.7
sudo: required
- addons:
- apt:
- sources:
- - sourceline: "deb http://us.archive.ubuntu.com/ubuntu/ xenial main universe"
- packages:
- - xvfb
+ dist: xenial
+ env: TESTENV=py37-pyqt511
- os: osx
- env: TESTENV=py36 OSX=sierra
+ env: TESTENV=py37 OSX=sierra
osx_image: xcode9.2
language: generic
# https://github.com/qutebrowser/qutebrowser/issues/2013
diff --git a/README.asciidoc b/README.asciidoc
index db401d49d..2237955dd 100644
--- a/README.asciidoc
+++ b/README.asciidoc
@@ -97,7 +97,7 @@ Requirements
The following software and libraries are required to run qutebrowser:
* http://www.python.org/[Python] 3.5 or newer (3.6 recommended)
-* http://qt.io/[Qt] 5.7.1 or newer (5.10 recommended) with the following modules:
+* http://qt.io/[Qt] 5.7.1 or newer (5.11.1 recommended) with the following modules:
- QtCore / qtbase
- QtQuick (part of qtbase in some distributions)
- QtSQL (part of qtbase in some distributions)
@@ -107,7 +107,7 @@ The following software and libraries are required to run qutebrowser:
link:https://github.com/annulen/webkit/wiki[updated fork] (5.212) is
supported
* http://www.riverbankcomputing.com/software/pyqt/intro[PyQt] 5.7.0 or newer
- (5.10 recommended) for Python 3
+ (5.11.2 recommended) for Python 3
* https://pypi.python.org/pypi/setuptools/[pkg_resources/setuptools]
* http://fdik.org/pyPEG/[pyPEG2]
* http://jinja.pocoo.org/[jinja2]
diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc
index efd451976..79a75f49f 100644
--- a/doc/changelog.asciidoc
+++ b/doc/changelog.asciidoc
@@ -21,6 +21,7 @@ v1.4.0 (unreleased)
Added
~~~~~
+- Support for the bundled `sip` module in PyQt 5.11.
- New `--debug-flag log-requests` to log requests to the debug log for
debugging.
- New `--first` flag for `:hint` (bound to `gi` for inputs) which automatically
@@ -59,14 +60,14 @@ Changed
~~~~~~~
- The following settings now support URL patterns:
- - content.headers.do_not_track
- - content.headers.custom
- - content.headers.accept_language
- - content.headers.user_agent
- - content.ssl_strict
- - content.geolocation
- - content.notifications
- - content.media_capture
+ * `content.headers.do_not_track`
+ * `content.headers.custom`
+ * `content.headers.accept_language`
+ * `content.headers.user_agent`
+ * `content.ssl_strict`
+ * `content.geolocation`
+ * `content.notifications`
+ * `content.media_capture`
- The Windows/macOS releases now bundle Qt 5.11.1 which is based on
Chromium 65.0.3325.151 with security fixes up to Chromium 67.0.3396.87.
- New short flags for commandline arguments: `-B` and `-T` for `--basedir` and
@@ -104,6 +105,10 @@ Changed
Existing dictionaries are copied over.
- If an error while parsing `~/.netrc` occurs, the cause of the error is now
logged.
+- On Qt 5.9 or newer, certificate errors now show Chromium's detailed error
+ page.
+- Greasemonkey scripts now support a "@qute-js-world" tag to run them in a
+ different JavaScript context.
Fixed
~~~~~
@@ -112,10 +117,16 @@ Fixed
- The security fix in v1.3.3 caused URLs with ampersands
(`www.example.com?one=1&two=2`) to send the wrong arguments when clicked on
the `qute://history` page.
+- Crash when opening a PDF page with PDF.js enabled (on QtWebKit), but no
+ PDF.js installed.
Removed
~~~~~~~
+- No prebuilt binaries for 32-bit Windows are supplied anymore. This is due to
+ Qt removing QtWebEngine support for those upstream. It might be possible to
+ distribute 32-bit binaries again with Qt 5.12 in December, but that will only
+ happen if it turns out enough people actually need 32-bit support.
- `:tab-detach` which has been deprecated in v1.1.0 has been removed.
- The `content.developer_extras` setting got removed. On QtWebKit, developer
extras are now automatically enabled when opening the inspector.
diff --git a/doc/contributing.asciidoc b/doc/contributing.asciidoc
index 07c2dfe25..26ff12a15 100644
--- a/doc/contributing.asciidoc
+++ b/doc/contributing.asciidoc
@@ -689,8 +689,6 @@ New PyQt release
~~~~~~~~~~~~~~~~
* See above.
-* Install new PyQt in Windows VM (32- and 64-bit).
-* Download new installer and update PyQt installer path in `ci_install.py`.
* Update `tox.ini`/`.travis.yml`/`.appveyor.yml` to test new versions.
qutebrowser release
@@ -712,8 +710,8 @@ qutebrowser release
as closed.
* Linux: Run `git checkout v1.$x.$y && ./.venv/bin/python3 scripts/dev/build_release.py --upload v1.$x.$y`.
-* Windows: Run `git checkout v1.X.Y; C:\Python36-32\python scripts\dev\build_release.py --asciidoc C:\Python27\python C:\asciidoc-8.6.9\asciidoc.py --upload v1.X.Y` (replace X/Y by hand).
-* macOS: Run `git checkout v1.X.Y && python3 scripts/dev/build_release.py --upload v1.X.Y` (replace X/Y by hand).
+* Windows: Run `git checkout v1.X.Y; py -3.6 scripts\dev\build_release.py --asciidoc C:\Python27\python C:\asciidoc-8.6.9\asciidoc.py --upload v1.X.Y` (replace X/Y by hand).
+* macOS: Run `pyenv shell 3.6.6 && git checkout v1.X.Y && python3 scripts/dev/build_release.py --upload v1.X.Y` (replace X/Y by hand).
* On server:
- Run `python3 scripts/dev/download_release.py v1.X.Y` (replace X/Y by hand).
- Run `git pull github master && sudo python3 scripts/asciidoc2html.py --website /srv/http/qutebrowser`
diff --git a/misc/requirements/requirements-pyqt-old.txt b/misc/requirements/requirements-pyqt-old.txt
deleted file mode 100644
index f0fa50ad5..000000000
--- a/misc/requirements/requirements-pyqt-old.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-# This file is automatically generated by scripts/dev/recompile_requirements.py
-
-PyQt5==5.10 # rq.filter: != 5.10.1
-sip==4.19.8
diff --git a/misc/requirements/requirements-pyqt-old.txt-raw b/misc/requirements/requirements-pyqt-old.txt-raw
deleted file mode 100644
index 16b82e0d5..000000000
--- a/misc/requirements/requirements-pyqt-old.txt-raw
+++ /dev/null
@@ -1,2 +0,0 @@
-PyQt5==5.10.0
-#@ filter: PyQt5 != 5.10.1 \ No newline at end of file
diff --git a/misc/requirements/requirements-pyqt.txt b/misc/requirements/requirements-pyqt.txt
index 059ff2df7..063f85122 100644
--- a/misc/requirements/requirements-pyqt.txt
+++ b/misc/requirements/requirements-pyqt.txt
@@ -1,4 +1,4 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
-PyQt5==5.10.1
-sip==4.19.8
+PyQt5==5.11.2
+PyQt5-sip==4.19.11
diff --git a/misc/requirements/requirements-qutebrowser.txt-raw b/misc/requirements/requirements-qutebrowser.txt-raw
index c66c65beb..506af17c0 100644
--- a/misc/requirements/requirements-qutebrowser.txt-raw
+++ b/misc/requirements/requirements-qutebrowser.txt-raw
@@ -1,7 +1,9 @@
Jinja2
Pygments
pyPEG2
-PyYAML
+PyYAML!=4.1
colorama
cssutils
attrs
+
+#@ filter: PyYAML != 4.1
diff --git a/misc/requirements/requirements-tests-git.txt b/misc/requirements/requirements-tests-git.txt
index 6681dd15e..ce00cd31c 100644
--- a/misc/requirements/requirements-tests-git.txt
+++ b/misc/requirements/requirements-tests-git.txt
@@ -35,8 +35,4 @@ git+https://github.com/pallets/markupsafe.git
hg+http://bitbucket.org/birkenfeld/pygments-main
hg+https://bitbucket.org/fdik/pypeg
git+https://github.com/python-attrs/attrs.git
-
-# Fails to build:
-# gcc: error: ext/_yaml.c: No such file or directory
-# hg+https://bitbucket.org/xi/pyyaml
-PyYAML==3.12
+git+https://github.com/yaml/pyyaml.git
diff --git a/misc/requirements/requirements-tests.txt b/misc/requirements/requirements-tests.txt
index 334e740c3..4475def05 100644
--- a/misc/requirements/requirements-tests.txt
+++ b/misc/requirements/requirements-tests.txt
@@ -11,7 +11,7 @@ fields==5.0.0
Flask==1.0.2
glob2==0.6
hunter==2.0.2
-hypothesis==3.59.1
+hypothesis==3.65.0
itsdangerous==0.24
# Jinja2==2.10
Mako==1.0.7
@@ -20,9 +20,9 @@ more-itertools==4.2.0
parse==1.8.4
parse-type==0.4.2
pluggy==0.6.0
-py==1.5.3
+py==1.5.4
py-cpuinfo==4.0.0
-pytest==3.6.1
+pytest==3.6.2
pytest-bdd==2.21.0
pytest-benchmark==3.1.1
pytest-cov==2.5.1
diff --git a/misc/requirements/requirements-tox.txt b/misc/requirements/requirements-tox.txt
index e8b3de2b1..64ab8bf68 100644
--- a/misc/requirements/requirements-tox.txt
+++ b/misc/requirements/requirements-tox.txt
@@ -1,7 +1,7 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
pluggy==0.6.0
-py==1.5.3
+py==1.5.4
six==1.11.0
tox==3.0.0
virtualenv==16.0.0
diff --git a/pytest.ini b/pytest.ini
index c897f0be7..1b5016321 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -63,4 +63,8 @@ qt_log_ignore =
^inotify_add_watch\(".*"\) failed: "No space left on device"
^QSettings::value: Empty key passed
^Icon theme ".*" not found
+ ^Error receiving trust for a CA certificate
xfail_strict = true
+filterwarnings =
+ # This happens in many qutebrowser dependencies...
+ ignore:Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working:DeprecationWarning
diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py
index 7e78b2621..0170b4627 100644
--- a/qutebrowser/browser/browsertab.py
+++ b/qutebrowser/browser/browsertab.py
@@ -22,7 +22,6 @@
import enum
import itertools
-import sip
import attr
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QUrl, QObject, QSizeF, Qt
from PyQt5.QtGui import QIcon
@@ -38,6 +37,7 @@ from qutebrowser.utils import (utils, objreg, usertypes, log, qtutils,
urlutils, message)
from qutebrowser.misc import miscwidgets, objects
from qutebrowser.browser import mouse, hints
+from qutebrowser.qt import sip
tab_id_gen = itertools.count(0)
@@ -893,10 +893,6 @@ class AbstractTab(QWidget):
self._progress = perc
self.load_progress.emit(perc)
- @pyqtSlot()
- def _on_ssl_errors(self):
- self._has_ssl_errors = True
-
def url(self, requested=False):
raise NotImplementedError
diff --git a/qutebrowser/browser/downloads.py b/qutebrowser/browser/downloads.py
index dd112e00a..2e30c26c2 100644
--- a/qutebrowser/browser/downloads.py
+++ b/qutebrowser/browser/downloads.py
@@ -29,7 +29,6 @@ import pathlib
import tempfile
import enum
-import sip
from PyQt5.QtCore import (pyqtSlot, pyqtSignal, Qt, QObject, QModelIndex,
QTimer, QAbstractListModel, QUrl)
@@ -37,6 +36,7 @@ from qutebrowser.commands import cmdexc, cmdutils
from qutebrowser.config import config
from qutebrowser.utils import (usertypes, standarddir, utils, message, log,
qtutils)
+from qutebrowser.qt import sip
ModelRole = enum.IntEnum('ModelRole', ['item'], start=Qt.UserRole)
diff --git a/qutebrowser/browser/downloadview.py b/qutebrowser/browser/downloadview.py
index 80da117d2..e90e37509 100644
--- a/qutebrowser/browser/downloadview.py
+++ b/qutebrowser/browser/downloadview.py
@@ -21,13 +21,13 @@
import functools
-import sip
from PyQt5.QtCore import pyqtSlot, QSize, Qt, QTimer
from PyQt5.QtWidgets import QListView, QSizePolicy, QMenu, QStyleFactory
from qutebrowser.browser import downloads
from qutebrowser.config import config
from qutebrowser.utils import qtutils, utils, objreg
+from qutebrowser.qt import sip
def update_geometry(obj):
diff --git a/qutebrowser/browser/greasemonkey.py b/qutebrowser/browser/greasemonkey.py
index db8246bab..cff45f3ac 100644
--- a/qutebrowser/browser/greasemonkey.py
+++ b/qutebrowser/browser/greasemonkey.py
@@ -58,6 +58,7 @@ class GreasemonkeyScript:
self.run_at = None
self.script_meta = None
self.runs_on_sub_frames = True
+ self.jsworld = "main"
for name, value in properties:
if name == 'name':
self.name = value
@@ -77,6 +78,8 @@ class GreasemonkeyScript:
self.runs_on_sub_frames = False
elif name == 'require':
self.requires.append(value)
+ elif name == 'qute-js-world':
+ self.jsworld = value
HEADER_REGEX = r'// ==UserScript==|\n+// ==/UserScript==\n'
PROPS_REGEX = r'// @(?P<prop>[^\s]+)\s*(?P<val>.*)'
diff --git a/qutebrowser/browser/qutescheme.py b/qutebrowser/browser/qutescheme.py
index e3483bac0..b3787e5d0 100644
--- a/qutebrowser/browser/qutescheme.py
+++ b/qutebrowser/browser/qutescheme.py
@@ -34,7 +34,6 @@ import urllib
import collections
import pkg_resources
-import sip
from PyQt5.QtCore import QUrlQuery, QUrl
import qutebrowser
@@ -42,6 +41,7 @@ from qutebrowser.config import config, configdata, configexc, configdiff
from qutebrowser.utils import (version, utils, jinja, log, message, docutils,
objreg, urlutils)
from qutebrowser.misc import objects
+from qutebrowser.qt import sip
pyeval_output = ":pyeval was never called"
diff --git a/qutebrowser/browser/shared.py b/qutebrowser/browser/shared.py
index 01ccab13b..2398ca2e4 100644
--- a/qutebrowser/browser/shared.py
+++ b/qutebrowser/browser/shared.py
@@ -48,7 +48,8 @@ def custom_headers(url):
for header, value in conf_headers.items():
headers[header.encode('ascii')] = value.encode('ascii')
- accept_language = config.instance.get('content.headers.accept_language', url=url)
+ accept_language = config.instance.get('content.headers.accept_language',
+ url=url)
if accept_language is not None:
headers[b'Accept-Language'] = accept_language.encode('ascii')
diff --git a/qutebrowser/browser/webengine/certificateerror.py b/qutebrowser/browser/webengine/certificateerror.py
index 47953d4cc..768f54ec6 100644
--- a/qutebrowser/browser/webengine/certificateerror.py
+++ b/qutebrowser/browser/webengine/certificateerror.py
@@ -28,6 +28,10 @@ class CertificateErrorWrapper(usertypes.AbstractCertificateErrorWrapper):
"""A wrapper over a QWebEngineCertificateError."""
+ def __init__(self, error):
+ super().__init__(error)
+ self.ignore = False
+
def __str__(self):
return self._error.errorDescription()
@@ -37,5 +41,8 @@ class CertificateErrorWrapper(usertypes.AbstractCertificateErrorWrapper):
self._error.error()),
string=str(self))
+ def url(self):
+ return self._error.url()
+
def is_overridable(self):
return self._error.isOverridable()
diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py
index 23c26ef95..9f840b715 100644
--- a/qutebrowser/browser/webengine/webenginetab.py
+++ b/qutebrowser/browser/webengine/webenginetab.py
@@ -25,7 +25,6 @@ import sys
import re
import html as html_utils
-import sip
from PyQt5.QtCore import (pyqtSignal, pyqtSlot, Qt, QEvent, QPoint, QPointF,
QUrl, QTimer, QObject, qVersion)
from PyQt5.QtGui import QKeyEvent, QIcon
@@ -38,10 +37,11 @@ from qutebrowser.browser import browsertab, mouse, shared
from qutebrowser.browser.webengine import (webview, webengineelem, tabhistory,
interceptor, webenginequtescheme,
cookies, webenginedownloads,
- webenginesettings)
+ webenginesettings, certificateerror)
from qutebrowser.misc import miscwidgets
from qutebrowser.utils import (usertypes, qtutils, log, javascript, utils,
message, objreg, jinja, debug)
+from qutebrowser.qt import sip
_qute_scheme_handler = None
@@ -788,6 +788,7 @@ class _WebEngineScripts(QObject):
super().__init__(parent)
self._tab = tab
self._widget = None
+ self._greasemonkey = objreg.get('greasemonkey')
def connect_signals(self):
config.instance.changed.connect(self._on_config_changed)
@@ -853,9 +854,16 @@ class _WebEngineScripts(QObject):
self._inject_early_js('js', js_code, subframes=True)
self._init_stylesheet()
- greasemonkey = objreg.get('greasemonkey')
- greasemonkey.scripts_reloaded.connect(self._inject_userscripts)
- self._inject_userscripts()
+ # The Greasemonkey metadata block support in QtWebEngine only starts at
+ # Qt 5.8. With 5.7.1, we need to inject the scripts ourselves in
+ # response to urlChanged.
+ if not qtutils.version_check('5.8'):
+ self._tab.url_changed.connect(
+ self._inject_greasemonkey_scripts_for_url)
+ else:
+ self._greasemonkey.scripts_reloaded.connect(
+ self._inject_all_greasemonkey_scripts)
+ self._inject_all_greasemonkey_scripts()
def _init_stylesheet(self):
"""Initialize custom stylesheets.
@@ -872,40 +880,77 @@ class _WebEngineScripts(QObject):
)
self._inject_early_js('stylesheet', js_code, subframes=True)
- def _inject_userscripts(self):
- """Register user JavaScript files with the global profiles."""
- # The Greasemonkey metadata block support in QtWebEngine only starts at
- # Qt 5.8. With 5.7.1, we need to inject the scripts ourselves in
- # response to urlChanged.
- if not qtutils.version_check('5.8'):
+ @pyqtSlot(QUrl)
+ def _inject_greasemonkey_scripts_for_url(self, url):
+ matching_scripts = self._greasemonkey.scripts_for(url)
+ self._inject_greasemonkey_scripts(
+ matching_scripts.start, QWebEngineScript.DocumentCreation, True)
+ self._inject_greasemonkey_scripts(
+ matching_scripts.end, QWebEngineScript.DocumentReady, False)
+ self._inject_greasemonkey_scripts(
+ matching_scripts.idle, QWebEngineScript.Deferred, False)
+
+ @pyqtSlot()
+ def _inject_all_greasemonkey_scripts(self):
+ scripts = self._greasemonkey.all_scripts()
+ self._inject_greasemonkey_scripts(scripts)
+
+ def _inject_greasemonkey_scripts(self, scripts=None, injection_point=None,
+ remove_first=True):
+ """Register user JavaScript files with the current tab.
+
+ Args:
+ scripts: A list of GreasemonkeyScripts, or None to add all
+ known by the Greasemonkey subsystem.
+ injection_point: The QWebEngineScript::InjectionPoint stage
+ to inject the script into, None to use
+ auto-detection.
+ remove_first: Whether to remove all previously injected
+ scripts before adding these ones.
+ """
+ if sip.isdeleted(self._widget):
return
- # Since we are inserting scripts into profile.scripts they won't
- # just get replaced by new gm scripts like if we were injecting them
- # ourselves so we need to remove all gm scripts, while not removing
- # any other stuff that might have been added. Like the one for
- # stylesheets.
- greasemonkey = objreg.get('greasemonkey')
- scripts = self._widget.page().scripts()
- for script in scripts.toList():
- if script.name().startswith("GM-"):
- log.greasemonkey.debug('Removing script: {}'
- .format(script.name()))
- removed = scripts.remove(script)
- assert removed, script.name()
-
- # Then add the new scripts.
- for script in greasemonkey.all_scripts():
- # @run-at (and @include/@exclude/@match) is parsed by
- # QWebEngineScript.
+ # Since we are inserting scripts into a per-tab collection,
+ # rather than just injecting scripts on page load, we need to
+ # make sure we replace existing scripts, not just add new ones.
+ # While, taking care not to remove any other scripts that might
+ # have been added elsewhere, like the one for stylesheets.
+ page_scripts = self._widget.page().scripts()
+ if remove_first:
+ for script in page_scripts.toList():
+ if script.name().startswith("GM-"):
+ log.greasemonkey.debug('Removing script: {}'
+ .format(script.name()))
+ removed = page_scripts.remove(script)
+ assert removed, script.name()
+
+ if not scripts:
+ return
+
+ for script in scripts:
new_script = QWebEngineScript()
- new_script.setWorldId(QWebEngineScript.MainWorld)
+ try:
+ world = int(script.jsworld)
+ except ValueError:
+ try:
+ world = _JS_WORLD_MAP[usertypes.JsWorld[
+ script.jsworld.lower()]]
+ except KeyError:
+ log.greasemonkey.error(
+ "script {} has invalid value for '@qute-js-world'"
+ ": {}".format(script.name, script.jsworld))
+ continue
+ new_script.setWorldId(world)
new_script.setSourceCode(script.code())
new_script.setName("GM-{}".format(script.name))
new_script.setRunsOnSubFrames(script.runs_on_sub_frames)
+ # Override the @run-at value parsed by QWebEngineScript if desired.
+ if injection_point:
+ new_script.setInjectionPoint(injection_point)
log.greasemonkey.debug('adding script: {}'
.format(new_script.name()))
- scripts.insert(new_script)
+ page_scripts.insert(new_script)
class WebEngineTab(browsertab.AbstractTab):
@@ -1227,6 +1272,34 @@ class WebEngineTab(browsertab.AbstractTab):
# the old icon is still displayed.
self.icon_changed.emit(QIcon())
+ @pyqtSlot(certificateerror.CertificateErrorWrapper)
+ def _on_ssl_errors(self, error):
+ self._has_ssl_errors = True
+
+ url = error.url()
+ log.webview.debug("Certificate error: {}".format(error))
+
+ if error.is_overridable():
+ error.ignore = shared.ignore_certificate_errors(
+ url, [error], abort_on=[self.shutting_down, self.load_started])
+ else:
+ log.webview.error("Non-overridable certificate error: "
+ "{}".format(error))
+
+ log.webview.debug("ignore {}, URL {}, requested {}".format(
+ error.ignore, url, self.url(requested=True)))
+
+ # WORKAROUND for https://bugreports.qt.io/browse/QTBUG-56207
+ # We can't really know when to show an error page, as the error might
+ # have happened when loading some resource.
+ # However, self.url() is not available yet and the requested URL
+ # might not match the URL we get from the error - so we just apply a
+ # heuristic here.
+ if (not qtutils.version_check('5.9') and
+ not error.ignore and
+ url.matches(self.url(requested=True), QUrl.RemoveScheme)):
+ self._show_error_page(url, str(error))
+
@pyqtSlot(QUrl)
def _on_predicted_navigation(self, url):
"""If we know we're going to visit an URL soon, change the settings.
diff --git a/qutebrowser/browser/webengine/webview.py b/qutebrowser/browser/webengine/webview.py
index 7436bc01a..b10cc5f9a 100644
--- a/qutebrowser/browser/webengine/webview.py
+++ b/qutebrowser/browser/webengine/webview.py
@@ -19,18 +19,17 @@
"""The main browser widget for QtWebEngine."""
-import sip
-from PyQt5.QtCore import pyqtSignal, pyqtSlot, QUrl, PYQT_VERSION
+from PyQt5.QtCore import pyqtSignal, QUrl, PYQT_VERSION
from PyQt5.QtGui import QPalette
from PyQt5.QtWidgets import QWidget
-from PyQt5.QtWebEngineWidgets import (QWebEngineView, QWebEnginePage,
- QWebEngineScript)
+from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEnginePage
from qutebrowser.browser import shared
-from qutebrowser.browser.webengine import certificateerror, webenginesettings
+from qutebrowser.browser.webengine import webenginesettings, certificateerror
from qutebrowser.config import config
-from qutebrowser.utils import log, debug, usertypes, jinja, objreg, qtutils
+from qutebrowser.utils import log, debug, usertypes, objreg, qtutils
from qutebrowser.misc import miscwidgets
+from qutebrowser.qt import sip
class WebEngineView(QWebEngineView):
@@ -152,11 +151,13 @@ class WebEnginePage(QWebEnginePage):
Signals:
certificate_error: Emitted on certificate errors.
+ Needs to be directly connected to a slot setting the
+ 'ignore' attribute.
shutting_down: Emitted when the page is shutting down.
navigation_request: Emitted on acceptNavigationRequest.
"""
- certificate_error = pyqtSignal()
+ certificate_error = pyqtSignal(certificateerror.CertificateErrorWrapper)
shutting_down = pyqtSignal()
navigation_request = pyqtSignal(usertypes.NavigationRequest)
@@ -166,7 +167,6 @@ class WebEnginePage(QWebEnginePage):
self._theme_color = theme_color
self._set_bg_color()
config.instance.changed.connect(self._set_bg_color)
- self.urlChanged.connect(self._inject_userjs)
@config.change_filter('colors.webpage.bg')
def _set_bg_color(self):
@@ -181,36 +181,9 @@ class WebEnginePage(QWebEnginePage):
def certificateError(self, error):
"""Handle certificate errors coming from Qt."""
- self.certificate_error.emit()
- url = error.url()
error = certificateerror.CertificateErrorWrapper(error)
- log.webview.debug("Certificate error: {}".format(error))
-
- url_string = url.toDisplayString()
- error_page = jinja.render(
- 'error.html', title="Error loading page: {}".format(url_string),
- url=url_string, error=str(error))
-
- if error.is_overridable():
- ignore = shared.ignore_certificate_errors(
- url, [error], abort_on=[self.loadStarted, self.shutting_down])
- else:
- log.webview.error("Non-overridable certificate error: "
- "{}".format(error))
- ignore = False
-
- # We can't really know when to show an error page, as the error might
- # have happened when loading some resource.
- # However, self.url() is not available yet and self.requestedUrl()
- # might not match the URL we get from the error - so we just apply a
- # heuristic here.
- # See https://bugreports.qt.io/browse/QTBUG-56207
- log.webview.debug("ignore {}, URL {}, requested {}".format(
- ignore, url, self.requestedUrl()))
- if not ignore and url.matches(self.requestedUrl(), QUrl.RemoveScheme):
- self.setHtml(error_page)
-
- return ignore
+ self.certificate_error.emit(error)
+ return error.ignore
def javaScriptConfirm(self, url, js_msg):
"""Override javaScriptConfirm to use qutebrowser prompts."""
@@ -288,43 +261,3 @@ class WebEnginePage(QWebEnginePage):
is_main_frame=is_main_frame)
self.navigation_request.emit(navigation)
return navigation.accepted
-
- @pyqtSlot('QUrl')
- def _inject_userjs(self, url):
- """Inject userscripts registered for `url` into the current page."""
- if qtutils.version_check('5.8'):
- # Handled in webenginetab with the builtin Greasemonkey
- # support.
- return
-
- # Using QWebEnginePage.scripts() to hold the user scripts means
- # we don't have to worry ourselves about where to inject the
- # page but also means scripts hang around for the tab lifecycle.
- # So clear them here.
- scripts = self.scripts()
- for script in scripts.toList():
- if script.name().startswith("GM-"):
- log.greasemonkey.debug("Removing script: {}"
- .format(script.name()))
- removed = scripts.remove(script)
- assert removed, script.name()
-
- def _add_script(script, injection_point):
- new_script = QWebEngineScript()
- new_script.setInjectionPoint(injection_point)
- new_script.setWorldId(QWebEngineScript.MainWorld)
- new_script.setSourceCode(script.code())
- new_script.setName("GM-{}".format(script.name))
- new_script.setRunsOnSubFrames(script.runs_on_sub_frames)
- log.greasemonkey.debug("Adding script: {}"
- .format(new_script.name()))
- scripts.insert(new_script)
-
- greasemonkey = objreg.get('greasemonkey')
- matching_scripts = greasemonkey.scripts_for(url)
- for script in matching_scripts.start:
- _add_script(script, QWebEngineScript.DocumentCreation)
- for script in matching_scripts.end:
- _add_script(script, QWebEngineScript.DocumentReady)
- for script in matching_scripts.idle:
- _add_script(script, QWebEngineScript.Deferred)
diff --git a/qutebrowser/browser/webkit/webkittab.py b/qutebrowser/browser/webkit/webkittab.py
index cf158ed2d..31cb82f29 100644
--- a/qutebrowser/browser/webkit/webkittab.py
+++ b/qutebrowser/browser/webkit/webkittab.py
@@ -23,7 +23,6 @@ import re
import functools
import xml.etree.ElementTree
-import sip
from PyQt5.QtCore import (pyqtSlot, Qt, QEvent, QUrl, QPoint, QTimer, QSizeF,
QSize)
from PyQt5.QtGui import QKeyEvent, QIcon
@@ -35,6 +34,7 @@ from qutebrowser.browser import browsertab, shared
from qutebrowser.browser.webkit import (webview, tabhistory, webkitelem,
webkitsettings)
from qutebrowser.utils import qtutils, usertypes, utils, log, debug
+from qutebrowser.qt import sip
class WebKitAction(browsertab.AbstractAction):
@@ -808,6 +808,10 @@ class WebKitTab(browsertab.AbstractTab):
if navigation.is_main_frame:
self.settings.update_for_url(navigation.url)
+ @pyqtSlot()
+ def _on_ssl_errors(self):
+ self._has_ssl_errors = True
+
def _connect_signals(self):
view = self._widget
page = view.page()
diff --git a/qutebrowser/browser/webkit/webpage.py b/qutebrowser/browser/webkit/webpage.py
index 853ff1b81..4e0701329 100644
--- a/qutebrowser/browser/webkit/webpage.py
+++ b/qutebrowser/browser/webkit/webpage.py
@@ -22,7 +22,6 @@
import html
import functools
-import sip
from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QUrl, QPoint
from PyQt5.QtGui import QDesktopServices
from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest
@@ -35,6 +34,7 @@ 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, objreg
+from qutebrowser.qt import sip
class BrowserPage(QWebPage):
@@ -212,7 +212,8 @@ class BrowserPage(QWebPage):
page = pdfjs.generate_pdfjs_page(reply.url())
except pdfjs.PDFJSNotFound:
page = jinja.render('no_pdfjs.html',
- url=reply.url().toDisplayString())
+ url=reply.url().toDisplayString(),
+ title="PDF.js not found")
self.mainFrame().setContent(page.encode('utf-8'), 'text/html',
reply.url())
reply.deleteLater()
diff --git a/qutebrowser/javascript/.eslintrc.yaml b/qutebrowser/javascript/.eslintrc.yaml
index 02081f7a7..6a46f96e9 100644
--- a/qutebrowser/javascript/.eslintrc.yaml
+++ b/qutebrowser/javascript/.eslintrc.yaml
@@ -57,3 +57,4 @@ rules:
no-ternary: "off"
max-lines: "off"
multiline-ternary: ["error", "always-multiline"]
+ max-lines-per-function: "off"
diff --git a/qutebrowser/keyinput/keyutils.py b/qutebrowser/keyinput/keyutils.py
index f0cf9c22a..e22e24f77 100644
--- a/qutebrowser/keyinput/keyutils.py
+++ b/qutebrowser/keyinput/keyutils.py
@@ -138,6 +138,37 @@ def _key_to_string(key):
'Dead_Hook': 'Hook',
'Dead_Horn': 'Horn',
+ 'Dead_Stroke': '̵',
+ 'Dead_Abovecomma': '̓',
+ 'Dead_Abovereversedcomma': '̔',
+ 'Dead_Doublegrave': '̏',
+ 'Dead_Belowring': '̥',
+ 'Dead_Belowmacron': '̱',
+ 'Dead_Belowcircumflex': '̭',
+ 'Dead_Belowtilde': '̰',
+ 'Dead_Belowbreve': '̮',
+ 'Dead_Belowdiaeresis': '̤',
+ 'Dead_Invertedbreve': '̑',
+ 'Dead_Belowcomma': '̦',
+ 'Dead_Currency': '¤',
+ 'Dead_a': 'a',
+ 'Dead_A': 'A',
+ 'Dead_e': 'e',
+ 'Dead_E': 'E',
+ 'Dead_i': 'i',
+ 'Dead_I': 'I',
+ 'Dead_o': 'o',
+ 'Dead_O': 'O',
+ 'Dead_u': 'u',
+ 'Dead_U': 'U',
+ 'Dead_Small_Schwa': 'ə',
+ 'Dead_Capital_Schwa': 'Ə',
+ 'Dead_Greek': 'Greek',
+ 'Dead_Lowline': '̲',
+ 'Dead_Aboveverticalline': '̍',
+ 'Dead_Belowverticalline': '\u0329',
+ 'Dead_Longsolidusoverlay': '̸',
+
'Memo': 'Memo',
'ToDoList': 'To Do List',
'Calendar': 'Calendar',
diff --git a/qutebrowser/mainwindow/prompt.py b/qutebrowser/mainwindow/prompt.py
index f7af28440..357f63dd7 100644
--- a/qutebrowser/mainwindow/prompt.py
+++ b/qutebrowser/mainwindow/prompt.py
@@ -24,7 +24,6 @@ import html
import collections
import attr
-import sip
from PyQt5.QtCore import (pyqtSlot, pyqtSignal, Qt, QTimer, QDir, QModelIndex,
QItemSelectionModel, QObject, QEventLoop)
from PyQt5.QtWidgets import (QWidget, QGridLayout, QVBoxLayout, QLineEdit,
@@ -36,6 +35,7 @@ from qutebrowser.config import config
from qutebrowser.utils import usertypes, log, utils, qtutils, objreg, message
from qutebrowser.keyinput import modeman
from qutebrowser.commands import cmdutils, cmdexc
+from qutebrowser.qt import sip
prompt_queue = None
diff --git a/qutebrowser/misc/earlyinit.py b/qutebrowser/misc/earlyinit.py
index 9649d27cc..92e3a9731 100644
--- a/qutebrowser/misc/earlyinit.py
+++ b/qutebrowser/misc/earlyinit.py
@@ -246,7 +246,7 @@ def configure_pyqt():
from PyQt5.QtCore import pyqtRemoveInputHook
pyqtRemoveInputHook()
- import sip
+ from qutebrowser.qt import sip
try:
# Added in sip 4.19.4
sip.enableoverflowchecking(True)
diff --git a/qutebrowser/misc/sessions.py b/qutebrowser/misc/sessions.py
index dddf48b05..358ab0c53 100644
--- a/qutebrowser/misc/sessions.py
+++ b/qutebrowser/misc/sessions.py
@@ -24,7 +24,6 @@ import os.path
import itertools
import urllib
-import sip
from PyQt5.QtCore import QUrl, QObject, QPoint, QTimer
from PyQt5.QtWidgets import QApplication
import yaml
@@ -35,6 +34,7 @@ from qutebrowser.commands import cmdexc, cmdutils
from qutebrowser.config import config, configfiles
from qutebrowser.completion.models import miscmodels
from qutebrowser.mainwindow import mainwindow
+from qutebrowser.qt import sip
default = object() # Sentinel value
diff --git a/qutebrowser/misc/utilcmds.py b/qutebrowser/misc/utilcmds.py
index 5d987afa3..66483e09a 100644
--- a/qutebrowser/misc/utilcmds.py
+++ b/qutebrowser/misc/utilcmds.py
@@ -29,7 +29,6 @@ try:
except ImportError:
hunter = None
-import sip
from PyQt5.QtCore import QUrl
# so it's available for :debug-pyeval
from PyQt5.QtWidgets import QApplication # pylint: disable=unused-import
@@ -40,6 +39,7 @@ from qutebrowser.commands import cmdutils, runners, cmdexc
from qutebrowser.config import config, configdata
from qutebrowser.misc import consolewidget
from qutebrowser.utils.version import pastebin_version
+from qutebrowser.qt import sip
@cmdutils.register(maxsplit=1, no_cmd_split=True, no_replace_variables=True)
diff --git a/qutebrowser/qt.py b/qutebrowser/qt.py
new file mode 100644
index 000000000..2878bbe98
--- /dev/null
+++ b/qutebrowser/qt.py
@@ -0,0 +1,28 @@
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2018 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/>.
+
+"""Wrappers around Qt/PyQt code."""
+
+# pylint: disable=unused-import
+# PyQt 5.11 comes with a bundled sip,
+# for older PyQt versions it's a separate module.
+try:
+ from PyQt5 import sip
+except ImportError:
+ import sip
diff --git a/qutebrowser/utils/log.py b/qutebrowser/utils/log.py
index 30e570e16..48711614d 100644
--- a/qutebrowser/utils/log.py
+++ b/qutebrowser/utils/log.py
@@ -209,6 +209,11 @@ def _init_py_warnings():
"""Initialize Python warning handling."""
warnings.simplefilter('default')
warnings.filterwarnings('ignore', module='pdb', category=ResourceWarning)
+ # This happens in many qutebrowser dependencies...
+ warnings.filterwarnings('ignore', category=DeprecationWarning,
+ message="Using or importing the ABCs from "
+ "'collections' instead of from 'collections.abc' "
+ "is deprecated, and in 3.8 it will stop working")
@contextlib.contextmanager
diff --git a/qutebrowser/utils/version.py b/qutebrowser/utils/version.py
index dc5168a8d..570d1f887 100644
--- a/qutebrowser/utils/version.py
+++ b/qutebrowser/utils/version.py
@@ -318,7 +318,7 @@ def _chromium_version():
Qt 5.9: Chromium 56
Qt 5.10: Chromium 61
Qt 5.11: Chromium 65
- Qt 5.12: Chromium 69 (?)
+ Qt 5.12: Chromium 69 (? - current dev branch: 67)
Also see https://www.chromium.org/developers/calendar
"""
diff --git a/requirements.txt b/requirements.txt
index 2695ba55f..4516340d7 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -7,4 +7,4 @@ Jinja2==2.10
MarkupSafe==1.0
Pygments==2.2.0
pyPEG2==2.15.2
-PyYAML==3.12
+PyYAML==3.13b1 # rq.filter: != 4.1
diff --git a/scripts/dev/build_release.py b/scripts/dev/build_release.py
index 146b7a462..254132b3c 100755
--- a/scripts/dev/build_release.py
+++ b/scripts/dev/build_release.py
@@ -45,7 +45,6 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir,
import qutebrowser
from scripts import utils
# from scripts.dev import update_3rdparty
-from scripts.dev import gen_versioninfo
def call_script(name, *args, python=sys.executable):
@@ -239,31 +238,16 @@ def build_windows():
except FileNotFoundError:
python_x64 = r'C:\Python{}\python.exe'.format(ver)
- try:
- reg32_key = winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE,
- r'SOFTWARE\WOW6432Node\Python\PythonCore'
- r'\{}-32\InstallPath'.format(dot_ver))
- python_x86 = winreg.QueryValueEx(reg32_key, 'ExecutablePath')[0]
- except FileNotFoundError:
- python_x86 = r'C:\Python{}-32\python.exe'.format(ver)
-
out_pyinstaller = os.path.join('dist', 'qutebrowser')
- out_32 = os.path.join('dist',
- 'qutebrowser-{}-x86'.format(qutebrowser.__version__))
out_64 = os.path.join('dist',
'qutebrowser-{}-x64'.format(qutebrowser.__version__))
artifacts = []
+ from scripts.dev import gen_versioninfo
utils.print_title("Updating VersionInfo file")
gen_versioninfo.main()
- utils.print_title("Running pyinstaller 32bit")
- _maybe_remove(out_32)
- call_tox('pyinstaller', '-r', python=python_x86)
- shutil.move(out_pyinstaller, out_32)
- patch_windows(out_32)
-
utils.print_title("Running pyinstaller 64bit")
_maybe_remove(out_64)
call_tox('pyinstaller', '-r', python=python_x64)
@@ -272,38 +256,21 @@ def build_windows():
utils.print_title("Building installers")
subprocess.run(['makensis.exe',
- '/DVERSION={}'.format(qutebrowser.__version__),
- 'misc/qutebrowser.nsi'], check=True)
- subprocess.run(['makensis.exe',
'/DX64',
'/DVERSION={}'.format(qutebrowser.__version__),
'misc/qutebrowser.nsi'], check=True)
- name_32 = 'qutebrowser-{}-win32.exe'.format(qutebrowser.__version__)
name_64 = 'qutebrowser-{}-amd64.exe'.format(qutebrowser.__version__)
artifacts += [
- (os.path.join('dist', name_32),
- 'application/vnd.microsoft.portable-executable',
- 'Windows 32bit installer'),
(os.path.join('dist', name_64),
'application/vnd.microsoft.portable-executable',
'Windows 64bit installer'),
]
- utils.print_title("Running 32bit smoke test")
- smoke_test(os.path.join(out_32, 'qutebrowser.exe'))
utils.print_title("Running 64bit smoke test")
smoke_test(os.path.join(out_64, 'qutebrowser.exe'))
- utils.print_title("Zipping 32bit standalone...")
- name = 'qutebrowser-{}-windows-standalone-win32'.format(
- qutebrowser.__version__)
- shutil.make_archive(name, 'zip', 'dist', os.path.basename(out_32))
- artifacts.append(('{}.zip'.format(name),
- 'application/zip',
- 'Windows 32bit standalone'))
-
utils.print_title("Zipping 64bit standalone...")
name = 'qutebrowser-{}-windows-standalone-amd64'.format(
qutebrowser.__version__)
diff --git a/scripts/dev/ci/travis_install.sh b/scripts/dev/ci/travis_install.sh
index 20aa1c12d..18f5aa9ec 100644
--- a/scripts/dev/ci/travis_install.sh
+++ b/scripts/dev/ci/travis_install.sh
@@ -43,11 +43,6 @@ travis_retry() {
return $result
}
-brew_install() {
- brew update
- brew install "$@"
-}
-
pip_install() {
travis_retry python3 -m pip install "$@"
}
@@ -62,7 +57,10 @@ check_pyqt() {
python3 <<EOF
import sys
from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR, qVersion
-from sip import SIP_VERSION_STR
+try:
+ from PyQt.sip import SIP_VERSION_STR
+except ModuleNotFoundError:
+ from sip import SIP_VERSION_STR
print("Python {}".format(sys.version))
print("PyQt5 {}".format(PYQT_VERSION_STR))
@@ -84,8 +82,8 @@ elif [[ $TRAVIS_OS_NAME == osx ]]; then
brew --version
brew update
- brew upgrade python
- brew install qt5 pyqt5 libyaml
+ brew upgrade python libyaml
+ brew install qt5 pyqt5
pip_install -r misc/requirements/requirements-tox.txt
python3 -m pip --version
diff --git a/tests/conftest.py b/tests/conftest.py
index 7df1a1e08..595c2940e 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -25,7 +25,6 @@ import os
import sys
import warnings
-import sip
import pytest
import hypothesis
from PyQt5.QtCore import qVersion, PYQT_VERSION
@@ -38,6 +37,7 @@ from helpers.messagemock import message_mock
from helpers.fixtures import * # noqa: F403
from qutebrowser.utils import qtutils, standarddir, usertypes, utils, version
from qutebrowser.misc import objects
+from qutebrowser.qt import sip
import qutebrowser.app # To register commands
diff --git a/tests/end2end/conftest.py b/tests/end2end/conftest.py
index 136d6eb67..3240cc7b8 100644
--- a/tests/end2end/conftest.py
+++ b/tests/end2end/conftest.py
@@ -85,8 +85,8 @@ def _get_version_tag(tag):
do_skip = {
'==': not qtutils.version_check(version, exact=True,
compiled=False),
- '>=': not qtutils.version_check(version),
- '<': qtutils.version_check(version),
+ '>=': not qtutils.version_check(version, compiled=False),
+ '<': qtutils.version_check(version, compiled=False),
'!=': qtutils.version_check(version, exact=True, compiled=False),
}
return pytest.mark.skipif(do_skip[op], reason='Needs ' + tag)
diff --git a/tests/end2end/features/hints.feature b/tests/end2end/features/hints.feature
index e33b16f68..3a0cb1da0 100644
--- a/tests/end2end/features/hints.feature
+++ b/tests/end2end/features/hints.feature
@@ -165,13 +165,13 @@ Feature: Using hints
And I hint with args "all run message-info {hint-url}" and follow a
Then the message "http://localhost:(port)/data/hello.txt" should be shown
- @qt!=5.11.0
+ @qt<5.11
Scenario: Clicking an invalid link
When I open data/invalid_link.html
And I hint with args "all" and follow a
Then the error "Invalid link clicked - *" should be shown
- @qt!=5.11.0
+ @qt<5.11
Scenario: Clicking an invalid link opening in a new tab
When I open data/invalid_link.html
And I hint with args "all tab" and follow a
diff --git a/tests/end2end/features/history.feature b/tests/end2end/features/history.feature
index 0432c0705..c3791945e 100644
--- a/tests/end2end/features/history.feature
+++ b/tests/end2end/features/history.feature
@@ -118,9 +118,11 @@ Feature: Page history
And I open qute://history
Then the javascript message "XSS" should not be logged
+ @flaky
Scenario: Escaping of URLs in :history
When I open query?one=1&two=2
And I open qute://history
+ And I wait 1s # JS loads the history async
And I hint with args "links normal" and follow a
And I wait until query?one=1&two=2 is loaded
Then the query parameter two should be set to 2
diff --git a/tests/end2end/features/keyinput.feature b/tests/end2end/features/keyinput.feature
index f681012f1..5456b6739 100644
--- a/tests/end2end/features/keyinput.feature
+++ b/tests/end2end/features/keyinput.feature
@@ -18,6 +18,7 @@ Feature: Keyboard input
# input.forward_unbound_keys
+ @qt<5.11.1
Scenario: Forwarding all keys
When I open data/keyinput/log.html
And I set input.forward_unbound_keys to all
@@ -30,6 +31,7 @@ Feature: Keyboard input
And the javascript message "key press: 112" should be logged
And the javascript message "key release: 112" should be logged
+ @qt<5.11.1
Scenario: Forwarding special keys
When I open data/keyinput/log.html
And I set input.forward_unbound_keys to auto
diff --git a/tests/end2end/features/test_prompts_bdd.py b/tests/end2end/features/test_prompts_bdd.py
index 12d9cbeec..0d74700b4 100644
--- a/tests/end2end/features/test_prompts_bdd.py
+++ b/tests/end2end/features/test_prompts_bdd.py
@@ -17,9 +17,13 @@
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
+import time
+
import pytest_bdd as bdd
bdd.scenarios('prompts.feature')
+from qutebrowser.utils import qtutils
+
@bdd.when("I load an SSL page")
def load_ssl_page(quteproc, ssl_server):
@@ -46,14 +50,21 @@ def no_prompt_shown(quteproc):
@bdd.then("a SSL error page should be shown")
def ssl_error_page(request, quteproc):
- if not request.config.webengine:
- line = quteproc.wait_for(message='Error while loading *: SSL '
- 'handshake failed')
- line.expected = True
- quteproc.wait_for(message="Changing title for idx * to 'Error "
- "loading page: *'")
- content = quteproc.get_content().strip()
- assert "Unable to load page" in content
+ if request.config.webengine and qtutils.version_check('5.9'):
+ quteproc.wait_for(message="Certificate error: *")
+ time.sleep(0.5) # Wait for error page to appear
+ content = quteproc.get_content().strip()
+ assert ("ERR_INSECURE_RESPONSE" in content or # Qt <= 5.10
+ "ERR_CERT_AUTHORITY_INVALID" in content) # Qt 5.11
+ else:
+ if not request.config.webengine:
+ line = quteproc.wait_for(message='Error while loading *: SSL '
+ 'handshake failed')
+ line.expected = True
+ quteproc.wait_for(message="Changing title for idx * to 'Error "
+ "loading page: *'")
+ content = quteproc.get_content().strip()
+ assert "Unable to load page" in content
class AbstractCertificateErrorWrapper:
diff --git a/tests/end2end/fixtures/quteprocess.py b/tests/end2end/fixtures/quteprocess.py
index 4101e6142..01cca1ee1 100644
--- a/tests/end2end/fixtures/quteprocess.py
+++ b/tests/end2end/fixtures/quteprocess.py
@@ -204,6 +204,10 @@ def is_ignored_chromium_message(line):
# [30412:30412:0323/074933.387250:ERROR:node_channel.cc(899)] Dropping
# message on closed channel.
'Dropping message on closed channel.',
+ # [2204:1408:0703/113804.788:ERROR:
+ # gpu_process_transport_factory.cc(1019)] Lost UI shared context.
+ 'Lost UI shared context.',
+
]
return any(testutils.pattern_match(pattern=pattern, value=message)
for pattern in ignored_messages)
diff --git a/tests/unit/browser/webengine/test_spell.py b/tests/unit/browser/webengine/test_spell.py
index 1cc2c5d0f..14b343df5 100644
--- a/tests/unit/browser/webengine/test_spell.py
+++ b/tests/unit/browser/webengine/test_spell.py
@@ -92,3 +92,60 @@ def test_local_filename_installed_malformed(tmpdir, monkeypatch, caplog):
(tmpdir / lang_file).ensure()
with caplog.at_level(logging.WARNING):
assert spell.local_filename('en-US') == 'en-US-11-0'
+
+
+class TestInit:
+
+ ENV = 'QTWEBENGINE_DICTIONARIES_PATH'
+
+ @pytest.fixture(autouse=True)
+ def remove_envvar(self, monkeypatch):
+ monkeypatch.delenv(self.ENV, raising=False)
+
+ @pytest.fixture
+ def patch_new_qt(self, monkeypatch):
+ monkeypatch.setattr(spell.qtutils, 'version_check',
+ lambda _ver, compiled: True)
+
+ @pytest.fixture
+ def dict_dir(self, data_tmpdir):
+ return data_tmpdir / 'qtwebengine_dictionaries'
+
+ @pytest.fixture
+ def old_dict_dir(self, monkeypatch, tmpdir):
+ data_dir = tmpdir / 'old'
+ dict_dir = data_dir / 'qtwebengine_dictionaries'
+ (dict_dir / 'somedict').ensure()
+ monkeypatch.setattr(spell.QLibraryInfo, 'location',
+ lambda _arg: str(data_dir))
+ return dict_dir
+
+ def test_old_qt(self, monkeypatch):
+ monkeypatch.setattr(spell.qtutils, 'version_check',
+ lambda _ver, compiled: False)
+ spell.init()
+ assert self.ENV not in os.environ
+
+ def test_new_qt(self, dict_dir, patch_new_qt):
+ spell.init()
+ assert os.environ[self.ENV] == str(dict_dir)
+
+ def test_moving(self, old_dict_dir, dict_dir, patch_new_qt):
+ spell.init()
+ assert (dict_dir / 'somedict').exists()
+
+ def test_moving_oserror(self, mocker, caplog,
+ old_dict_dir, dict_dir, patch_new_qt):
+ mocker.patch('shutil.copytree', side_effect=OSError)
+
+ with caplog.at_level(logging.ERROR):
+ spell.init()
+
+ record = caplog.records[0]
+ assert record.message == 'Failed to copy old dictionaries'
+
+ def test_moving_existing_destdir(self, old_dict_dir, dict_dir,
+ patch_new_qt):
+ dict_dir.ensure(dir=True)
+ spell.init()
+ assert not (dict_dir / 'somedict').exists()
diff --git a/tests/unit/config/test_config.py b/tests/unit/config/test_config.py
index e1ef7ef94..bf4f7c02d 100644
--- a/tests/unit/config/test_config.py
+++ b/tests/unit/config/test_config.py
@@ -642,8 +642,8 @@ class TestConfig:
meth = getattr(conf, method)
with pytest.raises(configexc.BackendError):
with qtbot.assert_not_emitted(conf.changed):
- meth('content.cookies.accept', 'all')
- assert not conf._values['content.cookies.accept']
+ meth('hints.find_implementation', 'javascript')
+ assert not conf._values['hints.find_implementation']
@pytest.mark.parametrize('method, value', [
('set_obj', {}),
diff --git a/tests/unit/keyinput/key_data.py b/tests/unit/keyinput/key_data.py
index bf1ccdede..48b5c8c56 100644
--- a/tests/unit/keyinput/key_data.py
+++ b/tests/unit/keyinput/key_data.py
@@ -374,37 +374,36 @@ KEYS = [
Key('Dead_Hook', 'Hook', qtest=False),
Key('Dead_Horn', 'Horn', qtest=False),
- # Not in Qt 5.10, so data may be wrong!
- Key('Dead_Stroke', qtest=False),
- Key('Dead_Abovecomma', qtest=False),
- Key('Dead_Abovereversedcomma', qtest=False),
- Key('Dead_Doublegrave', qtest=False),
- Key('Dead_Belowring', qtest=False),
- Key('Dead_Belowmacron', qtest=False),
- Key('Dead_Belowcircumflex', qtest=False),
- Key('Dead_Belowtilde', qtest=False),
- Key('Dead_Belowbreve', qtest=False),
- Key('Dead_Belowdiaeresis', qtest=False),
- Key('Dead_Invertedbreve', qtest=False),
- Key('Dead_Belowcomma', qtest=False),
- Key('Dead_Currency', qtest=False),
- Key('Dead_a', qtest=False),
- Key('Dead_A', qtest=False),
- Key('Dead_e', qtest=False),
- Key('Dead_E', qtest=False),
- Key('Dead_i', qtest=False),
- Key('Dead_I', qtest=False),
- Key('Dead_o', qtest=False),
- Key('Dead_O', qtest=False),
- Key('Dead_u', qtest=False),
- Key('Dead_U', qtest=False),
- Key('Dead_Small_Schwa', qtest=False),
- Key('Dead_Capital_Schwa', qtest=False),
- Key('Dead_Greek', qtest=False),
- Key('Dead_Lowline', qtest=False),
- Key('Dead_Aboveverticalline', qtest=False),
- Key('Dead_Belowverticalline', qtest=False),
- Key('Dead_Longsolidusoverlay', qtest=False),
+ Key('Dead_Stroke', '̵', qtest=False),
+ Key('Dead_Abovecomma', '̓', qtest=False),
+ Key('Dead_Abovereversedcomma', '̔', qtest=False),
+ Key('Dead_Doublegrave', '̏', qtest=False),
+ Key('Dead_Belowring', '̥', qtest=False),
+ Key('Dead_Belowmacron', '̱', qtest=False),
+ Key('Dead_Belowcircumflex', '̭', qtest=False),
+ Key('Dead_Belowtilde', '̰', qtest=False),
+ Key('Dead_Belowbreve', '̮', qtest=False),
+ Key('Dead_Belowdiaeresis', '̤', qtest=False),
+ Key('Dead_Invertedbreve', '̑', qtest=False),
+ Key('Dead_Belowcomma', '̦', qtest=False),
+ Key('Dead_Currency', '¤', qtest=False),
+ Key('Dead_a', 'a', qtest=False),
+ Key('Dead_A', 'A', qtest=False),
+ Key('Dead_e', 'e', qtest=False),
+ Key('Dead_E', 'E', qtest=False),
+ Key('Dead_i', 'i', qtest=False),
+ Key('Dead_I', 'I', qtest=False),
+ Key('Dead_o', 'o', qtest=False),
+ Key('Dead_O', 'O', qtest=False),
+ Key('Dead_u', 'u', qtest=False),
+ Key('Dead_U', 'U', qtest=False),
+ Key('Dead_Small_Schwa', 'ə', qtest=False),
+ Key('Dead_Capital_Schwa', 'Ə', qtest=False),
+ Key('Dead_Greek', 'Greek', qtest=False),
+ Key('Dead_Lowline', '̲', qtest=False),
+ Key('Dead_Aboveverticalline', '̍', qtest=False),
+ Key('Dead_Belowverticalline', '\u0329', qtest=False),
+ Key('Dead_Longsolidusoverlay', '̸', qtest=False),
### multimedia/internet keys - ignored by default - see QKeyEvent c'tor
Key('Back'),
diff --git a/tests/unit/utils/test_qtutils.py b/tests/unit/utils/test_qtutils.py
index 6de085b25..3a848474f 100644
--- a/tests/unit/utils/test_qtutils.py
+++ b/tests/unit/utils/test_qtutils.py
@@ -25,13 +25,6 @@ import os
import os.path
import unittest
import unittest.mock
-try:
- # pylint: disable=no-name-in-module,useless-suppression
- from test import test_file
- # pylint: enable=no-name-in-module,useless-suppression
-except ImportError:
- # Debian patches Python to remove the tests...
- test_file = None
import pytest
from PyQt5.QtCore import (QDataStream, QPoint, QUrl, QByteArray, QIODevice,
@@ -40,6 +33,20 @@ from PyQt5.QtCore import (QDataStream, QPoint, QUrl, QByteArray, QIODevice,
from qutebrowser.utils import qtutils, utils
import overflow_test_cases
+if utils.is_linux:
+ # Those are not run on macOS because that seems to cause a hang sometimes.
+ # On Windows, we don't run them either because of
+ # https://github.com/pytest-dev/pytest/issues/3650
+ try:
+ # pylint: disable=no-name-in-module,useless-suppression
+ from test import test_file
+ # pylint: enable=no-name-in-module,useless-suppression
+ except ImportError:
+ # Debian patches Python to remove the tests...
+ test_file = None
+else:
+ test_file = None
+
# pylint: disable=bad-continuation
@pytest.mark.parametrize(['qversion', 'compiled', 'pyqt', 'version', 'exact',
@@ -476,13 +483,11 @@ class TestSavefileOpen:
assert data == b'foo\nbar\nbaz'
-if test_file is not None and not utils.is_mac:
+if test_file is not None:
# If we were able to import Python's test_file module, we run some code
# here which defines unittest TestCases to run the python tests over
# PyQIODevice.
- # Those are not run on macOS because that seems to cause a hang sometimes.
-
@pytest.fixture(scope='session', autouse=True)
def clean_up_python_testfile():
"""Clean up the python testfile after tests if tests didn't."""
diff --git a/tests/unit/utils/test_standarddir.py b/tests/unit/utils/test_standarddir.py
index a483ede64..b9777f670 100644
--- a/tests/unit/utils/test_standarddir.py
+++ b/tests/unit/utils/test_standarddir.py
@@ -183,10 +183,13 @@ class TestStandardDir:
@pytest.mark.qt_log_ignore(r'^QStandardPaths: ')
def test_linux_invalid_runtimedir(self, monkeypatch, tmpdir):
"""With invalid XDG_RUNTIME_DIR, fall back to TempLocation."""
+ tmpdir_env = tmpdir / 'temp'
+ tmpdir_env.ensure(dir=True)
monkeypatch.setenv('XDG_RUNTIME_DIR', str(tmpdir / 'does-not-exist'))
- monkeypatch.setenv('TMPDIR', str(tmpdir / 'temp'))
+ monkeypatch.setenv('TMPDIR', tmpdir_env)
+
standarddir._init_dirs()
- assert standarddir.runtime() == str(tmpdir / 'temp' / APPNAME)
+ assert standarddir.runtime() == str(tmpdir_env / APPNAME)
@pytest.mark.fake_os('windows')
def test_runtimedir_empty_tempdir(self, monkeypatch, tmpdir):
diff --git a/tests/unit/utils/test_version.py b/tests/unit/utils/test_version.py
index 683fba02e..e2fbf8f1c 100644
--- a/tests/unit/utils/test_version.py
+++ b/tests/unit/utils/test_version.py
@@ -659,7 +659,7 @@ class TestModuleVersions:
The aim of this test is to fail if that gets missing in some future
version of sip.
"""
- import sip
+ from qutebrowser.qt import sip
assert isinstance(sip.SIP_VERSION_STR, str)
diff --git a/tox.ini b/tox.ini
index 4e155df57..21baa7be5 100644
--- a/tox.ini
+++ b/tox.ini
@@ -4,7 +4,7 @@
# and then run "tox" from this directory.
[tox]
-envlist = py36-pyqt59-cov,misc,vulture,flake8,pylint,pyroma,check-manifest,eslint
+envlist = py36-pyqt511-cov,misc,vulture,flake8,pylint,pyroma,check-manifest,eslint
distshare = {toxworkdir}
skipsdist = true
@@ -13,14 +13,14 @@ skipsdist = true
setenv =
QT_QPA_PLATFORM_PLUGIN_PATH={envdir}/Lib/site-packages/PyQt5/plugins/platforms
PYTEST_QT_API=pyqt5
- pyqt{,56,571,59,510}: LINK_PYQT_SKIP=true
- pyqt{,56,571,59,510}: QUTE_BDD_WEBENGINE=true
+ pyqt{,56,571,59,510,511}: LINK_PYQT_SKIP=true
+ pyqt{,56,571,59,510,511}: QUTE_BDD_WEBENGINE=true
cov: PYTEST_ADDOPTS=--cov --cov-report xml --cov-report=html --cov-report=
passenv = PYTHON DISPLAY XAUTHORITY HOME USERNAME USER CI TRAVIS XDG_* QUTE_* DOCKER
basepython =
- py35: python3.5
- py36: python3.6
- py37: python3.7
+ py35: {env:PYTHON:python3.5}
+ py36: {env:PYTHON:python3.6}
+ py37: {env:PYTHON:python3.7}
deps =
-r{toxinidir}/requirements.txt
-r{toxinidir}/misc/requirements/requirements-tests.txt
@@ -28,6 +28,7 @@ deps =
pyqt571: PyQt5==5.7.1
pyqt59: PyQt5==5.9.2
pyqt510: PyQt5==5.10.1
+ pyqt511: PyQt5==5.11.2
commands =
{envpython} scripts/link_pyqt.py --tox {envdir}
{envpython} -bb -m pytest {posargs:tests}
@@ -59,7 +60,7 @@ commands = {envpython} -c ""
usedevelop = true
deps =
-r{toxinidir}/requirements.txt
- -r{toxinidir}/misc/requirements/requirements-pyqt-old.txt
+ -r{toxinidir}/misc/requirements/requirements-pyqt.txt
[testenv:misc]
ignore_errors = true