diff --git a/.travis.yml b/.travis.yml index 8b6e7c91c..ec2868730 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,7 @@ sudo: required dist: trusty language: generic +group: edge matrix: include: @@ -15,9 +16,6 @@ matrix: - os: linux env: DOCKER=archlinux-webengine QUTE_BDD_WEBENGINE=true services: docker - - os: linux - env: DOCKER=archlinux-ng - services: docker - os: linux env: DOCKER=ubuntu-xenial services: docker @@ -25,14 +23,18 @@ matrix: language: python python: 3.6 env: TESTENV=py36-pyqt571 + - os: linux + language: python + python: 3.6 + env: TESTENV=py36-pyqt58 - os: linux language: python python: 3.5 - env: TESTENV=py35-pyqt58 + env: TESTENV=py35-pyqt59 - os: linux language: python python: 3.6 - env: TESTENV=py36-pyqt58 + env: TESTENV=py36-pyqt59 - os: osx env: TESTENV=py36 OSX=elcapitan osx_image: xcode7.3 diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index dffdff139..14fd41b88 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -14,8 +14,8 @@ This project adheres to http://semver.org/[Semantic Versioning]. // `Fixed` for any bug fixes. // `Security` to invite users to upgrade in case of vulnerabilities. -v0.11.0 (unreleased) --------------------- +v0.11.0 +------- New dependencies ~~~~~~~~~~~~~~~~ @@ -23,13 +23,15 @@ New dependencies - New dependency on `PyQt5.QtOpenGL` if QtWebEngine is used. QtWebEngine depends on QtOpenGL already, but on distributions packaging split PyQt5 wrappers, the wrappers for QtOpenGL are now required. -- New optional (but recommended) `PyOpenGL` dependency with QtWebEngine to work - around a bug where a black screen is shown with some setups. +- New dependency on `PyOpenGL` if QtWebEngine is used. Added ~~~~~ -- New `-p` flag for `:open` to open a private window. +- Private browsing is now implemented for QtWebEngine, *and changed its + behavior*: The `general -> private-browsing` setting now only applies to newly + opened windows, and you can use the `-p` flag to `:open` to open a private + window. - New "pinned tabs" feature, with a new `:tab-pin` command (bound to `` by default). - (QtWebEngine) Implemented `:follow-selected`. @@ -46,6 +48,8 @@ Added customize statusbar colors for private windows. - New `{private}` field displaying `[Private Mode]` for `ui -> window-title-format` and `tabs -> title-format`. +- (QtWebEngine) Proxy support with Qt 5.7.1 (already was supported for 5.8 and + newer) Changed ~~~~~~~ @@ -53,60 +57,51 @@ Changed - To prevent elaborate phishing attacks, the Punycode version (`xn--*`) is now shown in addition to the decoded version for international domain names (IDN). -- Private browsing is now implemented for QtWebEngine, and changed it's - behavior: The `general -> private-browsing` setting now only applies to newly - opened windows, and you can use the `-p` flag to `:open` to open a private - window. -- Improved `qute://history` page (with lazy loading) -- Starting with legacy QtWebKit now shows a warning message once. -- Crash reports are not public anymore. -- Paths like `C:` are now treated as absolute paths on Windows for downloads, - and invalid paths are handled properly. -- PAC on QtWebKit now supports SOCKS5 as type. -- Comments in the config file are now before the individual options instead of - being before sections. -- Messages are now hidden when clicked. -- stdin is now closed immediately for processes spawned from qutebrowser. -- When `ui -> message-timeout` is set to 0, messages are now never cleared. -- Middle/right-clicking the blank parts of the tab bar (when vertical) now - closes the current tab. -- (QtWebEngine) With Qt 5.9, `content -> cookies-store` can now be set without - a restart. -- (QtWebEngine) With Qt 5.9, better error messages are now shown for failed - downloads. -- The adblocker now also blocks non-GET requests (e.g. POST). -- `javascript:` links can now be hinted. -- `:view-source`, `:tab-clone` and `:navigate --tab` now don't open the tab as - "explicit" anymore, i.e. (with the default settings) open it next to the - active tab. -- (QtWebEngine) The underlying Chromium version is now shown in the version - info. -- `qute:*` pages now use `qute://*` instead (e.g. `qute://version` instead of - `qute:version`), but the old versions are automatically redirected. +- Starting with legacy QtWebKit now shows a warning message. + *With the next release, support for it will be removed.* - The Windows releases are redone from scratch, which means: - They now use the new QtWebEngine backend - The bundled Qt is updated from 5.5 to 5.9 - The bundled Python is updated from 3.4 to 3.6 - They are now generated with PyInstaller instead of cx_Freeze - The installer is now generated using NSIS instead of being a MSI +- Improved `qute://history` page (with lazy loading) +- Crash reports are not public anymore. +- Paths like `C:` are now treated as absolute paths on Windows for downloads, + and invalid paths are handled properly. +- Comments in the config file are now placed before the individual options + instead of being before sections. +- Messages are now hidden when clicked. +- stdin is now closed immediately for processes spawned from qutebrowser. +- When `ui -> message-timeout` is set to 0, messages are now never cleared. +- Middle/right-clicking the blank parts of the tab bar (when vertical) now + closes the current tab. +- The adblocker now also blocks non-GET requests (e.g. POST). +- `javascript:` links can now be hinted. +- `:view-source`, `:tab-clone` and `:navigate --tab` now don't open the tab as + "explicit" anymore, i.e. (with the default settings) open it next to the + active tab. +- `qute:*` pages now use `qute://*` instead (e.g. `qute://version` instead of + `qute:version`), but the old versions are automatically redirected. - Texts in prompts are now selectable. -- Renderer process crashes now show an error page. -- (QtWebKit) storage -> offline-web-application-storage` got renamed to `...-cache` - The default level for `:messages` is now `info`, not `error` +- Trying to focus the currently focused tab with `:tab-focus` now focuses the + last viewed tab. +- (QtWebEngine) With Qt 5.9, `content -> cookies-store` can now be set without + a restart. +- (QtWebEngine) With Qt 5.9, better error messages are now shown for failed + downloads. +- (QtWebEngine) The underlying Chromium version is now shown in the version + info. +- (QtWebKit) Renderer process crashes now show an error page on Qt 5.9 or newer. +- (QtWebKit) storage -> offline-web-application-storage` got renamed to `...-cache` +- (QtWebKit) PAC now supports SOCKS5 as type. Fixed ~~~~~ - The macOS .dmg is now built against Qt 5.9 which fixes various important issues (such as not being able to type dead keys). -- (QtWebEngine) Added a workaround for a black screen with some setups - (the workaround requires PyOpenGL to be installed, but it's optional) -- (QtWebEngine) Starting with Nouveau graphics now shows an error message - instead of crashing in Qt. This adds a new dependency on `PyQt5.QtOpenGL`. -- (QtWebEngine) Retrying downloads now shows an error instead of crashing. -- (QtWebEngine) Cloning a view-source tab now doesn't crash anymore. -- (QtWebKit) The HTTP cache is disabled on Qt 5.7.1 and 5.8 now as it leads to - frequent crashes due to a Qt bug. - Fixed crash with `:download` on PyQt 5.9. - Cloning a page without history doesn't crash anymore. - When a download results in a HTTP error, it now shows the error correctly @@ -116,7 +111,6 @@ Fixed - Fixed crash when unbinding an unbound key in the key config. - Fixed crash when using `:debug-log-filter` when `--filter` wasn't given on startup. - Fixed crash with some invalid setting values. -- (QtWebKit) Fixed Crash when a PAC file returns an invalid value. - Continuing a search after clearing it now works correctly. - The tabbar and completion should now be more consistently and correctly styled with various system styles. @@ -124,18 +118,27 @@ Fixed - The validation for colors in stylesheets is now less strict, allowing for all valid Qt values. - `data:` URLs now aren't added to the history anymore. -- (QtWebEngine) `window.navigator.userAgent` is now set correctly when - customizing the user agent. - Accidentally starting with Python 2 now shows a proper error message again. -- (QtWebEngine) HTML fullscreen is now tracked for each tab separately, which - means it's not possible anymore to accidentally get stuck in fullscreen state - by closing a tab with a fullscreen video. - For some people, running some userscripts crashed - this should now be fixed. - Various other rare crashes should now be fixed. - The settings documentation was truncated with v0.10.1 which should now be fixed. - Scrolling to an anchor in a background tab now works correctly, and javascript gets the correct window size for background tabs. +- (QtWebEngine) Added a workaround for a black screen with some setups +- (QtWebEngine) Starting with Nouveau graphics now shows an error message + instead of crashing in Qt. +- (QtWebEngine) Retrying downloads now shows an error instead of crashing. +- (QtWebEngine) Cloning a view-source tab now doesn't crash anymore. +- (QtWebEngine) `window.navigator.userAgent` is now set correctly when + customizing the user agent. +- (QtWebEngine) HTML fullscreen is now tracked for each tab separately, which + means it's not possible anymore to accidentally get stuck in fullscreen state + by closing a tab with a fullscreen video. +- (QtWebEngine) `:scroll-page` with `--bottom-navigate` now works correctly. +- (QtWebKit) The HTTP cache is disabled on Qt 5.7.1 and 5.8 now as it leads to + frequent crashes due to a Qt bug. +- (QtWebKit) Fixed Crash when a PAC file returns an invalid value. v0.10.1 ------- diff --git a/CONTRIBUTING.asciidoc b/CONTRIBUTING.asciidoc index d5c77f521..2ac99804e 100644 --- a/CONTRIBUTING.asciidoc +++ b/CONTRIBUTING.asciidoc @@ -682,8 +682,9 @@ qutebrowser release * Add newest config to `tests/unit/config/old_configs` and update `test_upgrade_version` - `python -m qutebrowser --basedir conf :quit` - - `sed '/^#/d' conf/config/qutebrowser.conf > tests/unit/config/old_configs/qutebrowser-v0.x.y.conf` + - `sed '/^#/d' conf/config/qutebrowser.conf > tests/unit/config/old_configs/qutebrowser-v0.$x.$y.conf` - `rm -r conf` + - git add - commit * Adjust `__version_info__` in `qutebrowser/__init__.py`. * Update changelog (remove *(unreleased)*) @@ -698,7 +699,7 @@ qutebrowser release as closed. * Linux: Run `python3 scripts/dev/build_release.py --upload v0.$x.$y` -* Windows: Run `C:\Python34_x32\python scripts\dev\build_release.py --asciidoc C:\Python27\python C:\asciidoc-8.6.9\asciidoc.py --upload v0.X.Y` (replace X/Y by hand) +* Windows: Run `C:\Python36-32\python scripts\dev\build_release.py --asciidoc C:\Python27\python C:\asciidoc-8.6.9\asciidoc.py --upload v0.X.Y` (replace X/Y by hand) * OS X: Run `python3 scripts/dev/build_release.py --upload v0.X.Y` (replace X/Y by hand) * On server: Run `python3 scripts/dev/download_release.py v0.X.Y` (replace X/Y by hand) * Update `qutebrowser-git` PKGBUILD if dependencies/install changed diff --git a/INSTALL.asciidoc b/INSTALL.asciidoc index 48427e121..5a3ef5dae 100644 --- a/INSTALL.asciidoc +++ b/INSTALL.asciidoc @@ -97,8 +97,9 @@ qutebrowser is available in the official [community] repository. # pacman -S qutebrowser ---- -It's also recommended to install `qt5-webkit-ng` or `qt5-webengine` (and start -with `--backend webengine`) to use an up-to-date backend. +Archlinux packages an updated `qt5-webkit` package by default. If you want to +use the QtWebEngine backend instead, install `qt5-webengine` and start with +`--backend webengine`. There is also a -git version available in the AUR: https://aur.archlinux.org/packages/qutebrowser-git/[qutebrowser-git]. @@ -370,14 +371,6 @@ your `$PATH` (e.g. `/usr/local/bin/qutebrowser` or `~/bin/qutebrowser`): ~/path/to/qutebrowser/.venv/bin/python3 -m qutebrowser "$@" ---- -If you are developing on qutebrowser, you may want to redirect it to a local -config: - ----- -#!/bin/bash -~/path/to/qutebrowser/.venv/bin/python3 -m qutebrowser -c .qutebrowser-local "$@" ----- - Updating ~~~~~~~~ diff --git a/README.asciidoc b/README.asciidoc index 7c0462ba7..1c7e42c33 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -99,15 +99,16 @@ Requirements The following software and libraries are required to run qutebrowser: * http://www.python.org/[Python] 3.4 or newer (3.5 recommended) -* http://qt.io/[Qt] 5.2.0 or newer (5.9.0 recommended) +* http://qt.io/[Qt] 5.2.0 or newer (5.9 recommended) * QtWebKit (old or link:https://github.com/annulen/webkit/wiki[reloaded]/NG) or QtWebEngine * http://www.riverbankcomputing.com/software/pyqt/intro[PyQt] 5.2.0 or newer -(5.8.1 recommended) for Python 3 +(5.9 recommended) for Python 3 * https://pypi.python.org/pypi/setuptools/[pkg_resources/setuptools] * http://fdik.org/pyPEG/[pyPEG2] * http://jinja.pocoo.org/[jinja2] * http://pygments.org/[pygments] * http://pyyaml.org/wiki/PyYAML[PyYAML] +* http://pyopengl.sourceforge.net/[PyOpenGL] when using QtWebEngine The following libraries are optional and provide a better user experience: @@ -237,6 +238,7 @@ Contributors, sorted by the number of commits in descending order: * Johannes Altmanninger * Jeremy Kaplan * Ismail +* Iordanis Grigoriou * Edgar Hipp * Daryl Finlay * arza @@ -279,6 +281,7 @@ Contributors, sorted by the number of commits in descending order: * Lazlow Carmichael * Kevin Wang * Ján Kobezda +* Justin Partain * Johannes Martinsson * Jean-Christophe Petkovich * Helen Sherwood-Taylor diff --git a/doc/backers.asciidoc b/doc/backers.asciidoc index c64a1631d..811cde75e 100644 --- a/doc/backers.asciidoc +++ b/doc/backers.asciidoc @@ -34,6 +34,7 @@ TODO: people with t-shirts or higher pledge levels - Benedikt Steindorf - Bernardo Kuri - Blaise Duszynski +- Bostan - Bruno Oliveira - Colin Jacobs - Daniel Andersson diff --git a/doc/help/commands.asciidoc b/doc/help/commands.asciidoc index c3ff94d35..05bf3ad93 100644 --- a/doc/help/commands.asciidoc +++ b/doc/help/commands.asciidoc @@ -85,7 +85,7 @@ It is possible to run or bind multiple commands by separating them with `;;`. |<>|Pin/Unpin the current/[count]th tab. |<>|Switch to the previous tab, or switch [count] tabs back. |<>|Unbind a keychain. -|<>|Re-open a closed tab (optionally skipping [count] closed tabs). +|<>|Re-open a closed tab. |<>|Show the source of the current page in a new tab. |<>|Close all windows except for the current one. |<>|Save open pages and quit. @@ -936,7 +936,7 @@ Unbind a keychain. [[undo]] === undo -Re-open a closed tab (optionally skipping [count] closed tabs). +Re-open a closed tab. [[view-source]] === view-source diff --git a/doc/help/settings.asciidoc b/doc/help/settings.asciidoc index 9c362dbca..b038d462d 100644 --- a/doc/help/settings.asciidoc +++ b/doc/help/settings.asciidoc @@ -789,8 +789,6 @@ The proxy to use. In addition to the listed values, you can use a `socks://...` or `http://...` URL. -This setting only works with Qt 5.8 or newer when using the QtWebEngine backend. - Valid values: * +system+: Use the system wide proxy. diff --git a/doc/qutebrowser.1.asciidoc b/doc/qutebrowser.1.asciidoc index cd95bf8e5..1b8345b0c 100644 --- a/doc/qutebrowser.1.asciidoc +++ b/doc/qutebrowser.1.asciidoc @@ -57,7 +57,7 @@ show it. How URLs should be opened if there is already a qutebrowser instance running. *--backend* '{webkit,webengine}':: - Which backend to use (webengine backend is EXPERIMENTAL!). + Which backend to use. *--enable-webengine-inspector*:: Enable the web inspector for QtWebEngine. Note that this is a SECURITY RISK and you should not visit untrusted websites with the inspector turned on. See https://bugreports.qt.io/browse/QTBUG-50725 for more details. diff --git a/misc/qutebrowser.spec b/misc/qutebrowser.spec index 5dc51015d..cd0ce3883 100644 --- a/misc/qutebrowser.spec +++ b/misc/qutebrowser.spec @@ -41,7 +41,7 @@ a = Analysis(['../qutebrowser/__main__.py'], pathex=['misc'], binaries=None, datas=get_data_files(), - hiddenimports=['PyQt5.QtOpenGL'], + hiddenimports=['PyQt5.QtOpenGL', 'PyQt5._QOpenGLFunctions_2_0'], hookspath=[], runtime_hooks=[], excludes=['tkinter'], diff --git a/misc/requirements/requirements-codecov.txt b/misc/requirements/requirements-codecov.txt index a72dae55d..86f78562d 100644 --- a/misc/requirements/requirements-codecov.txt +++ b/misc/requirements/requirements-codecov.txt @@ -5,5 +5,5 @@ chardet==3.0.4 codecov==2.0.9 coverage==4.4.1 idna==2.5 -requests==2.17.3 +requests==2.18.1 urllib3==1.21.1 diff --git a/misc/requirements/requirements-pylint-master.txt b/misc/requirements/requirements-pylint-master.txt index b6bce81e5..37e705b7a 100644 --- a/misc/requirements/requirements-pylint-master.txt +++ b/misc/requirements/requirements-pylint-master.txt @@ -10,7 +10,7 @@ lazy-object-proxy==1.3.1 mccabe==0.6.1 -e git+https://github.com/PyCQA/pylint.git#egg=pylint ./scripts/dev/pylint_checkers -requests==2.17.3 +requests==2.18.1 six==1.10.0 uritemplate==3.0.0 uritemplate.py==3.0.2 diff --git a/misc/requirements/requirements-pylint.txt b/misc/requirements/requirements-pylint.txt index 744677ae7..a76d0dbf4 100644 --- a/misc/requirements/requirements-pylint.txt +++ b/misc/requirements/requirements-pylint.txt @@ -8,9 +8,9 @@ idna==2.5 isort==4.2.15 lazy-object-proxy==1.3.1 mccabe==0.6.1 -pylint==1.7.1 +pylint==1.7.2 ./scripts/dev/pylint_checkers -requests==2.17.3 +requests==2.18.1 six==1.10.0 uritemplate==3.0.0 uritemplate.py==3.0.2 diff --git a/misc/requirements/requirements-pyqt.txt b/misc/requirements/requirements-pyqt.txt index da611589a..fffa133ab 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.8.2 -sip==4.19.2 +PyQt5==5.9 +sip==4.19.3 diff --git a/misc/requirements/requirements-tests.txt b/misc/requirements/requirements-tests.txt index 47cdca6f4..57a4daff7 100644 --- a/misc/requirements/requirements-tests.txt +++ b/misc/requirements/requirements-tests.txt @@ -1,7 +1,7 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py beautifulsoup4==4.6.0 -cheroot==5.5.0 +cheroot==5.7.0 click==6.7 # colorama==0.3.9 coverage==4.4.1 @@ -12,7 +12,7 @@ Flask==0.12.2 glob2==0.5 httpbin==0.5.0 hunter==1.4.1 -hypothesis==3.11.3 +hypothesis==3.11.6 itsdangerous==0.24 # Jinja2==2.9.6 Mako==1.0.6 @@ -30,7 +30,7 @@ pytest-instafail==0.3.0 pytest-mock==1.6.0 pytest-qt==2.1.0 pytest-repeat==0.4.1 -pytest-rerunfailures==2.1.0 +pytest-rerunfailures==2.2 pytest-travis-fold==1.2.0 pytest-xvfb==1.0.0 PyVirtualDisplay==0.2.1 diff --git a/pytest.ini b/pytest.ini index a2d2779d9..ab437801b 100644 --- a/pytest.ini +++ b/pytest.ini @@ -45,6 +45,7 @@ qt_log_ignore = ^QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to .* ^QGeoclueMaster error creating GeoclueMasterClient\. ^Geoclue error: Process org\.freedesktop\.Geoclue\.Master exited with status 127 + ^QDBusConnection: name 'org.freedesktop.Geoclue.Master' had owner '' but we thought it was ':1.1' ^QObject::connect: Cannot connect \(null\)::stateChanged\(QNetworkSession::State\) to QNetworkReplyHttpImpl::_q_networkSessionStateChanged\(QNetworkSession::State\) ^QXcbClipboard: Cannot transfer data, no data available ^load glyph failed diff --git a/qutebrowser/__init__.py b/qutebrowser/__init__.py index e61419c0c..cca2bf1b8 100644 --- a/qutebrowser/__init__.py +++ b/qutebrowser/__init__.py @@ -26,7 +26,7 @@ __copyright__ = "Copyright 2014-2017 Florian Bruhin (The Compiler)" __license__ = "GPL" __maintainer__ = __author__ __email__ = "mail@qutebrowser.org" -__version_info__ = (0, 10, 1) +__version_info__ = (0, 11, 0) __version__ = '.'.join(str(e) for e in __version_info__) __description__ = "A keyboard-driven, vim-like browser based on PyQt5." diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 4eefb38d8..677f79a7b 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -414,10 +414,8 @@ def _init_modules(args, crash_handler): log.init.debug("Initializing network...") networkmanager.init() - if qtutils.version_check('5.8'): - # Otherwise we can only initialize it for QtWebKit because of crashes - log.init.debug("Initializing proxy...") - proxy.init() + log.init.debug("Initializing proxy...") + proxy.init() log.init.debug("Initializing readline-bridge...") readline_bridge = readline.ReadlineBridge() diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 3ec4c63c0..97167758f 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -157,12 +157,14 @@ class CommandDispatcher: else: return None - def _tab_focus_last(self): + def _tab_focus_last(self, *, show_error=True): """Select the tab which was last focused.""" try: tab = objreg.get('last-focused-tab', scope='window', window=self._win_id) except KeyError: + if not show_error: + return raise cmdexc.CommandError("No last focused tab!") idx = self._tabbed_browser.indexOf(tab) if idx == -1: @@ -278,9 +280,7 @@ class CommandDispatcher: return to_pin = not tab.data.pinned - tab_index = self._current_index() if count is None else count - 1 - cmdutils.check_overflow(tab_index + 1, 'int') - self._tabbed_browser.set_tab_pinned(tab_index, to_pin) + self._tabbed_browser.set_tab_pinned(tab, to_pin) @cmdutils.register(instance='command-dispatcher', name='open', maxsplit=0, scope='window') @@ -513,7 +513,7 @@ class CommandDispatcher: newtab.data.keep_icon = True newtab.history.deserialize(history) newtab.zoom.set_factor(curtab.zoom.factor()) - new_tabbed_browser.set_tab_pinned(idx, curtab.data.pinned) + new_tabbed_browser.set_tab_pinned(newtab, curtab.data.pinned) return newtab @cmdutils.register(instance='command-dispatcher', scope='window') @@ -866,7 +866,7 @@ class CommandDispatcher: @cmdutils.register(instance='command-dispatcher', scope='window') @cmdutils.argument('count', count=True) - def zoom(self, zoom: int = None, count=None): + def zoom(self, zoom=None, count=None): """Set the zoom level for the current tab. The zoom can be given as argument or as [count]. If neither is @@ -877,6 +877,13 @@ class CommandDispatcher: zoom: The zoom percentage to set. count: The zoom percentage to set. """ + if zoom is not None: + try: + zoom = int(zoom.rstrip('%')) + except ValueError: + raise cmdexc.CommandError("zoom: Invalid int value {}" + .format(zoom)) + level = count if count is not None else zoom if level is None: level = config.get('ui', 'default-zoom') @@ -923,7 +930,7 @@ class CommandDispatcher: @cmdutils.register(instance='command-dispatcher', scope='window') def undo(self): - """Re-open a closed tab (optionally skipping [count] closed tabs).""" + """Re-open a closed tab.""" try: self._tabbed_browser.undo() except IndexError: @@ -1075,12 +1082,15 @@ class CommandDispatcher: last tab. count: The tab index to focus, starting with 1. """ + index = count if count is not None else index + if index == 'last': self._tab_focus_last() return - index = count if count is not None else index - - if index is None: + elif index == self._current_index() + 1: + self._tab_focus_last(show_error=False) + return + elif index is None: self.tab_next() return diff --git a/qutebrowser/browser/qutescheme.py b/qutebrowser/browser/qutescheme.py index e25b7df07..0e05cd7fb 100644 --- a/qutebrowser/browser/qutescheme.py +++ b/qutebrowser/browser/qutescheme.py @@ -385,5 +385,6 @@ def qute_backend_warning(_url): html = jinja.render('backend-warning.html', distribution=version.distribution(), Distribution=version.Distribution, - version=pkg_resources.parse_version) + version=pkg_resources.parse_version, + title="Legacy backend warning") return 'text/html', html diff --git a/qutebrowser/browser/webengine/webenginesettings.py b/qutebrowser/browser/webengine/webenginesettings.py index 76c2aa4ec..493a2a687 100644 --- a/qutebrowser/browser/webengine/webenginesettings.py +++ b/qutebrowser/browser/webengine/webenginesettings.py @@ -36,8 +36,7 @@ from PyQt5.QtWebEngineWidgets import (QWebEngineSettings, QWebEngineProfile, from qutebrowser.browser import shared from qutebrowser.config import config, websettings -from qutebrowser.utils import (objreg, utils, standarddir, javascript, log, - qtutils) +from qutebrowser.utils import objreg, utils, standarddir, javascript, qtutils # The default QWebEngineProfile @@ -133,9 +132,6 @@ def _init_stylesheet(profile): Mostly inspired by QupZilla: https://github.com/QupZilla/qupzilla/blob/v2.0/src/lib/app/mainapplication.cpp#L1063-L1101 https://github.com/QupZilla/qupzilla/blob/v2.0/src/lib/tools/scripts.cpp#L119-L132 - - FIXME:qtwebengine Use QWebEngineStyleSheet once that's available - https://codereview.qt-project.org/#/c/148671/ """ old_script = profile.scripts().findScript('_qute_stylesheet') if not old_script.isNull(): @@ -209,12 +205,7 @@ def init(args): if not os.environ.get('QUTE_NO_OPENGL_WORKAROUND'): # Hide "No OpenGL_accelerate module loaded: ..." message logging.getLogger('OpenGL.acceleratesupport').propagate = False - try: - from OpenGL import GL # pylint: disable=unused-variable - except ImportError: - pass - else: - log.misc.debug("Imported PyOpenGL as workaround") + from OpenGL import GL # pylint: disable=unused-variable _init_profiles() diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 8acd0eda0..0a4c2dfc7 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -20,6 +20,7 @@ """Wrapper over a QWebEngineView.""" import os +import math import functools import sip @@ -342,7 +343,7 @@ class WebEngineScroller(browsertab.AbstractScroller): else: perc_y = min(100, round(100 / dy * jsret['px']['y'])) - self._at_bottom = dy >= jsret['px']['y'] + self._at_bottom = math.ceil(jsret['px']['y']) >= dy self._pos_perc = perc_x, perc_y self.perc_changed.emit(*self._pos_perc) diff --git a/qutebrowser/browser/webkit/webkittab.py b/qutebrowser/browser/webkit/webkittab.py index fa626e863..c29aa15cb 100644 --- a/qutebrowser/browser/webkit/webkittab.py +++ b/qutebrowser/browser/webkit/webkittab.py @@ -33,7 +33,6 @@ from PyQt5.QtWebKit import QWebSettings from PyQt5.QtPrintSupport import QPrinter from qutebrowser.browser import browsertab -from qutebrowser.browser.network import proxy from qutebrowser.browser.webkit import webview, tabhistory, webkitelem from qutebrowser.browser.webkit.network import webkitqutescheme from qutebrowser.utils import qtutils, objreg, usertypes, utils, log, debug @@ -42,12 +41,6 @@ from qutebrowser.utils import qtutils, objreg, usertypes, utils, log, debug def init(): """Initialize QtWebKit-specific modules.""" qapp = QApplication.instance() - - if not qtutils.version_check('5.8'): - # Otherwise we initialize it globally in app.py - log.init.debug("Initializing proxy...") - proxy.init() - log.init.debug("Initializing js-bridge...") js_bridge = webkitqutescheme.JSBridge(qapp) objreg.register('js-bridge', js_bridge) diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index fd3235c8b..6af04d9f4 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -437,14 +437,10 @@ def data(readonly=False): "User agent to send. Empty to send the default."), ('proxy', - SettingValue(typ.Proxy(), 'system', - backends=(None if qtutils.version_check('5.8') - else [usertypes.Backend.QtWebKit])), + SettingValue(typ.Proxy(), 'system'), "The proxy to use.\n\n" "In addition to the listed values, you can use a `socks://...` " - "or `http://...` URL.\n\n" - "This setting only works with Qt 5.8 or newer when using the " - "QtWebEngine backend."), + "or `http://...` URL."), ('proxy-dns-requests', SettingValue(typ.Bool(), 'true', diff --git a/qutebrowser/html/backend-warning.html b/qutebrowser/html/backend-warning.html index b04793a35..ffff0e59b 100644 --- a/qutebrowser/html/backend-warning.html +++ b/qutebrowser/html/backend-warning.html @@ -39,9 +39,15 @@ the qute://settings page or caret browsing). If you know more, please open an issue! {%- endmacro %} +{% macro unknown_system() -%} + There's no information available for your system. {{ please_open_issue() }} +{%- endmacro %} +

