Merge branch 'master' into jay/prompt-on-click

This commit is contained in:
Jay Kamat 2017-07-09 14:17:03 -07:00
commit 28a2482cf7
No known key found for this signature in database
GPG Key ID: 5D2E399600F4F7B5
32 changed files with 408 additions and 107 deletions

View File

@ -23,14 +23,18 @@ matrix:
language: python language: python
python: 3.6 python: 3.6
env: TESTENV=py36-pyqt571 env: TESTENV=py36-pyqt571
- os: linux
language: python
python: 3.6
env: TESTENV=py36-pyqt58
- os: linux - os: linux
language: python language: python
python: 3.5 python: 3.5
env: TESTENV=py35-pyqt58 env: TESTENV=py35-pyqt59
- os: linux - os: linux
language: python language: python
python: 3.6 python: 3.6
env: TESTENV=py36-pyqt58 env: TESTENV=py36-pyqt59
- os: osx - os: osx
env: TESTENV=py36 OSX=elcapitan env: TESTENV=py36 OSX=elcapitan
osx_image: xcode7.3 osx_image: xcode7.3

View File

@ -14,8 +14,8 @@ This project adheres to http://semver.org/[Semantic Versioning].
// `Fixed` for any bug fixes. // `Fixed` for any bug fixes.
// `Security` to invite users to upgrade in case of vulnerabilities. // `Security` to invite users to upgrade in case of vulnerabilities.
v0.11.0 (unreleased) v0.11.0
-------------------- -------
New dependencies New dependencies
~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
@ -28,7 +28,10 @@ New dependencies
Added 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 - New "pinned tabs" feature, with a new `:tab-pin` command (bound
to `<Ctrl-p>` by default). to `<Ctrl-p>` by default).
- (QtWebEngine) Implemented `:follow-selected`. - (QtWebEngine) Implemented `:follow-selected`.
@ -45,6 +48,8 @@ Added
customize statusbar colors for private windows. customize statusbar colors for private windows.
- New `{private}` field displaying `[Private Mode]` for - New `{private}` field displaying `[Private Mode]` for
`ui -> window-title-format` and `tabs -> title-format`. `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 Changed
~~~~~~~ ~~~~~~~
@ -52,62 +57,51 @@ Changed
- To prevent elaborate phishing attacks, the Punycode version (`xn--*`) is now - To prevent elaborate phishing attacks, the Punycode version (`xn--*`) is now
shown in addition to the decoded version for international domain names shown in addition to the decoded version for international domain names
(IDN). (IDN).
- Private browsing is now implemented for QtWebEngine, and changed it's - Starting with legacy QtWebKit now shows a warning message.
behavior: The `general -> private-browsing` setting now only applies to newly *With the next release, support for it will be removed.*
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.
- The Windows releases are redone from scratch, which means: - The Windows releases are redone from scratch, which means:
- They now use the new QtWebEngine backend - They now use the new QtWebEngine backend
- The bundled Qt is updated from 5.5 to 5.9 - The bundled Qt is updated from 5.5 to 5.9
- The bundled Python is updated from 3.4 to 3.6 - The bundled Python is updated from 3.4 to 3.6
- They are now generated with PyInstaller instead of cx_Freeze - They are now generated with PyInstaller instead of cx_Freeze
- The installer is now generated using NSIS instead of being a MSI - 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. - 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` - The default level for `:messages` is now `info`, not `error`
- Trying to focus the currently focused tab with `:tab-focus` now focuses the - Trying to focus the currently focused tab with `:tab-focus` now focuses the
last viewed tab. 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 Fixed
~~~~~ ~~~~~
- The macOS .dmg is now built against Qt 5.9 which fixes various - The macOS .dmg is now built against Qt 5.9 which fixes various
important issues (such as not being able to type dead keys). 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. - Fixed crash with `:download` on PyQt 5.9.
- Cloning a page without history doesn't crash anymore. - Cloning a page without history doesn't crash anymore.
- When a download results in a HTTP error, it now shows the error correctly - When a download results in a HTTP error, it now shows the error correctly
@ -117,7 +111,6 @@ Fixed
- Fixed crash when unbinding an unbound key in the key config. - 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 when using `:debug-log-filter` when `--filter` wasn't given on startup.
- Fixed crash with some invalid setting values. - 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. - Continuing a search after clearing it now works correctly.
- The tabbar and completion should now be more consistently and correctly - The tabbar and completion should now be more consistently and correctly
styled with various system styles. styled with various system styles.
@ -125,18 +118,27 @@ Fixed
- The validation for colors in stylesheets is now less strict, - The validation for colors in stylesheets is now less strict,
allowing for all valid Qt values. allowing for all valid Qt values.
- `data:` URLs now aren't added to the history anymore. - `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. - 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. - For some people, running some userscripts crashed - this should now be fixed.
- Various other rare crashes 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 - The settings documentation was truncated with v0.10.1 which should now be
fixed. fixed.
- Scrolling to an anchor in a background tab now works correctly, and javascript - Scrolling to an anchor in a background tab now works correctly, and javascript
gets the correct window size for background tabs. 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 v0.10.1
------- -------

