Compare commits
70 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9e411311a | ||
|
|
33b71ee937 | ||
|
|
d166587f3d | ||
|
|
dd663df35f | ||
|
|
9d292128d2 | ||
|
|
f3eb5dbb66 | ||
|
|
3eaad092b8 | ||
|
|
38bccd4fdd | ||
|
|
9a5668f7a0 | ||
|
|
48023e7b1d | ||
|
|
74e1b1ec26 | ||
|
|
cfe7386f20 | ||
|
|
12b00dad44 | ||
|
|
ccf3cb6d7c | ||
|
|
fcd8be5b68 | ||
|
|
259d08ba29 | ||
|
|
9d65039b35 | ||
|
|
e50b7b65a4 | ||
|
|
1aefaaf7c7 | ||
|
|
4da89139d1 | ||
|
|
3f04c94047 | ||
|
|
00f456fd7f | ||
|
|
00d5aa6b22 | ||
|
|
91c5eff2c9 | ||
|
|
0d704043ec | ||
|
|
b11abb028b | ||
|
|
474e904409 | ||
|
|
cc05cb1c67 | ||
|
|
e10ce420ab | ||
|
|
5b5adc1a00 | ||
|
|
63fa412882 | ||
|
|
47ad8f212e | ||
|
|
7ac27fdd85 | ||
|
|
d4baeb2ada | ||
|
|
eaecfe5882 | ||
|
|
725d4a44f0 | ||
|
|
c424a745d8 | ||
|
|
3cbe419cee | ||
|
|
8f03a36862 | ||
|
|
7ecdd6c1c5 | ||
|
|
d96403fe93 | ||
|
|
2df9508e44 | ||
|
|
defe140d98 | ||
|
|
28410b8533 | ||
|
|
378914b327 | ||
|
|
023bf82638 | ||
|
|
45b1285402 | ||
|
|
629038632c | ||
|
|
3b53ec1cb6 | ||
|
|
2f26490536 | ||
|
|
69337ed264 | ||
|
|
2fbadc46d2 | ||
|
|
9cedaa60bc | ||
|
|
e4a054d34e | ||
|
|
596dee69d6 | ||
|
|
0d5a33ef2a | ||
|
|
d132b6ed71 | ||
|
|
302961a86a | ||
|
|
f136f78802 | ||
|
|
a98a6ac0c8 | ||
|
|
5ec94f96fd | ||
|
|
92d5f6c41d | ||
|
|
24caaea54d | ||
|
|
130be2aedc | ||
|
|
736dd77a6e | ||
|
|
8a7610206e | ||
|
|
78c93e1225 | ||
|
|
e8dac08a35 | ||
|
|
6d1775fcd6 | ||
|
|
cb67a911fa |
@@ -23,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
|
||||
|
||||
@@ -14,8 +14,22 @@ 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.1
|
||||
-------
|
||||
|
||||
Fixes
|
||||
~~~~~
|
||||
|
||||
- Fixed empty space being shown after tabs in the tabbar in some cases.
|
||||
- Fixed `:restart` in private browsing mode.
|
||||
- Fixed printing on macOS.
|
||||
- Closing a pinned tab via mouse now also prompts for confirmation.
|
||||
- The "try again" button on error pages works correctly again.
|
||||
- :spawn -u -d is now disallowed.
|
||||
- :spawn -d shows error messages correctly now.
|
||||
|
||||
v0.11.0
|
||||
-------
|
||||
|
||||
New dependencies
|
||||
~~~~~~~~~~~~~~~~
|
||||
@@ -28,7 +42,10 @@ New dependencies
|
||||
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 `<Ctrl-p>` by default).
|
||||
- (QtWebEngine) Implemented `:follow-selected`.
|
||||
@@ -45,6 +62,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
|
||||
~~~~~~~
|
||||
@@ -52,62 +71,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
|
||||
@@ -117,7 +125,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.
|
||||
@@ -125,18 +132,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
|
||||
-------
|
||||
@@ -181,7 +197,7 @@ Added
|
||||
- Open tabs are now auto-saved on each successful load and restored in case of a crash
|
||||
- `:jseval` now has a `--file` flag so you can pass a javascript file
|
||||
- `:session-save` now has a `--only-active-window` flag to only save the active window
|
||||
- OS X builds are back, and built with QtWebEngine
|
||||
- macOS builds are back, and built with QtWebEngine
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
@@ -483,7 +499,7 @@ Fixed
|
||||
- Fix crash when pressing enter without a command
|
||||
- Adjust error message to point out QtWebEngine is unsupported with the OS
|
||||
X .app currently.
|
||||
- Hide Harfbuzz warning with the OS X .app
|
||||
- Hide Harfbuzz warning with the macOS .app
|
||||
|
||||
v0.8.0
|
||||
------
|
||||
@@ -846,7 +862,7 @@ Fixed
|
||||
- Fixed scrolling to the very left/right with `:scroll-perc`.
|
||||
- Using an external editor should now work correctly with some funny chars
|
||||
(U+2028/U+2029/BOM).
|
||||
- Movements in caret mode now should work correctly on OS X and Windows.
|
||||
- Movements in caret mode now should work correctly on macOS and Windows.
|
||||
- Fixed upgrade from earlier config versions.
|
||||
- Fixed crash when killing a running userscript.
|
||||
- Fixed characters being passed through when shifted with
|
||||
@@ -921,7 +937,7 @@ Changed
|
||||
- The completion widget doesn't show a border anymore.
|
||||
- The tabbar doesn't display ugly arrows anymore if there isn't enough space
|
||||
for all tabs.
|
||||
- Some insignificant Qt warnings which were printed on OS X are now hidden.
|
||||
- Some insignificant Qt warnings which were printed on macOS are now hidden.
|
||||
- Better support for Qt 5.5 and Python 3.5.
|
||||
|
||||
Fixed
|
||||
@@ -1032,7 +1048,7 @@ Fixed
|
||||
- Fixed AssertionError when closing many windows quickly.
|
||||
- Various fixes for deprecated key bindings and auto-migrations.
|
||||
- Workaround for qutebrowser not starting when there are NUL-bytes in the history (because of a currently unknown bug).
|
||||
- Fixed handling of keybindings containing Ctrl/Meta on OS X.
|
||||
- Fixed handling of keybindings containing Ctrl/Meta on macOS.
|
||||
- Fixed crash when downloading a URL without filename (e.g. magnet links) via "Save as...".
|
||||
- Fixed exception when starting qutebrowser with `:set` as argument.
|
||||
- Fixed horrible completion performance when the `shrink` option was set.
|
||||
@@ -1130,7 +1146,7 @@ Changed
|
||||
- Add a `:search` command in addition to `/foo` so it's more visible and can be used from scripts.
|
||||
- Various improvements to documentation, logging, and the crash reporter.
|
||||
- Expand `~` to the users home directory with `:run-userscript`.
|
||||
- Improve the userscript runner on Linux/OS X by using `QSocketNotifier`.
|
||||
- Improve the userscript runner on Linux/macOS by using `QSocketNotifier`.
|
||||
- Add luakit-like `gt`/`gT` keybindings to cycle through tabs.
|
||||
- Show default value for config values in the completion.
|
||||
- Clone tab icon, tab text and zoom level when cloning tabs.
|
||||
@@ -1150,7 +1166,7 @@ Changed
|
||||
* `init_venv.py` and `run_checks.py` have been replaced by http://tox.readthedocs.org/[tox]. Install tox and run `tox -e mkvenv` instead.
|
||||
* The tests now use http://pytest.org/[pytest]
|
||||
* Many new tests added
|
||||
* Mac Mini buildbot to run the tests on OS X.
|
||||
* Mac Mini buildbot to run the tests on macOS.
|
||||
* Coverage recording via http://nedbatchelder.com/code/coverage/[coverage.py].
|
||||
* New `--pdb-postmortem argument` to drop into the pdb debugger on exceptions.
|
||||
* Use https://github.com/ionelmc/python-hunter[hunter] for line tracing instead of a selfmade solution.
|
||||
@@ -1286,7 +1302,7 @@ Fixed
|
||||
|
||||
* Fix rare exception when a key is pressed shortly after opening a window
|
||||
* Fix exception with certain invalid URLs like `http:foo:0`
|
||||
* Work around Qt bug which renders checkboxes on OS X unusable
|
||||
* Work around Qt bug which renders checkboxes on macOS unusable
|
||||
* Fix exception when a local files can't be read in `:adblock-update`
|
||||
* Hide 2 more Qt warnings.
|
||||
* Add `!important` to hint CSS so websites don't override the hint look
|
||||
@@ -1322,7 +1338,7 @@ Changes
|
||||
* Set zoom to default instead of 100% with `:zoom`/`=`.
|
||||
* Adjust page zoom if default zoom changed.
|
||||
* Force tabs to be focused on `:undo`.
|
||||
* Replace manual installation instructions on OS X with homebrew/macports.
|
||||
* Replace manual installation instructions on macOS with homebrew/macports.
|
||||
* Allow min-/maximizing of print preview on Windows.
|
||||
* Various documentation improvements.
|
||||
* Various other small improvements and cleanups.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -371,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
|
||||
~~~~~~~~
|
||||
|
||||
|
||||
@@ -99,16 +99,15 @@ 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:
|
||||
|
||||
@@ -281,6 +280,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
|
||||
|
||||
@@ -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-prev,tab-prev>>|Switch to the previous tab, or switch [count] tabs back.
|
||||
|<<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.
|
||||
|<<window-only,window-only>>|Close all windows except for the current one.
|
||||
|<<wq,wq>>|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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
-e git+https://github.com/xoviat/pyinstaller.git@qtweb#egg=PyInstaller
|
||||
-e git+https://github.com/pyinstaller/pyinstaller.git@develop#egg=PyInstaller
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
-e git+https://github.com/xoviat/pyinstaller.git@qtweb#egg=PyInstaller
|
||||
-e git+https://github.com/pyinstaller/pyinstaller.git@develop#egg=PyInstaller
|
||||
|
||||
# remove @commit-id for scm installs
|
||||
#@ replace: @.*# @qtweb#
|
||||
#@ replace: @.*# @develop#
|
||||
|
||||
@@ -8,7 +8,7 @@ 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.18.1
|
||||
six==1.10.0
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
beautifulsoup4==4.6.0
|
||||
cheroot==5.5.2
|
||||
cheroot==5.7.0
|
||||
click==6.7
|
||||
# colorama==0.3.9
|
||||
coverage==4.4.1
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Executes python-readability on current page and opens the summary as new tab.
|
||||
#
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Adds DuckDuckGo bang as searchengine.
|
||||
#
|
||||
@@ -8,14 +8,21 @@
|
||||
# Example:
|
||||
# :spawn --userscript ripbang amazon maps
|
||||
#
|
||||
import os, re, requests, sys, urllib
|
||||
|
||||
from __future__ import print_function
|
||||
import os, re, requests, sys
|
||||
|
||||
try:
|
||||
from urllib.parse import unquote
|
||||
except ImportError:
|
||||
from urllib import unquote
|
||||
|
||||
for argument in sys.argv[1:]:
|
||||
bang = '!' + argument
|
||||
r = requests.get('https://duckduckgo.com/',
|
||||
params={'q': bang + ' SEARCHTEXT'})
|
||||
|
||||
searchengine = urllib.unquote(re.search("url=[^']+", r.text).group(0))
|
||||
searchengine = unquote(re.search("url=[^']+", r.text).group(0))
|
||||
searchengine = searchengine.replace('url=', '')
|
||||
searchengine = searchengine.replace('/l/?kh=-1&uddg=', '')
|
||||
searchengine = searchengine.replace('SEARCHTEXT', '{}')
|
||||
@@ -24,4 +31,4 @@ for argument in sys.argv[1:]:
|
||||
with open(os.environ['QUTE_FIFO'], 'w') as fifo:
|
||||
fifo.write('set searchengines %s %s' % (bang, searchengine))
|
||||
else:
|
||||
print '%s %s' % (bang, searchengine)
|
||||
print('%s %s' % (bang, searchengine))
|
||||
|
||||
@@ -45,6 +45,8 @@ 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'
|
||||
^Failed to create Geoclue client interface. Geoclue error: org\.freedesktop\.DBus\.Error\.Disconnected
|
||||
^QObject::connect: Cannot connect \(null\)::stateChanged\(QNetworkSession::State\) to QNetworkReplyHttpImpl::_q_networkSessionStateChanged\(QNetworkSession::State\)
|
||||
^QXcbClipboard: Cannot transfer data, no data available
|
||||
^load glyph failed
|
||||
|
||||
@@ -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, 1)
|
||||
__version__ = '.'.join(str(e) for e in __version_info__)
|
||||
__description__ = "A keyboard-driven, vim-like browser based on PyQt5."
|
||||
|
||||
|
||||
@@ -410,10 +410,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()
|
||||
@@ -636,7 +634,7 @@ class Quitter:
|
||||
# Save the session if one is given.
|
||||
if session is not None:
|
||||
session_manager = objreg.get('session-manager')
|
||||
session_manager.save(session)
|
||||
session_manager.save(session, with_private=True)
|
||||
# Open a new process and immediately shutdown the existing one
|
||||
try:
|
||||
args, cwd = self._get_restart_args(pages, session)
|
||||
|
||||
@@ -20,11 +20,12 @@
|
||||
"""Command dispatcher for TabbedBrowser."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import os.path
|
||||
import shlex
|
||||
import functools
|
||||
|
||||
from PyQt5.QtWidgets import QApplication, QTabBar
|
||||
from PyQt5.QtWidgets import QApplication, QTabBar, QDialog
|
||||
from PyQt5.QtCore import Qt, QUrl, QEvent, QUrlQuery
|
||||
from PyQt5.QtGui import QKeyEvent
|
||||
from PyQt5.QtPrintSupport import QPrintDialog, QPrintPreviewDialog
|
||||
@@ -227,19 +228,6 @@ class CommandDispatcher:
|
||||
self._tabbed_browser.close_tab(tab)
|
||||
tabbar.setSelectionBehaviorOnRemove(old_selection_behavior)
|
||||
|
||||
def _tab_close_prompt_if_pinned(self, tab, force, yes_action):
|
||||
"""Helper method for tab_close.
|
||||
|
||||
If tab is pinned, prompt. If everything is good, run yes_action.
|
||||
"""
|
||||
if tab.data.pinned and not force:
|
||||
message.confirm_async(
|
||||
title='Pinned Tab',
|
||||
text="Are you sure you want to close a pinned tab?",
|
||||
yes_action=yes_action, default=False)
|
||||
else:
|
||||
yes_action()
|
||||
|
||||
@cmdutils.register(instance='command-dispatcher', scope='window')
|
||||
@cmdutils.argument('count', count=True)
|
||||
def tab_close(self, prev=False, next_=False, opposite=False,
|
||||
@@ -260,7 +248,7 @@ class CommandDispatcher:
|
||||
close = functools.partial(self._tab_close, tab, prev,
|
||||
next_, opposite)
|
||||
|
||||
self._tab_close_prompt_if_pinned(tab, force, close)
|
||||
self._tabbed_browser.tab_close_prompt_if_pinned(tab, force, close)
|
||||
|
||||
@cmdutils.register(instance='command-dispatcher', scope='window',
|
||||
name='tab-pin')
|
||||
@@ -280,9 +268,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')
|
||||
@@ -438,9 +424,18 @@ class CommandDispatcher:
|
||||
message.error("Printing failed!")
|
||||
diag.deleteLater()
|
||||
|
||||
def do_print():
|
||||
"""Called when the dialog was closed."""
|
||||
tab.printing.to_printer(diag.printer(), print_callback)
|
||||
|
||||
diag = QPrintDialog(tab)
|
||||
diag.open(lambda: tab.printing.to_printer(diag.printer(),
|
||||
print_callback))
|
||||
if sys.platform == 'darwin':
|
||||
# For some reason we get a segfault when using open() on macOS
|
||||
ret = diag.exec_()
|
||||
if ret == QDialog.Accepted:
|
||||
do_print()
|
||||
else:
|
||||
diag.open(do_print)
|
||||
|
||||
@cmdutils.register(instance='command-dispatcher', name='print',
|
||||
scope='window')
|
||||
@@ -515,7 +510,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')
|
||||
@@ -868,7 +863,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
|
||||
@@ -879,6 +874,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')
|
||||
@@ -913,8 +915,9 @@ class CommandDispatcher:
|
||||
if not force:
|
||||
for i, tab in enumerate(self._tabbed_browser.widgets()):
|
||||
if _to_close(i) and tab.data.pinned:
|
||||
self._tab_close_prompt_if_pinned(
|
||||
tab, force,
|
||||
self._tabbed_browser.tab_close_prompt_if_pinned(
|
||||
tab,
|
||||
force,
|
||||
lambda: self.tab_only(
|
||||
prev=prev, next_=next_, force=True))
|
||||
return
|
||||
@@ -925,7 +928,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:
|
||||
@@ -1160,6 +1163,7 @@ class CommandDispatcher:
|
||||
detach: Whether the command should be detached from qutebrowser.
|
||||
cmdline: The commandline to execute.
|
||||
"""
|
||||
cmdutils.check_exclusive((userscript, detach), 'ud')
|
||||
try:
|
||||
cmd, *args = shlex.split(cmdline)
|
||||
except ValueError as e:
|
||||
|
||||
@@ -28,7 +28,9 @@ Module attributes:
|
||||
"""
|
||||
|
||||
import os
|
||||
import logging
|
||||
import sys
|
||||
import ctypes
|
||||
import ctypes.util
|
||||
|
||||
from PyQt5.QtGui import QFont
|
||||
from PyQt5.QtWebEngineWidgets import (QWebEngineSettings, QWebEngineProfile,
|
||||
@@ -132,9 +134,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():
|
||||
@@ -203,12 +202,10 @@ def init(args):
|
||||
if args.enable_webengine_inspector:
|
||||
os.environ['QTWEBENGINE_REMOTE_DEBUGGING'] = str(utils.random_port())
|
||||
|
||||
# Workaround for a black screen with some setups
|
||||
# https://github.com/spyder-ide/spyder/issues/3226
|
||||
if not os.environ.get('QUTE_NO_OPENGL_WORKAROUND'):
|
||||
# Hide "No OpenGL_accelerate module loaded: ..." message
|
||||
logging.getLogger('OpenGL.acceleratesupport').propagate = False
|
||||
from OpenGL import GL # pylint: disable=unused-variable
|
||||
# WORKAROUND for
|
||||
# https://bugs.launchpad.net/ubuntu/+source/python-qt4/+bug/941826
|
||||
if sys.platform == 'linux':
|
||||
ctypes.CDLL(ctypes.util.find_library("GL"), mode=ctypes.RTLD_GLOBAL)
|
||||
|
||||
_init_profiles()
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"""Wrapper over a QWebEngineView."""
|
||||
|
||||
import os
|
||||
import math
|
||||
import functools
|
||||
|
||||
import sip
|
||||
@@ -50,12 +51,13 @@ def init():
|
||||
global _qute_scheme_handler
|
||||
app = QApplication.instance()
|
||||
|
||||
software_rendering = os.environ.get('LIBGL_ALWAYS_SOFTWARE') == '1'
|
||||
software_rendering = (os.environ.get('LIBGL_ALWAYS_SOFTWARE') == '1' or
|
||||
'QT_XCB_FORCE_SOFTWARE_OPENGL' in os.environ)
|
||||
if version.opengl_vendor() == 'nouveau' and not software_rendering:
|
||||
# FIXME:qtwebengine display something more sophisticated here
|
||||
raise browsertab.WebTabError(
|
||||
"QtWebEngine is not supported with Nouveau graphics (unless "
|
||||
"LIBGL_ALWAYS_SOFTWARE is set as environment variable).")
|
||||
"QT_XCB_FORCE_SOFTWARE_OPENGL is set as environment variable).")
|
||||
|
||||
log.init.debug("Initializing qute://* handler...")
|
||||
_qute_scheme_handler = webenginequtescheme.QuteSchemeHandler(parent=app)
|
||||
@@ -342,7 +344,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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -70,6 +70,8 @@ the <span class="mono">qute://settings</span> page or caret browsing).</span>
|
||||
{{ install_webengine('qt5-qtwebengine') }}
|
||||
{% elif distribution.parsed == Distribution.opensuse %}
|
||||
{{ install_webengine('libqt5-qtwebengine') }}
|
||||
{% elif distribution.parsed == Distribution.gentoo %}
|
||||
{{ install_webengine('dev-qt/qtwebengine') }}
|
||||
{% else %}
|
||||
{{ unknown_system() }}
|
||||
{% endif %}
|
||||
|
||||
@@ -61,7 +61,7 @@ li {
|
||||
{{ super() }}
|
||||
function tryagain()
|
||||
{
|
||||
location.href = url;
|
||||
location.href = "{{ url }}";
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -43,3 +43,4 @@ rules:
|
||||
array-bracket-newline: "off"
|
||||
array-element-newline: "off"
|
||||
no-multi-spaces: ["error", {"ignoreEOLComments": true}]
|
||||
function-paren-newline: "off"
|
||||
|
||||
@@ -232,6 +232,19 @@ class TabbedBrowser(tabwidget.TabWidget):
|
||||
for tab in self.widgets():
|
||||
self._remove_tab(tab)
|
||||
|
||||
def tab_close_prompt_if_pinned(self, tab, force, yes_action):
|
||||
"""Helper method for tab_close.
|
||||
|
||||
If tab is pinned, prompt. If everything is good, run yes_action.
|
||||
"""
|
||||
if tab.data.pinned and not force:
|
||||
message.confirm_async(
|
||||
title='Pinned Tab',
|
||||
text="Are you sure you want to close a pinned tab?",
|
||||
yes_action=yes_action, default=False)
|
||||
else:
|
||||
yes_action()
|
||||
|
||||
def close_tab(self, tab, *, add_undo=True):
|
||||
"""Close a tab.
|
||||
|
||||
@@ -273,6 +286,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 +355,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):
|
||||
@@ -364,7 +379,8 @@ class TabbedBrowser(tabwidget.TabWidget):
|
||||
log.webview.debug("Got invalid tab {} for index {}!".format(
|
||||
tab, idx))
|
||||
return
|
||||
self.close_tab(tab)
|
||||
self.tab_close_prompt_if_pinned(
|
||||
tab, False, lambda: self.close_tab(tab))
|
||||
|
||||
@pyqtSlot(browsertab.AbstractTab)
|
||||
def on_window_close_requested(self, widget):
|
||||
|
||||
@@ -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
|
||||
@@ -502,10 +503,6 @@ class TabBar(QTabBar):
|
||||
# We return it directly rather than setting `size' because we don't
|
||||
# want to ensure it's valid in this special case.
|
||||
return QSize()
|
||||
elif self.count() * minimum_size.width() > self.width():
|
||||
# If we don't have enough space, we return the minimum size so we
|
||||
# get scroll buttons as soon as needed.
|
||||
size = minimum_size
|
||||
else:
|
||||
tab_width_pinned_conf = config.get('tabs', 'pinned-width')
|
||||
|
||||
@@ -516,19 +513,21 @@ class TabBar(QTabBar):
|
||||
|
||||
no_pinned_count = self.count() - self.pinned_count
|
||||
pinned_width = tab_width_pinned_conf * self.pinned_count
|
||||
no_pinned_width = self.width() - pinned_width
|
||||
# Prevent any tabs from being smaller than the min size
|
||||
no_pinned_width = max(self.width() - pinned_width,
|
||||
minimum_size.width() * no_pinned_count)
|
||||
|
||||
if pinned:
|
||||
width = tab_width_pinned_conf
|
||||
else:
|
||||
|
||||
# If we *do* have enough space, tabs should occupy the whole
|
||||
# window width. If there are pinned tabs their size will be
|
||||
# subtracted from the total window width.
|
||||
# During shutdown the self.count goes down,
|
||||
# but the self.pinned_count not - this generates some odd
|
||||
# Tabs should attempt to occupy the whole window width. If
|
||||
# there are pinned tabs their size will be subtracted from the
|
||||
# total window width. During shutdown the self.count goes
|
||||
# down, but the self.pinned_count not - this generates some odd
|
||||
# behavior. To avoid this we compare self.count against
|
||||
# self.pinned_count.
|
||||
# self.pinned_count. If we end up having too little space, we
|
||||
# set the minimum size below.
|
||||
if self.pinned_count > 0 and no_pinned_count > 0:
|
||||
width = no_pinned_width / no_pinned_count
|
||||
else:
|
||||
@@ -540,6 +539,10 @@ class TabBar(QTabBar):
|
||||
index < no_pinned_width % no_pinned_count):
|
||||
width += 1
|
||||
|
||||
# If we don't have enough space, we return the minimum size so we
|
||||
# get scroll buttons as soon as needed.
|
||||
width = max(width, minimum_size.width())
|
||||
|
||||
size = QSize(width, height)
|
||||
qtutils.ensure_valid(size)
|
||||
return size
|
||||
|
||||
@@ -336,12 +336,12 @@ def check_libraries(backend):
|
||||
"http://pyyaml.org/download/pyyaml/ (py3.4) "
|
||||
"or Install via pip.",
|
||||
pip="PyYAML"),
|
||||
'PyQt5.QtQml': _missing_str("PyQt5.QtQml"),
|
||||
}
|
||||
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")
|
||||
|
||||
@@ -154,8 +154,8 @@ class GUIProcess(QObject):
|
||||
log.procs.debug("Process started.")
|
||||
self._started = True
|
||||
else:
|
||||
message.error("Error while spawning {}: {}.".format(
|
||||
self._what, self._proc.error()))
|
||||
message.error("Error while spawning {}: {}".format(
|
||||
self._what, ERROR_STRINGS[self._proc.error()]))
|
||||
|
||||
def exit_status(self):
|
||||
return self._proc.exitStatus()
|
||||
|
||||
@@ -406,7 +406,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):
|
||||
@@ -420,7 +420,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):
|
||||
@@ -489,7 +492,7 @@ class SessionManager(QObject):
|
||||
try:
|
||||
if only_active_window:
|
||||
name = self.save(name, only_window=win_id,
|
||||
with_private=with_private)
|
||||
with_private=True)
|
||||
else:
|
||||
name = self.save(name, with_private=with_private)
|
||||
except SessionError as e:
|
||||
@@ -516,7 +519,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))
|
||||
|
||||
@@ -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 "
|
||||
|
||||
@@ -81,6 +81,8 @@ def distribution():
|
||||
return None
|
||||
|
||||
pretty = info.get('PRETTY_NAME', 'Unknown')
|
||||
if pretty == 'Linux': # Thanks, Funtoo
|
||||
pretty = info.get('NAME', pretty)
|
||||
|
||||
if 'VERSION_ID' in info:
|
||||
dist_version = pkg_resources.parse_version(info['VERSION_ID'])
|
||||
@@ -88,8 +90,11 @@ def distribution():
|
||||
dist_version = None
|
||||
|
||||
dist_id = info.get('ID', None)
|
||||
id_mappings = {
|
||||
'funtoo': 'gentoo', # does not have ID_LIKE=gentoo
|
||||
}
|
||||
try:
|
||||
parsed = Distribution[dist_id]
|
||||
parsed = Distribution[id_mappings.get(dist_id, dist_id)]
|
||||
except KeyError:
|
||||
parsed = Distribution.unknown
|
||||
|
||||
@@ -186,7 +191,6 @@ def _module_versions():
|
||||
('yaml', ['__version__']),
|
||||
('cssutils', ['__version__']),
|
||||
('typing', []),
|
||||
('OpenGL', ['__version__']),
|
||||
('PyQt5.QtWebEngineWidgets', []),
|
||||
('PyQt5.QtWebKitWidgets', []),
|
||||
])
|
||||
|
||||
@@ -7,4 +7,3 @@ MarkupSafe==1.0
|
||||
Pygments==2.2.0
|
||||
pyPEG2==2.15.2
|
||||
PyYAML==3.12
|
||||
PyOpenGL==3.1.0
|
||||
|
||||
@@ -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),
|
||||
@@ -280,6 +292,14 @@ def build_sdist():
|
||||
return artifacts
|
||||
|
||||
|
||||
def read_github_token():
|
||||
"""Read the GitHub API token from disk."""
|
||||
token_file = os.path.join(os.path.expanduser('~'), '.gh_token')
|
||||
with open(token_file, encoding='ascii') as f:
|
||||
token = f.read().strip()
|
||||
return token
|
||||
|
||||
|
||||
def github_upload(artifacts, tag):
|
||||
"""Upload the given artifacts to GitHub.
|
||||
|
||||
@@ -290,9 +310,7 @@ def github_upload(artifacts, tag):
|
||||
import github3
|
||||
utils.print_title("Uploading to github...")
|
||||
|
||||
token_file = os.path.join(os.path.expanduser('~'), '.gh_token')
|
||||
with open(token_file, encoding='ascii') as f:
|
||||
token = f.read().strip()
|
||||
token = read_github_token()
|
||||
gh = github3.login(token=token)
|
||||
repo = gh.repository('qutebrowser', 'qutebrowser')
|
||||
|
||||
@@ -329,6 +347,12 @@ def main():
|
||||
|
||||
upload_to_pypi = False
|
||||
|
||||
if args.upload is not None:
|
||||
# Fail early when trying to upload without github3 installed
|
||||
# or without API token
|
||||
import github3 # pylint: disable=unused-variable
|
||||
read_github_token()
|
||||
|
||||
if os.name == 'nt':
|
||||
if sys.maxsize > 2**32:
|
||||
# WORKAROUND
|
||||
|
||||
@@ -43,6 +43,12 @@ travis_retry() {
|
||||
}
|
||||
|
||||
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 install --no-install-recommends "$@"
|
||||
}
|
||||
@@ -64,8 +70,9 @@ npm_install() {
|
||||
}
|
||||
|
||||
install_node() {
|
||||
curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash -
|
||||
apt_install nodejs
|
||||
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
|
||||
travis_retry sudo apt-get -y -q update
|
||||
travis_retry sudo apt-get -y -q install --no-install-recommends nodejs
|
||||
}
|
||||
|
||||
check_pyqt() {
|
||||
|
||||
@@ -365,8 +365,7 @@ def generate_commands(filename):
|
||||
|
||||
def _generate_setting_section(f, sectname, sect):
|
||||
"""Generate documentation for a single section."""
|
||||
version_dependent_options = [('network', 'proxy'),
|
||||
('general', 'print-element-backgrounds')]
|
||||
version_dependent_options = [('general', 'print-element-backgrounds')]
|
||||
for optname, option in sect.items():
|
||||
f.write("\n")
|
||||
f.write('[[{}-{}]]'.format(sectname, optname) + "\n")
|
||||
|
||||
@@ -579,9 +579,9 @@ Feature: Downloading things from a website.
|
||||
And I wait until the download is finished
|
||||
Then the downloaded file content-size should exist
|
||||
|
||||
@posix
|
||||
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
|
||||
Then the error "Download error: Permission denied" should be shown
|
||||
|
||||
|
||||
@@ -153,3 +153,26 @@ Feature: Using private browsing
|
||||
- history:
|
||||
- url: http://localhost:*/data/numbers/1.txt
|
||||
- url: http://localhost:*/data/numbers/2.txt
|
||||
|
||||
|
||||
Scenario: Saving a private session with only-active-window
|
||||
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 private window
|
||||
And I open data/numbers/4.txt in a new tab
|
||||
And I open data/numbers/5.txt in a new tab
|
||||
And I run :session-save --only-active-window window_session_name
|
||||
And I run :window-only
|
||||
And I run :tab-only
|
||||
And I run :session-load -c window_session_name
|
||||
And I wait until data/numbers/5.txt is loaded
|
||||
Then the session should look like:
|
||||
windows:
|
||||
- tabs:
|
||||
- history:
|
||||
- url: http://localhost:*/data/numbers/3.txt
|
||||
- history:
|
||||
- url: http://localhost:*/data/numbers/4.txt
|
||||
- history:
|
||||
- active: true
|
||||
url: http://localhost:*/data/numbers/5.txt
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -60,8 +60,3 @@ Feature: :spawn
|
||||
Scenario: Running :spawn with userscript that expects the stdin getting closed
|
||||
When I run :spawn -u (testdata)/userscripts/stdinclose.py
|
||||
Then the message "stdin closed" should be shown
|
||||
|
||||
@posix
|
||||
Scenario: Running :spawn -d with userscript that expects the stdin getting closed
|
||||
When I run :spawn -d -u (testdata)/userscripts/stdinclose.py
|
||||
Then the message "stdin closed" should be shown
|
||||
|
||||
@@ -1073,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
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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
|
||||
|
||||
251
tests/unit/config/old_configs/qutebrowser-v0.11.0.conf
Normal file
251
tests/unit/config/old_configs/qutebrowser-v0.11.0.conf
Normal 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
|
||||
@@ -27,7 +27,6 @@ import shutil
|
||||
from PyQt5.QtGui import QColor
|
||||
import pytest
|
||||
|
||||
import qutebrowser
|
||||
from qutebrowser.config import config, configexc, configdata
|
||||
from qutebrowser.config.parsers import keyconf
|
||||
from qutebrowser.commands import runners
|
||||
@@ -397,19 +396,6 @@ class TestDefaultConfig:
|
||||
for cmd in conf.get_bindings_for(sectname).values():
|
||||
runner.parse(cmd)
|
||||
|
||||
def test_upgrade_version(self):
|
||||
"""Fail when the qutebrowser version changed.
|
||||
|
||||
The aim of this is to remind us to add a new file to old_configs.
|
||||
|
||||
If the config file of the current release didn't change compared to the
|
||||
last one in old_configs, just increment the version here.
|
||||
|
||||
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'
|
||||
|
||||
@pytest.mark.parametrize('filename',
|
||||
os.listdir(os.path.join(os.path.dirname(__file__), 'old_configs')),
|
||||
ids=os.path.basename)
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -128,11 +128,13 @@ def test_start_detached_error(fake_proc, message_mock, caplog):
|
||||
"""Test starting a detached process with ok=False."""
|
||||
argv = ['foo', 'bar']
|
||||
fake_proc._proc.startDetached.return_value = (False, 0)
|
||||
fake_proc._proc.error.return_value = "Error message"
|
||||
fake_proc._proc.error.return_value = QProcess.FailedToStart
|
||||
with caplog.at_level(logging.ERROR):
|
||||
fake_proc.start_detached(*argv)
|
||||
msg = message_mock.getmsg(usertypes.MessageLevel.error)
|
||||
assert msg.text == "Error while spawning testprocess: Error message."
|
||||
expected = ("Error while spawning testprocess: The process failed to "
|
||||
"start.")
|
||||
assert msg.text == expected
|
||||
|
||||
|
||||
def test_double_start(qtbot, proc, py_proc):
|
||||
|
||||
@@ -163,6 +163,15 @@ from qutebrowser.browser import pdfjs
|
||||
version.DistributionInfo(
|
||||
id='manjaro', parsed=version.Distribution.manjaro,
|
||||
version=None, pretty='Manjaro Linux')),
|
||||
# Funtoo
|
||||
("""
|
||||
ID="funtoo"
|
||||
NAME="Funtoo GNU/Linux"
|
||||
PRETTY_NAME="Linux"
|
||||
""",
|
||||
version.DistributionInfo(
|
||||
id='funtoo', parsed=version.Distribution.gentoo,
|
||||
version=None, pretty='Funtoo GNU/Linux')),
|
||||
])
|
||||
def test_distribution(tmpdir, monkeypatch, os_release, expected):
|
||||
os_release_file = tmpdir / 'os-release'
|
||||
@@ -496,7 +505,6 @@ class ImportFake:
|
||||
'typing': True,
|
||||
'PyQt5.QtWebEngineWidgets': True,
|
||||
'PyQt5.QtWebKitWidgets': True,
|
||||
'OpenGL': True,
|
||||
}
|
||||
self.version_attribute = '__version__'
|
||||
self.version = '1.2.3'
|
||||
@@ -556,7 +564,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', 'OpenGL: 1.2.3',
|
||||
'cssutils: 1.2.3', 'typing: yes',
|
||||
'PyQt5.QtWebEngineWidgets: yes',
|
||||
'PyQt5.QtWebKitWidgets: yes']
|
||||
assert version._module_versions() == expected
|
||||
@@ -580,17 +588,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', 'OpenGL: yes',
|
||||
'cssutils: yes', 'typing: 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', 'OpenGL: yes',
|
||||
'cssutils: yes', 'typing: yes',
|
||||
'PyQt5.QtWebEngineWidgets: yes',
|
||||
'PyQt5.QtWebKitWidgets: yes']),
|
||||
(None, ['sip: yes', 'colorama: yes', 'pypeg2: yes', 'jinja2: yes',
|
||||
'pygments: yes', 'yaml: yes', 'cssutils: yes', 'typing: yes',
|
||||
'OpenGL: yes', 'PyQt5.QtWebEngineWidgets: yes',
|
||||
'PyQt5.QtWebEngineWidgets: yes',
|
||||
'PyQt5.QtWebKitWidgets: yes']),
|
||||
])
|
||||
def test_version_attribute(self, value, expected, import_fake):
|
||||
|
||||
24
tox.ini
24
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]
|
||||
|
||||
Reference in New Issue
Block a user