{% if distribution.parsed == Distribution.ubuntu %} - {% if distribution.version >= version('17.04') %} + {% if distribution.version == none %} + {{ unknown_system() }} + {% elif distribution.version >= version('17.04') %} {{ install_webengine('python3-pyqt5.qtwebengine') }} {% elif distribution.version >= version('16.04') %} QtWebEngine is only available in Ubuntu's repositories since 17.04, but you can install qutebrowser via tox with tox -e mkvenv-pypi to use the new backend. @@ -49,7 +55,9 @@ the qute://settings page or caret browsing). Unfortunately, no easy way is known to install QtWebEngine on Ubuntu < 16.04. {{ please_open_issue() }} {% endif %} {% elif distribution.parsed == Distribution.debian %} - {% if distribution.version >= version('9') %} + {% if distribution.version == none %} + {{ unknown_system() }} + {% elif distribution.version >= version('9') %} {{ install_webengine('python3-pyqt5.qtwebengine') }} {% else %} Unfortunately, no easy way is known to install QtWebEngine on Debian < 9. {{ please_open_issue() }} @@ -63,7 +71,7 @@ the qute://settings page or caret browsing). {% elif distribution.parsed == Distribution.opensuse %} {{ install_webengine('libqt5-qtwebengine') }} {% else %} - There's no information available for your system. {{ please_open_issue() }} + {{ unknown_system() }} {% endif %}