View File

@ -682,8 +682,9 @@ qutebrowser release
* Add newest config to `tests/unit/config/old_configs` and update `test_upgrade_version` * Add newest config to `tests/unit/config/old_configs` and update `test_upgrade_version`
- `python -m qutebrowser --basedir conf :quit` - `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` - `rm -r conf`
- git add
- commit - commit
* Adjust `__version_info__` in `qutebrowser/__init__.py`. * Adjust `__version_info__` in `qutebrowser/__init__.py`.
* Update changelog (remove *(unreleased)*) * Update changelog (remove *(unreleased)*)

View File

@ -99,10 +99,10 @@ Requirements
The following software and libraries are required to run qutebrowser: The following software and libraries are required to run qutebrowser:
* http://www.python.org/[Python] 3.4 or newer (3.5 recommended) * 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 * 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 * 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] * https://pypi.python.org/pypi/setuptools/[pkg_resources/setuptools]
* http://fdik.org/pyPEG/[pyPEG2] * http://fdik.org/pyPEG/[pyPEG2]
* http://jinja.pocoo.org/[jinja2] * http://jinja.pocoo.org/[jinja2]

View File

@ -85,7 +85,7 @@ It is possible to run or bind multiple commands by separating them with `;;`.
|<<tab-pin,tab-pin>>|Pin/Unpin the current/[count]th tab. |<<tab-pin,tab-pin>>|Pin/Unpin the current/[count]th tab.
|<<tab-prev,tab-prev>>|Switch to the previous tab, or switch [count] tabs back. |<<tab-prev,tab-prev>>|Switch to the previous tab, or switch [count] tabs back.
|<<unbind,unbind>>|Unbind a keychain. |<<unbind,unbind>>|Unbind a keychain.
|<<undo,undo>>|Re-open a closed tab (optionally skipping [count] closed tabs). |<<undo,undo>>|Re-open a closed tab.
|<<view-source,view-source>>|Show the source of the current page in a new tab. |<<view-source,view-source>>|Show the source of the current page in a new tab.
|<<window-only,window-only>>|Close all windows except for the current one. |<<window-only,window-only>>|Close all windows except for the current one.
|<<wq,wq>>|Save open pages and quit. |<<wq,wq>>|Save open pages and quit.
@ -936,7 +936,7 @@ Unbind a keychain.
[[undo]] [[undo]]
=== undo === undo
Re-open a closed tab (optionally skipping [count] closed tabs). Re-open a closed tab.
[[view-source]] [[view-source]]
=== view-source === view-source

View File

@ -789,8 +789,6 @@ The proxy to use.
In addition to the listed values, you can use a `socks://...` or `http://...` URL. 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: Valid values:
* +system+: Use the system wide proxy. * +system+: Use the system wide proxy.

View File

@ -57,7 +57,7 @@ show it.
How URLs should be opened if there is already a qutebrowser instance running. How URLs should be opened if there is already a qutebrowser instance running.
*--backend* '{webkit,webengine}':: *--backend* '{webkit,webengine}'::
Which backend to use (webengine backend is EXPERIMENTAL!). Which backend to use.
*--enable-webengine-inspector*:: *--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. 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.

View File

@ -1,4 +1,4 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py # This file is automatically generated by scripts/dev/recompile_requirements.py
PyQt5==5.8.2 PyQt5==5.9
sip==4.19.2 sip==4.19.3

View File

@ -45,6 +45,7 @@ qt_log_ignore =
^QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to .* ^QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to .*
^QGeoclueMaster error creating GeoclueMasterClient\. ^QGeoclueMaster error creating GeoclueMasterClient\.
^Geoclue error: Process org\.freedesktop\.Geoclue\.Master exited with status 127 ^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\) ^QObject::connect: Cannot connect \(null\)::stateChanged\(QNetworkSession::State\) to QNetworkReplyHttpImpl::_q_networkSessionStateChanged\(QNetworkSession::State\)
^QXcbClipboard: Cannot transfer data, no data available ^QXcbClipboard: Cannot transfer data, no data available
^load glyph failed ^load glyph failed

View File

@ -26,7 +26,7 @@ __copyright__ = "Copyright 2014-2017 Florian Bruhin (The Compiler)"
__license__ = "GPL" __license__ = "GPL"
__maintainer__ = __author__ __maintainer__ = __author__
__email__ = "mail@qutebrowser.org" __email__ = "mail@qutebrowser.org"
__version_info__ = (0, 10, 1) __version_info__ = (0, 11, 0)
__version__ = '.'.join(str(e) for e in __version_info__) __version__ = '.'.join(str(e) for e in __version_info__)
__description__ = "A keyboard-driven, vim-like browser based on PyQt5." __description__ = "A keyboard-driven, vim-like browser based on PyQt5."

View File

@ -410,10 +410,8 @@ def _init_modules(args, crash_handler):
log.init.debug("Initializing network...") log.init.debug("Initializing network...")
networkmanager.init() networkmanager.init()
if qtutils.version_check('5.8'): log.init.debug("Initializing proxy...")
# Otherwise we can only initialize it for QtWebKit because of crashes proxy.init()
log.init.debug("Initializing proxy...")
proxy.init()
log.init.debug("Initializing readline-bridge...") log.init.debug("Initializing readline-bridge...")
readline_bridge = readline.ReadlineBridge() readline_bridge = readline.ReadlineBridge()

View File

@ -281,9 +281,7 @@ class CommandDispatcher:
return return
to_pin = not tab.data.pinned to_pin = not tab.data.pinned
tab_index = self._current_index() if count is None else count - 1 self._tabbed_browser.set_tab_pinned(tab, to_pin)
cmdutils.check_overflow(tab_index + 1, 'int')
self._tabbed_browser.set_tab_pinned(tab_index, to_pin)
@cmdutils.register(instance='command-dispatcher', name='open', @cmdutils.register(instance='command-dispatcher', name='open',
maxsplit=0, scope='window') maxsplit=0, scope='window')
@ -516,7 +514,7 @@ class CommandDispatcher:
newtab.data.keep_icon = True newtab.data.keep_icon = True
newtab.history.deserialize(history) newtab.history.deserialize(history)
newtab.zoom.set_factor(curtab.zoom.factor()) 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 return newtab
@cmdutils.register(instance='command-dispatcher', scope='window') @cmdutils.register(instance='command-dispatcher', scope='window')
@ -933,7 +931,7 @@ class CommandDispatcher:
@cmdutils.register(instance='command-dispatcher', scope='window') @cmdutils.register(instance='command-dispatcher', scope='window')
def undo(self): def undo(self):
"""Re-open a closed tab (optionally skipping [count] closed tabs).""" """Re-open a closed tab."""
try: try:
self._tabbed_browser.undo() self._tabbed_browser.undo()
except IndexError: except IndexError:

View File

@ -132,9 +132,6 @@ def _init_stylesheet(profile):
Mostly inspired by QupZilla: 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/app/mainapplication.cpp#L1063-L1101
https://github.com/QupZilla/qupzilla/blob/v2.0/src/lib/tools/scripts.cpp#L119-L132 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') old_script = profile.scripts().findScript('_qute_stylesheet')
if not old_script.isNull(): if not old_script.isNull():

View File

@ -20,6 +20,7 @@
"""Wrapper over a QWebEngineView.""" """Wrapper over a QWebEngineView."""
import os import os
import math
import functools import functools
import sip import sip
@ -342,7 +343,7 @@ class WebEngineScroller(browsertab.AbstractScroller):
else: else:
perc_y = min(100, round(100 / dy * jsret['px']['y'])) 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._pos_perc = perc_x, perc_y
self.perc_changed.emit(*self._pos_perc) self.perc_changed.emit(*self._pos_perc)