@@ -72,18 +80,18 @@ the qute://settings page or caret browsing). This is a drop-in replacement for legacy QtWebKit.

-{% if distribution.parsed == Distribution.debian and distribution.version >= version('9') %} +{% if distribution.parsed == Distribution.debian and distribution.version != none and distribution.version >= version('9') %} There are unofficial QtWebKit-NG packages available. {% elif distribution.parsed in [Distribution.ubuntu, Distribution.debian] %} No easy way is known to install QtWebKit-NG on your system. There are unofficial QtWebKit-NG packages available, but they are intended for Debian Unstable. {{ please_open_issue() }} {% elif distribution.parsed in [Distribution.arch, Distribution.manjaro] %} - You should be able to install qt5-webkit-ng via pacman. + With an updated qt5-webkit package, you should already get QtWebKit-NG. {% elif distribution.parsed == Distribution.gentoo %} There's an unofficial ebuild available. {% else %} - There's no information available for your system. {{ please_open_issue() }} + {{ unknown_system() }} {% endif %}

diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index b01c9d282..94508ab63 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -273,6 +273,8 @@ class TabbedBrowser(tabwidget.TabWidget): """ idx = self.indexOf(tab) if idx == -1: + if crashed: + return raise TabDeletedError("tab {} is not contained in " "TabbedWidget!".format(tab)) if tab is self._now_focused: @@ -340,7 +342,7 @@ class TabbedBrowser(tabwidget.TabWidget): newtab = self.tabopen(url, background=False, idx=idx) newtab.history.deserialize(history_data) - self.set_tab_pinned(idx, pinned) + self.set_tab_pinned(newtab, pinned) @pyqtSlot('QUrl', bool) def openurl(self, url, newtab): diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index 2c4eb6eab..c7823f84e 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -26,7 +26,7 @@ from PyQt5.QtCore import (pyqtSignal, pyqtSlot, Qt, QSize, QRect, QPoint, QTimer, QUrl) from PyQt5.QtWidgets import (QTabWidget, QTabBar, QSizePolicy, QCommonStyle, QStyle, QStylePainter, QStyleOptionTab, - QStyleFactory) + QStyleFactory, QWidget) from PyQt5.QtGui import QIcon, QPalette, QColor from qutebrowser.utils import qtutils, objreg, utils, usertypes, log @@ -94,17 +94,18 @@ class TabWidget(QTabWidget): bar.set_tab_data(idx, 'indicator-color', color) bar.update(bar.tabRect(idx)) - def set_tab_pinned(self, idx, pinned, *, loading=False): + def set_tab_pinned(self, tab: QWidget, + pinned: bool, *, loading: bool = False) -> None: """Set the tab status as pinned. Args: - idx: The tab index. + tab: The tab to pin pinned: Pinned tab state to set. loading: Whether to ignore current data state when counting pinned_count. """ bar = self.tabBar() - tab = self.widget(idx) + idx = self.indexOf(tab) # Only modify pinned_count if we had a change # always modify pinned_count if we are loading diff --git a/qutebrowser/misc/earlyinit.py b/qutebrowser/misc/earlyinit.py index 21be8ed37..68f5db71e 100644 --- a/qutebrowser/misc/earlyinit.py +++ b/qutebrowser/misc/earlyinit.py @@ -336,13 +336,14 @@ def check_libraries(backend): "http://pyyaml.org/download/pyyaml/ (py3.4) " "or Install via pip.", pip="PyYAML"), - 'PyQt5.QtSql': - _missing_str("PyQt5.QtSql") + 'PyQt5.QtQml': _missing_str("PyQt5.QtQml"), + 'PyQt5.QtSql': _missing_str("PyQt5.QtSql"), } if backend == 'webengine': modules['PyQt5.QtWebEngineWidgets'] = _missing_str("QtWebEngine", webengine=True) modules['PyQt5.QtOpenGL'] = _missing_str("PyQt5.QtOpenGL") + modules['OpenGL'] = _missing_str("PyOpenGL") else: assert backend == 'webkit' modules['PyQt5.QtWebKit'] = _missing_str("PyQt5.QtWebKit") diff --git a/qutebrowser/misc/sessions.py b/qutebrowser/misc/sessions.py index 124b78053..4fe0fe4c7 100644 --- a/qutebrowser/misc/sessions.py +++ b/qutebrowser/misc/sessions.py @@ -407,7 +407,7 @@ class SessionManager(QObject): tab_to_focus = i if new_tab.data.pinned: tabbed_browser.set_tab_pinned( - i, new_tab.data.pinned, loading=True) + new_tab, new_tab.data.pinned, loading=True) if tab_to_focus is not None: tabbed_browser.setCurrentIndex(tab_to_focus) if win.get('active', False): @@ -421,7 +421,10 @@ class SessionManager(QObject): def delete(self, name): """Delete a session.""" path = self._get_session_path(name, check_exists=True) - os.remove(path) + try: + os.remove(path) + except OSError as e: + raise SessionError(e) self.update_completion.emit() def list_sessions(self): @@ -517,7 +520,7 @@ class SessionManager(QObject): self.delete(name) except SessionNotFoundError: raise cmdexc.CommandError("Session {} not found!".format(name)) - except (OSError, SessionError) as e: + except SessionError as e: log.sessions.exception("Error while deleting session!") raise cmdexc.CommandError("Error while deleting session: {}" .format(e)) diff --git a/qutebrowser/qutebrowser.py b/qutebrowser/qutebrowser.py index eba5b6884..e163a9f8d 100644 --- a/qutebrowser/qutebrowser.py +++ b/qutebrowser/qutebrowser.py @@ -64,8 +64,7 @@ def get_argparser(): help="How URLs should be opened if there is already a " "qutebrowser instance running.") parser.add_argument('--backend', choices=['webkit', 'webengine'], - help="Which backend to use (webengine backend is " - "EXPERIMENTAL!).") + help="Which backend to use.") parser.add_argument('--enable-webengine-inspector', action='store_true', help="Enable the web inspector for QtWebEngine. Note " "that this is a SECURITY RISK and you should not " diff --git a/qutebrowser/utils/version.py b/qutebrowser/utils/version.py index a408ce1fd..86af58d3e 100644 --- a/qutebrowser/utils/version.py +++ b/qutebrowser/utils/version.py @@ -186,6 +186,7 @@ def _module_versions(): ('yaml', ['__version__']), ('cssutils', ['__version__']), ('typing', []), + ('OpenGL', ['__version__']), ('PyQt5.QtWebEngineWidgets', []), ('PyQt5.QtWebKitWidgets', []), ]) diff --git a/requirements.txt b/requirements.txt index b2cc93c1f..cbf9ba407 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,4 @@ MarkupSafe==1.0 Pygments==2.2.0 pyPEG2==2.15.2 PyYAML==3.12 +PyOpenGL==3.1.0 diff --git a/scripts/dev/build_release.py b/scripts/dev/build_release.py index 073b9a58e..e6a4f89e1 100755 --- a/scripts/dev/build_release.py +++ b/scripts/dev/build_release.py @@ -64,7 +64,7 @@ def call_tox(toxenv, *args, python=sys.executable): env['PYTHON'] = python env['PATH'] = os.environ['PATH'] + os.pathsep + os.path.dirname(python) subprocess.check_call( - [sys.executable, '-m', 'tox', '-v', '-e', toxenv] + list(args), + [sys.executable, '-m', 'tox', '-vv', '-e', toxenv] + list(args), env=env) @@ -109,8 +109,11 @@ def patch_osx_app(): for f in glob.glob(os.path.join(qtwe_core_dir, 'Resources', '*')): dest = os.path.join(app_path, 'Contents', 'Resources') if os.path.isdir(f): - shutil.copytree(f, os.path.join(dest, f)) + dir_dest = os.path.join(dest, os.path.basename(f)) + print("Copying directory {} to {}".format(f, dir_dest)) + shutil.copytree(f, dir_dest) else: + print("Copying {} to {}".format(f, dest)) shutil.copy(f, dest) # Link dependencies for lib in ['QtCore', 'QtWebEngineCore', 'QtQuick', 'QtQml', 'QtNetwork', @@ -124,7 +127,16 @@ def patch_osx_app(): def build_osx(): """Build OS X .dmg/.app.""" + utils.print_title("Cleaning up...") + for f in ['wc.dmg', 'template.dmg']: + try: + os.remove(f) + except FileNotFoundError: + pass + for d in ['dist', 'build']: + shutil.rmtree(d, ignore_errors=True) utils.print_title("Updating 3rdparty content") + # Currently disabled because QtWebEngine has no pdfjs support # update_3rdparty.run(ace=False, pdfjs=True, fancy_dmg=False) utils.print_title("Building .app via pyinstaller") call_tox('pyinstaller', '-r') @@ -132,25 +144,24 @@ def build_osx(): patch_osx_app() utils.print_title("Building .dmg") subprocess.check_call(['make', '-f', 'scripts/dev/Makefile-dmg']) - utils.print_title("Cleaning up...") - for f in ['wc.dmg', 'template.dmg']: - os.remove(f) - for d in ['dist', 'build']: - shutil.rmtree(d) dmg_name = 'qutebrowser-{}.dmg'.format(qutebrowser.__version__) os.rename('qutebrowser.dmg', dmg_name) utils.print_title("Running smoke test") - with tempfile.TemporaryDirectory() as tmpdir: - subprocess.check_call(['hdiutil', 'attach', dmg_name, - '-mountpoint', tmpdir]) - try: - binary = os.path.join(tmpdir, 'qutebrowser.app', 'Contents', - 'MacOS', 'qutebrowser') - smoke_test(binary) - finally: - subprocess.check_call(['hdiutil', 'detach', tmpdir]) + + try: + with tempfile.TemporaryDirectory() as tmpdir: + subprocess.check_call(['hdiutil', 'attach', dmg_name, + '-mountpoint', tmpdir]) + try: + binary = os.path.join(tmpdir, 'qutebrowser.app', 'Contents', + 'MacOS', 'qutebrowser') + smoke_test(binary) + finally: + subprocess.call(['hdiutil', 'detach', tmpdir]) + except PermissionError as e: + print("Failed to remove tempdir: {}".format(e)) return [(dmg_name, 'application/x-apple-diskimage', 'OS X .dmg')] @@ -167,6 +178,7 @@ def patch_windows(out_dir): def build_windows(): """Build windows executables/setups.""" utils.print_title("Updating 3rdparty content") + # Currently disabled because QtWebEngine has no pdfjs support # update_3rdparty.run(ace=False, pdfjs=True, fancy_dmg=False) utils.print_title("Building Windows binaries") @@ -203,8 +215,8 @@ def build_windows(): '/DVERSION={}'.format(qutebrowser.__version__), 'misc/qutebrowser.nsi']) - name_32 = 'qutebrowser-{}-win32.msi'.format(qutebrowser.__version__) - name_64 = 'qutebrowser-{}-amd64.msi'.format(qutebrowser.__version__) + name_32 = 'qutebrowser-{}-win32.exe'.format(qutebrowser.__version__) + name_64 = 'qutebrowser-{}-amd64.exe'.format(qutebrowser.__version__) artifacts += [ (os.path.join('dist', name_32), diff --git a/scripts/dev/ci/travis_install.sh b/scripts/dev/ci/travis_install.sh index b200fb6d0..53bcf06e8 100644 --- a/scripts/dev/ci/travis_install.sh +++ b/scripts/dev/ci/travis_install.sh @@ -43,6 +43,12 @@ travis_retry() { } apt_install() { + sudo tee /etc/apt/sources.list < prompt-download-directory to false + When the unwritable dir is unwritable + And I set storage -> prompt-download-directory to false And I run :download http://localhost:(port)/data/downloads/download.bin --dest (tmpdir)/downloads/unwritable Then the error "Download error: Permission denied" should be shown diff --git a/tests/end2end/features/javascript.feature b/tests/end2end/features/javascript.feature index c6c7acc15..c6942f6e0 100644 --- a/tests/end2end/features/javascript.feature +++ b/tests/end2end/features/javascript.feature @@ -50,7 +50,7 @@ Feature: Javascript stuff And I open data/javascript/window_open.html in a new tab And I run :click-element id open-normal And I wait for "Changing title for idx 2 to 'about:blank'" in the log - And I run :tab-focus 2 + And I run :buffer window_open.html And I run :click-element id close-twice And I wait for "Focus object changed: *" in the log Then no crash should happen diff --git a/tests/end2end/features/scroll.feature b/tests/end2end/features/scroll.feature index a69b6ffae..28658e1e6 100644 --- a/tests/end2end/features/scroll.feature +++ b/tests/end2end/features/scroll.feature @@ -292,6 +292,13 @@ Feature: Scrolling And I run :scroll-page --bottom-navigate next 0 1 Then data/hello2.txt should be loaded + Scenario: :scroll-page with --bottom-navigate when not at the bottom + When I run :scroll-px 0 10 + And I wait until the scroll position changed + And I run :scroll-page --bottom-navigate next 0 1 + Then the following tabs should be open: + - data/scroll/simple.html + Scenario: :scroll-page with --top-navigate When I run :scroll-page --top-navigate prev 0 -1 Then data/hello3.txt should be loaded diff --git a/tests/end2end/features/tabs.feature b/tests/end2end/features/tabs.feature index 00fe5c16a..8097f390e 100644 --- a/tests/end2end/features/tabs.feature +++ b/tests/end2end/features/tabs.feature @@ -236,6 +236,18 @@ Feature: Tab management - data/numbers/2.txt - data/numbers/3.txt + Scenario: :tab-focus with current tab number + When I open data/numbers/1.txt + And I open data/numbers/2.txt in a new tab + And I open data/numbers/3.txt in a new tab + And I run :tab-focus 1 + And I run :tab-focus 3 + And I run :tab-focus 3 + Then the following tabs should be open: + - data/numbers/1.txt (active) + - data/numbers/2.txt + - data/numbers/3.txt + Scenario: :tab-focus with -1 When I open data/numbers/1.txt And I open data/numbers/2.txt in a new tab @@ -1061,6 +1073,16 @@ Feature: Tab management - data/numbers/2.txt (pinned) - data/numbers/3.txt (active) + Scenario: :tab-pin with an invalid count + When I open data/numbers/1.txt + And I open data/numbers/2.txt in a new tab + And I open data/numbers/3.txt in a new tab + And I run :tab-pin with count 23 + Then the following tabs should be open: + - data/numbers/1.txt + - data/numbers/2.txt + - data/numbers/3.txt (active) + Scenario: Pinned :tab-close prompt yes When I open data/numbers/1.txt And I run :tab-pin diff --git a/tests/end2end/features/test_downloads_bdd.py b/tests/end2end/features/test_downloads_bdd.py index 25eb52aad..4be175a66 100644 --- a/tests/end2end/features/test_downloads_bdd.py +++ b/tests/end2end/features/test_downloads_bdd.py @@ -21,6 +21,7 @@ import os import sys import shlex +import pytest import pytest_bdd as bdd bdd.scenarios('downloads.feature') @@ -53,6 +54,14 @@ def clean_old_downloads(quteproc): quteproc.send_cmd(':download-clear') +@bdd.when("the unwritable dir is unwritable") +def check_unwritable(tmpdir): + unwritable = tmpdir / 'downloads' / 'unwritable' + if os.access(str(unwritable), os.W_OK): + # Docker container or similar + pytest.skip("Unwritable dir was writable") + + @bdd.when("I wait until the download is finished") def wait_for_download_finished(quteproc): quteproc.wait_for(category='downloads', message='Download * finished') diff --git a/tests/end2end/features/zoom.feature b/tests/end2end/features/zoom.feature index 015b85b17..bc36afe0d 100644 --- a/tests/end2end/features/zoom.feature +++ b/tests/end2end/features/zoom.feature @@ -51,6 +51,11 @@ Feature: Zooming in and out Then the message "Zoom level: 50%" should be shown And the zoom should be 50% + Scenario: Setting zoom with trailing % + When I run :zoom 50% + Then the message "Zoom level: 50%" should be shown + And the zoom should be 50% + Scenario: Setting zoom with count When I run :zoom with count 40 Then the message "Zoom level: 40%" should be shown diff --git a/tests/helpers/stubs.py b/tests/helpers/stubs.py index 05d91fac0..61dcefd66 100644 --- a/tests/helpers/stubs.py +++ b/tests/helpers/stubs.py @@ -584,6 +584,9 @@ class TabbedBrowserStub(QObject): self.tabs = [] self.shutting_down = False self._qtabbar = QTabBar() + self.index_of = None + self.current_index = None + self.opened_url = None def count(self): return len(self.tabs) @@ -600,6 +603,26 @@ class TabbedBrowserStub(QObject): def tabBar(self): return self._qtabbar + def indexOf(self, _tab): + if self.index_of is None: + raise ValueError("indexOf got called with index_of None!") + elif self.index_of is RuntimeError: + raise RuntimeError + else: + return self.index_of + + def currentIndex(self): + if self.current_index is None: + raise ValueError("currentIndex got called with current_index " + "None!") + return self.current_index + + def currentWidget(self): + return self.tabs[self.currentIndex() - 1] + + def tabopen(self, url): + self.opened_url = url + class ApplicationStub(QObject): diff --git a/tests/unit/browser/test_commands.py b/tests/unit/browser/test_commands.py deleted file mode 100644 index 5a9d7a8da..000000000 --- a/tests/unit/browser/test_commands.py +++ /dev/null @@ -1,53 +0,0 @@ -# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: - -# Copyright 2015-2017 Florian Bruhin (The Compiler) -# -# 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 . - -import collections - -import pytest - -from qutebrowser.browser import commands -from qutebrowser.mainwindow import tabbedbrowser -from qutebrowser.utils import objreg -from qutebrowser.keyinput import modeman - - -ObjectsRet = collections.namedtuple('Dispatcher', ['tb', 'cd']) - -pytestmark = pytest.mark.usefixtures('cookiejar_and_cache') - - -@pytest.fixture -def objects(qtbot, default_config, key_config_stub, tab_registry, - host_blocker_stub): - """Fixture providing a CommandDispatcher and a fake TabbedBrowser.""" - win_id = 0 - modeman.init(win_id, parent=None) - tabbed_browser = tabbedbrowser.TabbedBrowser(win_id) - qtbot.add_widget(tabbed_browser) - objreg.register('tabbed-browser', tabbed_browser, scope='window', - window=win_id) - dispatcher = commands.CommandDispatcher(win_id, tabbed_browser) - objreg.register('command-dispatcher', dispatcher, scope='window', - window=win_id) - yield ObjectsRet(tabbed_browser, dispatcher) - - -@pytest.mark.skipif(True, reason="Work in progress") -def test_openurl(objects): - objects.cd.openurl('localhost') diff --git a/tests/unit/browser/test_signalfilter.py b/tests/unit/browser/test_signalfilter.py index 4c02f89c4..b56e4ecc0 100644 --- a/tests/unit/browser/test_signalfilter.py +++ b/tests/unit/browser/test_signalfilter.py @@ -29,27 +29,6 @@ from qutebrowser.browser import signalfilter from qutebrowser.utils import objreg -class FakeTabbedBrowser: - - def __init__(self): - self.index_of = None - self.current_index = None - - def indexOf(self, _tab): - if self.index_of is None: - raise ValueError("indexOf got called with index_of None!") - elif self.index_of is RuntimeError: - raise RuntimeError - else: - return self.index_of - - def currentIndex(self): - if self.current_index is None: - raise ValueError("currentIndex got called with current_index " - "None!") - return self.current_index - - class Signaller(QObject): signal = pyqtSignal(str) @@ -84,8 +63,8 @@ def objects(): @pytest.fixture -def tabbed_browser(win_registry): - tb = FakeTabbedBrowser() +def tabbed_browser(stubs, win_registry): + tb = stubs.TabbedBrowserStub() objreg.register('tabbed-browser', tb, scope='window', window=0) yield tb objreg.delete('tabbed-browser', scope='window', window=0) diff --git a/tests/unit/commands/test_argparser.py b/tests/unit/commands/test_argparser.py index cf9eef016..b44fd5dce 100644 --- a/tests/unit/commands/test_argparser.py +++ b/tests/unit/commands/test_argparser.py @@ -31,15 +31,6 @@ from qutebrowser.utils import usertypes, objreg Enum = usertypes.enum('Enum', ['foo', 'foo_bar']) -class FakeTabbedBrowser: - - def __init__(self): - self.opened_url = None - - def tabopen(self, url): - self.opened_url = url - - class TestArgumentParser: @pytest.fixture @@ -47,8 +38,8 @@ class TestArgumentParser: return argparser.ArgumentParser('foo') @pytest.fixture - def tabbed_browser(self, win_registry): - tb = FakeTabbedBrowser() + def tabbed_browser(self, stubs, win_registry): + tb = stubs.TabbedBrowserStub() objreg.register('tabbed-browser', tb, scope='window', window=0) yield tb objreg.delete('tabbed-browser', scope='window', window=0) diff --git a/tests/unit/config/old_configs/qutebrowser-v0.11.0.conf b/tests/unit/config/old_configs/qutebrowser-v0.11.0.conf new file mode 100644 index 000000000..ba48a6ff5 --- /dev/null +++ b/tests/unit/config/old_configs/qutebrowser-v0.11.0.conf @@ -0,0 +1,251 @@ +[general] +ignore-case = smart +startpage = https://start.duckduckgo.com +yank-ignored-url-parameters = ref,utm_source,utm_medium,utm_campaign,utm_term,utm_content +default-open-dispatcher = +default-page = ${startpage} +auto-search = naive +auto-save-config = true +auto-save-interval = 15000 +editor = gvim -f "{}" +editor-encoding = utf-8 +private-browsing = false +developer-extras = false +print-element-backgrounds = true +xss-auditing = false +default-encoding = iso-8859-1 +new-instance-open-target = tab +new-instance-open-target.window = last-focused +log-javascript-console = debug +save-session = false +session-default-name = +url-incdec-segments = path,query +[ui] +history-session-interval = 30 +zoom-levels = 25%,33%,50%,67%,75%,90%,100%,110%,125%,150%,175%,200%,250%,300%,400%,500% +default-zoom = 100% +downloads-position = top +status-position = bottom +message-timeout = 2000 +message-unfocused = false +confirm-quit = never +zoom-text-only = false +frame-flattening = false +user-stylesheet = +hide-scrollbar = true +smooth-scrolling = false +remove-finished-downloads = -1 +hide-statusbar = false +statusbar-padding = 1,1,0,0 +window-title-format = {perc}{title}{title_sep}qutebrowser +modal-js-dialog = false +hide-wayland-decoration = false +keyhint-blacklist = +keyhint-delay = 500 +prompt-radius = 8 +prompt-filebrowser = true +[network] +do-not-track = true +accept-language = en-US,en +referer-header = same-domain +user-agent = +proxy = system +proxy-dns-requests = true +ssl-strict = ask +dns-prefetch = true +custom-headers = +netrc-file = +[completion] +show = always +download-path-suggestion = path +timestamp-format = %Y-%m-%d +height = 50% +cmd-history-max-items = 100 +web-history-max-items = 1000 +quick-complete = true +shrink = false +scrollbar-width = 12 +scrollbar-padding = 2 +[input] +timeout = 500 +partial-timeout = 5000 +insert-mode-on-plugins = false +auto-leave-insert-mode = true +auto-insert-mode = false +forward-unbound-keys = auto +spatial-navigation = false +links-included-in-focus-chain = true +rocker-gestures = false +mouse-zoom-divider = 512 +[tabs] +background-tabs = false +select-on-remove = next +new-tab-position = next +new-tab-position-explicit = last +last-close = ignore +show = always +show-switching-delay = 800 +wrap = true +movable = true +close-mouse-button = middle +position = top +show-favicons = true +favicon-scale = 1.0 +width = 20% +pinned-width = 43 +indicator-width = 3 +tabs-are-windows = false +title-format = {index}: {title} +title-format-pinned = {index} +title-alignment = left +mousewheel-tab-switching = true +padding = 0,0,5,5 +indicator-padding = 2,2,0,4 +[storage] +download-directory = +prompt-download-directory = true +remember-download-directory = true +maximum-pages-in-cache = 0 +offline-web-application-cache = true +local-storage = true +cache-size = +[content] +allow-images = true +allow-javascript = true +allow-plugins = false +webgl = true +hyperlink-auditing = false +geolocation = ask +notifications = ask +media-capture = ask +javascript-can-open-windows-automatically = false +javascript-can-close-windows = false +javascript-can-access-clipboard = false +ignore-javascript-prompt = false +ignore-javascript-alert = false +local-content-can-access-remote-urls = false +local-content-can-access-file-urls = true +cookies-accept = no-3rdparty +cookies-store = true +host-block-lists = https://www.malwaredomainlist.com/hostslist/hosts.txt,http://someonewhocares.org/hosts/hosts,http://winhelp2002.mvps.org/hosts.zip,http://malwaredomains.lehigh.edu/files/justdomains.zip,https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&mimetype=plaintext +host-blocking-enabled = true +host-blocking-whitelist = piwik.org +enable-pdfjs = false +[hints] +border = 1px solid #E3BE23 +mode = letter +chars = asdfghjkl +min-chars = 1 +scatter = true +uppercase = false +dictionary = /usr/share/dict/words +auto-follow = unique-match +auto-follow-timeout = 0 +next-regexes = \bnext\b,\bmore\b,\bnewer\b,\b[>→≫]\b,\b(>>|»)\b,\bcontinue\b +prev-regexes = \bprev(ious)?\b,\bback\b,\bolder\b,\b[<←≪]\b,\b(<<|«)\b +find-implementation = python +hide-unmatched-rapid-hints = true +[searchengines] +DEFAULT = https://duckduckgo.com/?q={} +[aliases] +[colors] +completion.fg = white +completion.bg = #333333 +completion.alternate-bg = #444444 +completion.category.fg = white +completion.category.bg = qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #888888, stop:1 #505050) +completion.category.border.top = black +completion.category.border.bottom = ${completion.category.border.top} +completion.item.selected.fg = black +completion.item.selected.bg = #e8c000 +completion.item.selected.border.top = #bbbb00 +completion.item.selected.border.bottom = ${completion.item.selected.border.top} +completion.match.fg = #ff4444 +completion.scrollbar.fg = ${completion.fg} +completion.scrollbar.bg = ${completion.bg} +statusbar.fg = white +statusbar.bg = black +statusbar.fg.private = ${statusbar.fg} +statusbar.bg.private = #666666 +statusbar.fg.insert = ${statusbar.fg} +statusbar.bg.insert = darkgreen +statusbar.fg.command = ${statusbar.fg} +statusbar.bg.command = ${statusbar.bg} +statusbar.fg.command.private = ${statusbar.fg.private} +statusbar.bg.command.private = ${statusbar.bg.private} +statusbar.fg.caret = ${statusbar.fg} +statusbar.bg.caret = purple +statusbar.fg.caret-selection = ${statusbar.fg} +statusbar.bg.caret-selection = #a12dff +statusbar.progress.bg = white +statusbar.url.fg = ${statusbar.fg} +statusbar.url.fg.success = white +statusbar.url.fg.success.https = lime +statusbar.url.fg.error = orange +statusbar.url.fg.warn = yellow +statusbar.url.fg.hover = aqua +tabs.fg.odd = white +tabs.bg.odd = grey +tabs.fg.even = white +tabs.bg.even = darkgrey +tabs.fg.selected.odd = white +tabs.bg.selected.odd = black +tabs.fg.selected.even = ${tabs.fg.selected.odd} +tabs.bg.selected.even = ${tabs.bg.selected.odd} +tabs.bg.bar = #555555 +tabs.indicator.start = #0000aa +tabs.indicator.stop = #00aa00 +tabs.indicator.error = #ff0000 +tabs.indicator.system = rgb +hints.fg = black +hints.bg = qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 247, 133, 0.8), stop:1 rgba(255, 197, 66, 0.8)) +hints.fg.match = green +downloads.bg.bar = black +downloads.fg.start = white +downloads.bg.start = #0000aa +downloads.fg.stop = ${downloads.fg.start} +downloads.bg.stop = #00aa00 +downloads.fg.system = rgb +downloads.bg.system = rgb +downloads.fg.error = white +downloads.bg.error = red +webpage.bg = white +keyhint.fg = #FFFFFF +keyhint.fg.suffix = #FFFF00 +keyhint.bg = rgba(0, 0, 0, 80%) +messages.fg.error = white +messages.bg.error = red +messages.border.error = #bb0000 +messages.fg.warning = white +messages.bg.warning = darkorange +messages.border.warning = #d47300 +messages.fg.info = white +messages.bg.info = black +messages.border.info = #333333 +prompts.fg = white +prompts.bg = darkblue +prompts.selected.bg = #308cc6 +[fonts] +_monospace = xos4 Terminus, Terminus, Monospace, "DejaVu Sans Mono", Monaco, "Bitstream Vera Sans Mono", "Andale Mono", "Courier New", Courier, "Liberation Mono", monospace, Fixed, Consolas, Terminal +completion = 8pt ${_monospace} +completion.category = bold ${completion} +tabbar = 8pt ${_monospace} +statusbar = 8pt ${_monospace} +downloads = 8pt ${_monospace} +hints = bold 13px ${_monospace} +debug-console = 8pt ${_monospace} +web-family-standard = +web-family-fixed = +web-family-serif = +web-family-sans-serif = +web-family-cursive = +web-family-fantasy = +web-size-minimum = 0 +web-size-minimum-logical = 6 +web-size-default = 16 +web-size-default-fixed = 13 +keyhint = 8pt ${_monospace} +messages.error = 8pt ${_monospace} +messages.warning = 8pt ${_monospace} +messages.info = 8pt ${_monospace} +prompts = 8pt sans-serif diff --git a/tests/unit/config/test_config.py b/tests/unit/config/test_config.py index c897b9f16..be526dac3 100644 --- a/tests/unit/config/test_config.py +++ b/tests/unit/config/test_config.py @@ -408,7 +408,7 @@ class TestDefaultConfig: If it did change, place a new qutebrowser-vx.y.z.conf in old_configs and then increment the version. """ - assert qutebrowser.__version__ == '0.10.1' + assert qutebrowser.__version__ == '0.11.0' @pytest.mark.parametrize('filename', os.listdir(os.path.join(os.path.dirname(__file__), 'old_configs')), diff --git a/tests/unit/misc/test_editor.py b/tests/unit/misc/test_editor.py index de9125c8b..70886cda6 100644 --- a/tests/unit/misc/test_editor.py +++ b/tests/unit/misc/test_editor.py @@ -123,24 +123,30 @@ class TestFileHandling: os.remove(filename) - @pytest.mark.posix def test_unreadable(self, message_mock, editor, caplog): """Test file handling when closing with an unreadable file.""" editor.edit("") filename = editor._file.name assert os.path.exists(filename) os.chmod(filename, 0o077) + if os.access(filename, os.R_OK): + # Docker container or similar + pytest.skip("File was still readable") + with caplog.at_level(logging.ERROR): editor._proc.finished.emit(0, QProcess.NormalExit) assert not os.path.exists(filename) msg = message_mock.getmsg(usertypes.MessageLevel.error) assert msg.text.startswith("Failed to read back edited file: ") - @pytest.mark.posix def test_unwritable(self, monkeypatch, message_mock, editor, tmpdir, caplog): """Test file handling when the initial file is not writable.""" tmpdir.chmod(0) + if os.access(str(tmpdir), os.W_OK): + # Docker container or similar + pytest.skip("File was still writable") + monkeypatch.setattr(editormod.tempfile, 'tempdir', str(tmpdir)) with caplog.at_level(logging.ERROR): diff --git a/tests/unit/misc/test_sessions.py b/tests/unit/misc/test_sessions.py index 59b335f78..e77b6aa02 100644 --- a/tests/unit/misc/test_sessions.py +++ b/tests/unit/misc/test_sessions.py @@ -137,20 +137,6 @@ class FakeMainWindow(QObject): return self._geometry -class FakeTabbedBrowser: - - """A fake tabbed-browser which contains some widgets.""" - - def __init__(self, widgets): - self._widgets = widgets - - def widgets(self): - return self._widgets - - def currentIndex(self): - return 1 - - @pytest.fixture def fake_window(win_registry, stubs, monkeypatch, qtbot): """Fixture which provides a fake main windows with a tabbedbrowser.""" @@ -159,7 +145,7 @@ def fake_window(win_registry, stubs, monkeypatch, qtbot): webview = QWebView() qtbot.add_widget(webview) - browser = FakeTabbedBrowser([webview]) + browser = stubs.TabbedBrowserStub([webview]) objreg.register('tabbed-browser', browser, scope='window', window=0) yield @@ -211,7 +197,7 @@ class TestSave: """Fixture which provides a window with a fake history.""" win = FakeMainWindow(b'fake-geometry-0', win_id=0) objreg.register('main-window', win, scope='window', window=0) - browser = FakeTabbedBrowser([webview]) + browser = stubs.TabbedBrowserStub([webview]) objreg.register('tabbed-browser', browser, scope='window', window=0) qapp = stubs.FakeQApplication(active_window=win) diff --git a/tests/unit/utils/test_version.py b/tests/unit/utils/test_version.py index c0c731f88..1227f4d3e 100644 --- a/tests/unit/utils/test_version.py +++ b/tests/unit/utils/test_version.py @@ -496,6 +496,7 @@ class ImportFake: 'typing': True, 'PyQt5.QtWebEngineWidgets': True, 'PyQt5.QtWebKitWidgets': True, + 'OpenGL': True, } self.version_attribute = '__version__' self.version = '1.2.3' @@ -555,7 +556,7 @@ class TestModuleVersions: """Test with all modules present in version 1.2.3.""" expected = ['sip: yes', 'colorama: 1.2.3', 'pypeg2: 1.2.3', 'jinja2: 1.2.3', 'pygments: 1.2.3', 'yaml: 1.2.3', - 'cssutils: 1.2.3', 'typing: yes', + 'cssutils: 1.2.3', 'typing: yes', 'OpenGL: 1.2.3', 'PyQt5.QtWebEngineWidgets: yes', 'PyQt5.QtWebKitWidgets: yes'] assert version._module_versions() == expected @@ -579,17 +580,17 @@ class TestModuleVersions: @pytest.mark.parametrize('value, expected', [ ('VERSION', ['sip: yes', 'colorama: 1.2.3', 'pypeg2: yes', 'jinja2: yes', 'pygments: yes', 'yaml: yes', - 'cssutils: yes', 'typing: yes', + 'cssutils: yes', 'typing: yes', 'OpenGL: yes', 'PyQt5.QtWebEngineWidgets: yes', 'PyQt5.QtWebKitWidgets: yes']), ('SIP_VERSION_STR', ['sip: 1.2.3', 'colorama: yes', 'pypeg2: yes', 'jinja2: yes', 'pygments: yes', 'yaml: yes', - 'cssutils: yes', 'typing: yes', + 'cssutils: yes', 'typing: yes', 'OpenGL: yes', 'PyQt5.QtWebEngineWidgets: yes', 'PyQt5.QtWebKitWidgets: yes']), (None, ['sip: yes', 'colorama: yes', 'pypeg2: yes', 'jinja2: yes', 'pygments: yes', 'yaml: yes', 'cssutils: yes', 'typing: yes', - 'PyQt5.QtWebEngineWidgets: yes', + 'OpenGL: yes', 'PyQt5.QtWebEngineWidgets: yes', 'PyQt5.QtWebKitWidgets: yes']), ]) def test_version_attribute(self, value, expected, import_fake): @@ -807,7 +808,7 @@ def test_chromium_version_unpatched(qapp): (True, False, True, False, True), # no webkit (True, False, True, 'ng', True), # QtWebKit-NG (True, False, True, True, False), # unknown Linux distribution -]) +]) # pylint: disable=too-many-locals def test_version_output(git_commit, frozen, style, with_webkit, known_distribution, stubs, monkeypatch, init_sql): """Test version.version().""" diff --git a/tox.ini b/tox.ini index 722dad3c4..04cf826d1 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ # and then run "tox" from this directory. [tox] -envlist = py34,py35,py36-cov,misc,vulture,flake8,pylint,pyroma,check-manifest +envlist = py36-cov,misc,vulture,flake8,pylint,pyroma,check-manifest,eslint distshare = {toxworkdir} skipsdist = true @@ -111,6 +111,28 @@ deps = PyQt5==5.8.2 commands = {envpython} -bb -m pytest {posargs:tests} +[testenv:py35-pyqt59] +basepython = python3.5 +setenv = + {[testenv]setenv} + QUTE_BDD_WEBENGINE=true +passenv = {[testenv]passenv} +deps = + {[testenv]deps} + PyQt5==5.9 +commands = {envpython} -bb -m pytest {posargs:tests} + +[testenv:py36-pyqt59] +basepython = {env:PYTHON:python3.6} +setenv = + {[testenv]setenv} + QUTE_BDD_WEBENGINE=true +passenv = {[testenv]passenv} +deps = + {[testenv]deps} + PyQt5==5.9 +commands = {envpython} -bb -m pytest {posargs:tests} + # other envs [testenv:mkvenv]