View File

@ -33,7 +33,6 @@ from PyQt5.QtWebKit import QWebSettings
from PyQt5.QtPrintSupport import QPrinter from PyQt5.QtPrintSupport import QPrinter
from qutebrowser.browser import browsertab from qutebrowser.browser import browsertab
from qutebrowser.browser.network import proxy
from qutebrowser.browser.webkit import webview, tabhistory, webkitelem from qutebrowser.browser.webkit import webview, tabhistory, webkitelem
from qutebrowser.browser.webkit.network import webkitqutescheme from qutebrowser.browser.webkit.network import webkitqutescheme
from qutebrowser.utils import qtutils, objreg, usertypes, utils, log, debug 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(): def init():
"""Initialize QtWebKit-specific modules.""" """Initialize QtWebKit-specific modules."""
qapp = QApplication.instance() 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...") log.init.debug("Initializing js-bridge...")
js_bridge = webkitqutescheme.JSBridge(qapp) js_bridge = webkitqutescheme.JSBridge(qapp)
objreg.register('js-bridge', js_bridge) objreg.register('js-bridge', js_bridge)

View File

@ -437,14 +437,10 @@ def data(readonly=False):
"User agent to send. Empty to send the default."), "User agent to send. Empty to send the default."),
('proxy', ('proxy',
SettingValue(typ.Proxy(), 'system', SettingValue(typ.Proxy(), 'system'),
backends=(None if qtutils.version_check('5.8')
else [usertypes.Backend.QtWebKit])),
"The proxy to use.\n\n" "The proxy to use.\n\n"
"In addition to the listed values, you can use a `socks://...` " "In addition to the listed values, you can use a `socks://...` "
"or `http://...` URL.\n\n" "or `http://...` URL."),
"This setting only works with Qt 5.8 or newer when using the "
"QtWebEngine backend."),
('proxy-dns-requests', ('proxy-dns-requests',
SettingValue(typ.Bool(), 'true', SettingValue(typ.Bool(), 'true',

View File

@ -343,7 +343,7 @@ class TabbedBrowser(tabwidget.TabWidget):
newtab = self.tabopen(url, background=False, idx=idx) newtab = self.tabopen(url, background=False, idx=idx)
newtab.history.deserialize(history_data) newtab.history.deserialize(history_data)
self.set_tab_pinned(idx, pinned) self.set_tab_pinned(newtab, pinned)
@pyqtSlot('QUrl', bool) @pyqtSlot('QUrl', bool)
def openurl(self, url, newtab): def openurl(self, url, newtab):

View File

@ -26,7 +26,7 @@ from PyQt5.QtCore import (pyqtSignal, pyqtSlot, Qt, QSize, QRect, QPoint,
QTimer, QUrl) QTimer, QUrl)
from PyQt5.QtWidgets import (QTabWidget, QTabBar, QSizePolicy, QCommonStyle, from PyQt5.QtWidgets import (QTabWidget, QTabBar, QSizePolicy, QCommonStyle,
QStyle, QStylePainter, QStyleOptionTab, QStyle, QStylePainter, QStyleOptionTab,
QStyleFactory) QStyleFactory, QWidget)
from PyQt5.QtGui import QIcon, QPalette, QColor from PyQt5.QtGui import QIcon, QPalette, QColor
from qutebrowser.utils import qtutils, objreg, utils, usertypes, log 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.set_tab_data(idx, 'indicator-color', color)
bar.update(bar.tabRect(idx)) 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. """Set the tab status as pinned.
Args: Args:
idx: The tab index. tab: The tab to pin
pinned: Pinned tab state to set. pinned: Pinned tab state to set.
loading: Whether to ignore current data state when loading: Whether to ignore current data state when
counting pinned_count. counting pinned_count.
""" """
bar = self.tabBar() bar = self.tabBar()
tab = self.widget(idx) idx = self.indexOf(tab)
# Only modify pinned_count if we had a change # Only modify pinned_count if we had a change
# always modify pinned_count if we are loading # always modify pinned_count if we are loading

View File

@ -336,6 +336,7 @@ def check_libraries(backend):
"http://pyyaml.org/download/pyyaml/ (py3.4) " "http://pyyaml.org/download/pyyaml/ (py3.4) "
"or Install via pip.", "or Install via pip.",
pip="PyYAML"), pip="PyYAML"),
'PyQt5.QtQml': _missing_str("PyQt5.QtQml"),
} }
if backend == 'webengine': if backend == 'webengine':
modules['PyQt5.QtWebEngineWidgets'] = _missing_str("QtWebEngine", modules['PyQt5.QtWebEngineWidgets'] = _missing_str("QtWebEngine",

View File

@ -406,7 +406,7 @@ class SessionManager(QObject):
tab_to_focus = i tab_to_focus = i
if new_tab.data.pinned: if new_tab.data.pinned:
tabbed_browser.set_tab_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: if tab_to_focus is not None:
tabbed_browser.setCurrentIndex(tab_to_focus) tabbed_browser.setCurrentIndex(tab_to_focus)
if win.get('active', False): if win.get('active', False):

View File

@ -64,8 +64,7 @@ def get_argparser():
help="How URLs should be opened if there is already a " help="How URLs should be opened if there is already a "
"qutebrowser instance running.") "qutebrowser instance running.")
parser.add_argument('--backend', choices=['webkit', 'webengine'], parser.add_argument('--backend', choices=['webkit', 'webengine'],
help="Which backend to use (webengine backend is " help="Which backend to use.")
"EXPERIMENTAL!).")
parser.add_argument('--enable-webengine-inspector', action='store_true', parser.add_argument('--enable-webengine-inspector', action='store_true',
help="Enable the web inspector for QtWebEngine. Note " help="Enable the web inspector for QtWebEngine. Note "
"that this is a SECURITY RISK and you should not " "that this is a SECURITY RISK and you should not "

View File

@ -64,7 +64,7 @@ def call_tox(toxenv, *args, python=sys.executable):
env['PYTHON'] = python env['PYTHON'] = python
env['PATH'] = os.environ['PATH'] + os.pathsep + os.path.dirname(python) env['PATH'] = os.environ['PATH'] + os.pathsep + os.path.dirname(python)
subprocess.check_call( subprocess.check_call(
[sys.executable, '-m', 'tox', '-v', '-e', toxenv] + list(args), [sys.executable, '-m', 'tox', '-vv', '-e', toxenv] + list(args),
env=env) env=env)

View File

@ -43,6 +43,12 @@ travis_retry() {
} }
apt_install() { apt_install() {
sudo tee /etc/apt/sources.list <<EOF
deb http://us.archive.ubuntu.com/ubuntu/ trusty main
deb http://us.archive.ubuntu.com/ubuntu/ trusty-security main
deb http://us.archive.ubuntu.com/ubuntu/ trusty-updates main
EOF
sudo rm -rf /etc/apt/sources.list.d
travis_retry sudo apt-get -y -q update travis_retry sudo apt-get -y -q update
travis_retry sudo apt-get -y -q install --no-install-recommends "$@" travis_retry sudo apt-get -y -q install --no-install-recommends "$@"
} }
@ -64,8 +70,9 @@ npm_install() {
} }
install_node() { install_node() {
curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash - curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
apt_install nodejs travis_retry sudo apt-get -y -q update
travis_retry sudo apt-get -y -q install --no-install-recommends nodejs
} }
check_pyqt() { check_pyqt() {

View File

@ -365,8 +365,7 @@ def generate_commands(filename):
def _generate_setting_section(f, sectname, sect): def _generate_setting_section(f, sectname, sect):
"""Generate documentation for a single section.""" """Generate documentation for a single section."""
version_dependent_options = [('network', 'proxy'), version_dependent_options = [('general', 'print-element-backgrounds')]
('general', 'print-element-backgrounds')]
for optname, option in sect.items(): for optname, option in sect.items():
f.write("\n") f.write("\n")
f.write('[[{}-{}]]'.format(sectname, optname) + "\n") f.write('[[{}-{}]]'.format(sectname, optname) + "\n")

View File

@ -579,9 +579,9 @@ Feature: Downloading things from a website.
And I wait until the download is finished And I wait until the download is finished
Then the downloaded file content-size should exist Then the downloaded file content-size should exist
@posix
Scenario: Downloading to unwritable destination Scenario: Downloading to unwritable destination
When I set storage -> 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 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 Then the error "Download error: Permission denied" should be shown

View File

@ -292,6 +292,13 @@ Feature: Scrolling
And I run :scroll-page --bottom-navigate next 0 1 And I run :scroll-page --bottom-navigate next 0 1
Then data/hello2.txt should be loaded 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 Scenario: :scroll-page with --top-navigate
When I run :scroll-page --top-navigate prev 0 -1 When I run :scroll-page --top-navigate prev 0 -1
Then data/hello3.txt should be loaded Then data/hello3.txt should be loaded

View File

@ -1073,6 +1073,16 @@ Feature: Tab management
- data/numbers/2.txt (pinned) - data/numbers/2.txt (pinned)
- data/numbers/3.txt (active) - 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 Scenario: Pinned :tab-close prompt yes
When I open data/numbers/1.txt When I open data/numbers/1.txt
And I run :tab-pin And I run :tab-pin

View File

@ -21,6 +21,7 @@ import os
import sys import sys
import shlex import shlex
import pytest
import pytest_bdd as bdd import pytest_bdd as bdd
bdd.scenarios('downloads.feature') bdd.scenarios('downloads.feature')
@ -53,6 +54,14 @@ def clean_old_downloads(quteproc):
quteproc.send_cmd(':download-clear') 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") @bdd.when("I wait until the download is finished")
def wait_for_download_finished(quteproc): def wait_for_download_finished(quteproc):
quteproc.wait_for(category='downloads', message='Download * finished') quteproc.wait_for(category='downloads', message='Download * finished')

View File

@ -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

View File

@ -408,7 +408,7 @@ class TestDefaultConfig:
If it did change, place a new qutebrowser-vx.y.z.conf in old_configs If it did change, place a new qutebrowser-vx.y.z.conf in old_configs
and then increment the version. and then increment the version.
""" """
assert qutebrowser.__version__ == '0.10.1' assert qutebrowser.__version__ == '0.11.0'
@pytest.mark.parametrize('filename', @pytest.mark.parametrize('filename',
os.listdir(os.path.join(os.path.dirname(__file__), 'old_configs')), os.listdir(os.path.join(os.path.dirname(__file__), 'old_configs')),

View File

@ -123,24 +123,30 @@ class TestFileHandling:
os.remove(filename) os.remove(filename)
@pytest.mark.posix
def test_unreadable(self, message_mock, editor, caplog): def test_unreadable(self, message_mock, editor, caplog):
"""Test file handling when closing with an unreadable file.""" """Test file handling when closing with an unreadable file."""
editor.edit("") editor.edit("")
filename = editor._file.name filename = editor._file.name
assert os.path.exists(filename) assert os.path.exists(filename)
os.chmod(filename, 0o077) 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): with caplog.at_level(logging.ERROR):
editor._proc.finished.emit(0, QProcess.NormalExit) editor._proc.finished.emit(0, QProcess.NormalExit)
assert not os.path.exists(filename) assert not os.path.exists(filename)
msg = message_mock.getmsg(usertypes.MessageLevel.error) msg = message_mock.getmsg(usertypes.MessageLevel.error)
assert msg.text.startswith("Failed to read back edited file: ") assert msg.text.startswith("Failed to read back edited file: ")
@pytest.mark.posix
def test_unwritable(self, monkeypatch, message_mock, editor, tmpdir, def test_unwritable(self, monkeypatch, message_mock, editor, tmpdir,
caplog): caplog):
"""Test file handling when the initial file is not writable.""" """Test file handling when the initial file is not writable."""
tmpdir.chmod(0) 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)) monkeypatch.setattr(editormod.tempfile, 'tempdir', str(tmpdir))
with caplog.at_level(logging.ERROR): with caplog.at_level(logging.ERROR):

24
tox.ini
View File

@ -4,7 +4,7 @@
# and then run "tox" from this directory. # and then run "tox" from this directory.
[tox] [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} distshare = {toxworkdir}
skipsdist = true skipsdist = true
@ -111,6 +111,28 @@ deps =
PyQt5==5.8.2 PyQt5==5.8.2
commands = {envpython} -bb -m pytest {posargs:tests} 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 # other envs
[testenv:mkvenv] [testenv:mkvenv]